Name | Type | is_array | initial_value |
positions | location | Yes |
/*****************************************************************************
*
* StackNSplit v1.1.2.2
* by Bannar
*
* Easy item charges stacking and splitting.
*
* Thanks to Dangerb0y for original system.
* Container idea provided by Spinnaker.
*
******************************************************************************
*
* Requirements:
*
* ListT by Bannar
* hiveworkshop.com/threads/containers-list-t.249011/
*
* InventoryEvent by Bannar
* hiveworkshop.com/threads/snippet-inventoryevent.287084/
*
* RegisterPlayerUnitEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
*
* Optional requirement:
*
* SmoothItemPickup by Bannar
* hiveworkshop.com/threads/smoothitempickup.306016/
*
******************************************************************************
*
* Event API:
*
* integer EVENT_ITEM_CHARGES_ADDED
* integer EVENT_ITEM_CHARGES_REMOVED
*
* Use RegisterNativeEvent or RegisterIndexNativeEvent for event registration.
* GetNativeEventTrigger and GetIndexNativeEventTrigger provide access to trigger handles.
*
*
* function GetItemStackingUnit takes nothing returns unit
* Returns unit which manupilated event item.
*
* function GetItemStackingItem takes nothing returns item
* Returns manipulated event item.
*
* function GetItemStackingCharges takes nothing returns integer
* Returns number of charges that has been added or removed.
*
******************************************************************************
*
* Containers - idea behind:
*
* Each item type is allowed to have another item type assigned as its container,
* and becoming an element at the same time.
*
* Example:
*
* | element - Gold Coin
* | container - Endless Sack of Gold
*
* Any item type can become container for another as long as it is not a stackable item type.
* Containers cannot have thier own containers assigned.
* Containers may declare different maximum stack and split count values.
* Container is always prioritized over element type when item charges are being redistributed.
* Each element can have multiple item types assigned as its containers.
*
******************************************************************************
*
* Functions:
*
* function IsUnitItemFullyStacked takes unit whichUnit, integer itemTypeId returns boolean
* Checks if specified unit has hold any additional charges of provided item.
*
* function UnitStackItem takes unit whichUnit, item whichItem returns boolean
* Attempts to stack provided item for specified unit.
*
* function UnitSplitItem takes unit whichUnit, item whichItem returns boolean
* Attempts to split provided item for specified unit.
*
*
* Functions (Container):
*
* function IsItemContainer takes integer containerType returns boolean
* Returns value indicating whether specifed item is stackable or not.
*
* function GetItemContainerMaxStacks takes integer containerType returns integer
* Returns maximum number of charges for specified container.
*
* function GetItemContainerSplitCount takes integer containerType returns integer
* Returns number of charges lost by specified container per split.
*
* function GetItemContainerItem takes integer containerType returns integer
* Returns item type assigned to specified container as its elements.
*
* function GetItemContainerMinCharges takes integer containerType returns integer
* Number of charges that container cannot go below during split operation.
*
* function UnsetItemContainer takes integer elementType returns nothing
* Unsets any container related data related to specified element item type.
*
* function SetItemContainer takes integer elementType, integer containerType, integer stacks, integer splits, integer minCharges returns boolean
* Sets specified containerType item type as container for item type elementType.
* Argument minCharges specifies number of charges that container item cannot
* go below during split operation.
*
*
* Functions (Element):
*
* function IsItemStackable takes integer elementType returns boolean
* Returns value indicating whether specifed item is stackable or not.
*
* function GetItemMaxStacks takes integer elementType returns integer
* Returns maximum number of charges for specified item.
*
* function GetItemSplitCount takes integer elementType returns integer
* Returns number of charges lost by specified item per split.
*
* function ItemHasContainer takes integer elementType returns boolean
* Indicates if specifed element type has container assigned to it.
*
* function GetItemContainers takes integer elementType returns IntegerList
* Returns list of item types assigned to specified element as its containers.
*
* function MakeItemUnstackable takes integer elementType returns nothing
* Unregisters specified item from being stackable.
*
* function MakeItemStackable takes integer elementType, integer stacks, integer splits returns boolean
* Registers specified item as stackable.
*
*****************************************************************************/
library StackNSplit requires /*
*/ ListT /*
*/ InventoryEvent /*
*/ RegisterPlayerUnitEvent /*
*/ optional SmoothItemPickup
globals
integer EVENT_ITEM_CHARGES_ADDED
integer EVENT_ITEM_CHARGES_REMOVED
endglobals
globals
private unit eventUnit = null
private item eventItem = null
private integer eventCharges = -1
private TableArray table = 0
endglobals
function GetItemStackingUnit takes nothing returns unit
return eventUnit
endfunction
function GetItemStackingItem takes nothing returns item
return eventItem
endfunction
function GetItemStackingCharges takes nothing returns integer
return eventCharges
endfunction
function IsItemContainer takes integer containerType returns boolean
return table[3].has(containerType)
endfunction
function GetItemContainerMaxStacks takes integer containerType returns integer
if IsItemContainer(containerType) then
return table[0][containerType]
endif
return -1
endfunction
function GetItemContainerSplitCount takes integer containerType returns integer
if IsItemContainer(containerType) then
return table[1][containerType]
endif
return -1
endfunction
function GetItemContainerItem takes integer containerType returns integer
if IsItemContainer(containerType) then
return table[3][containerType]
endif
return 0
endfunction
function GetItemContainerMinCharges takes integer containerType returns integer
if IsItemContainer(containerType) then
return table[4][containerType]
endif
return -1
endfunction
function IsItemStackable takes integer elementType returns boolean
return not IsItemContainer(elementType) and table[0].has(elementType)
endfunction
function GetItemMaxStacks takes integer elementType returns integer
if IsItemStackable(elementType) then
return table[0][elementType]
endif
return -1
endfunction
function GetItemSplitCount takes integer elementType returns integer
if IsItemStackable(elementType) then
return table[1][elementType]
endif
return -1
endfunction
function ItemHasContainer takes integer elementType returns boolean
return table[2].has(elementType)
endfunction
function GetItemContainers takes integer elementType returns IntegerList
if ItemHasContainer(elementType) then
return table[2][elementType]
endif
return 0
endfunction
function MakeItemUnstackable takes integer elementType returns nothing
if IsItemStackable(elementType) then
call table[0].remove(elementType)
call table[1].remove(elementType)
endif
endfunction
function MakeItemStackable takes integer elementType, integer stacks, integer splits returns boolean
if not IsItemContainer(elementType) and stacks > 0 then
set table[0][elementType] = stacks
set table[1][elementType] = IMaxBJ(splits, 1)
return true
endif
return false
endfunction
function UnsetItemContainer takes integer containerType returns nothing
local integer elementType = GetItemContainerItem(containerType)
local IntegerList containers
if elementType != 0 then
call table[0].remove(containerType)
call table[1].remove(containerType)
call table[3].remove(containerType)
call table[4].remove(containerType)
// Remove containerType from containers list
set containers = GetItemContainers(elementType)
call containers.removeElem(containerType)
if containers.empty() then
call containers.destroy()
call table[2].remove(elementType)
endif
endif
endfunction
function SetItemContainer takes integer elementType, integer containerType, integer stacks, integer splits, integer minCharges returns boolean
local IntegerList containers
if elementType == 0 or containerType == 0 then
return false
elseif stacks <= 0 or elementType == containerType then
return false
elseif IsItemContainer(elementType) or IsItemContainer(containerType) then
return false
elseif IsItemStackable(containerType) then
return false
endif
set containers = GetItemContainers(elementType)
if containers == 0 then
set containers = IntegerList.create()
set table[2][elementType] = containers
endif
call containers.push(containerType)
set table[0][containerType] = stacks
set table[1][containerType] = IMaxBJ(splits, 1)
set table[3][containerType] = elementType
set table[4][containerType] = IMaxBJ(minCharges, 0)
return true
endfunction
function IsUnitItemFullyStacked takes unit whichUnit, integer itemTypeId returns boolean
local boolean result = true
local integer max
local item itm
local integer size
local integer slot = 0
local IntegerListItem iter
if not IsUnitInventoryFull(whichUnit) then
return false
elseif IsItemContainer(itemTypeId) then
return result
endif
set size = UnitInventorySize(whichUnit)
if ItemHasContainer(itemTypeId) then
set iter = GetItemContainers(itemTypeId).first
loop
exitwhen iter == 0
set max = GetItemContainerMaxStacks(iter.data)
if max > 0 then
loop
exitwhen slot >= size
set itm = UnitItemInSlot(whichUnit, slot)
if GetItemTypeId(itm) == iter.data and GetItemCharges(itm) < max then
set result = false
exitwhen true
endif
set slot = slot + 1
endloop
endif
set iter = iter.next
endloop
endif
if result and IsItemStackable(itemTypeId) then
set max = GetItemMaxStacks(itemTypeId)
if max > 0 then
set slot = 0
loop
exitwhen slot >= size
set itm = UnitItemInSlot(whichUnit, slot)
if GetItemTypeId(itm) == itemTypeId and GetItemCharges(itm) < max then
set result = false
exitwhen true
endif
set slot = slot + 1
endloop
endif
endif
set itm = null
return result
endfunction
private function FireEvent takes integer evt, unit u, item itm, integer charges returns nothing
local unit prevUnit = eventUnit
local item prevItem = eventItem
local integer prevCharges = eventCharges
local integer playerId = GetPlayerId(GetOwningPlayer(u))
set eventUnit = u
set eventItem = itm
set eventCharges = charges
call TriggerEvaluate(GetNativeEventTrigger(evt))
if IsNativeEventRegistered(playerId, evt) then
call TriggerEvaluate(GetIndexNativeEventTrigger(playerId, evt))
endif
set eventUnit = prevUnit
set eventItem = prevItem
set eventCharges = prevCharges
set prevUnit = null
set prevItem = null
endfunction
private function StackItem takes unit u, item itm, item ignored, integer withTypeId, integer max returns integer
local integer charges = GetItemCharges(itm)
local integer slot = 0
local integer size = UnitInventorySize(u)
local item with
local integer withCharges
local integer diff
loop
exitwhen slot >= size
set with = UnitItemInSlot(u, slot)
if with != ignored and GetItemTypeId(with) == withTypeId then
set withCharges = GetItemCharges(with)
if withCharges < max then
set diff = max - withCharges
if diff >= charges then
call SetItemCharges(with, withCharges + charges)
call RemoveItem(itm)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, with, charges)
set charges = 0
exitwhen true
else
set charges = charges - diff
call SetItemCharges(with, max)
call SetItemCharges(itm, charges)
call FireEvent(EVENT_ITEM_CHARGES_REMOVED, u, itm, diff)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, with, diff)
endif
endif
endif
set slot = slot + 1
endloop
set with = null
return charges
endfunction
function UnitStackItem takes unit whichUnit, item whichItem returns boolean
local integer charges = GetItemCharges(whichItem)
local integer itemTypeId = GetItemTypeId(whichItem)
local integer containerType
local integer max
local boolean result = false
local IntegerListItem iter
if whichUnit == null or charges == 0 then
return result
endif
if not IsItemContainer(itemTypeId) then
if ItemHasContainer(itemTypeId) then
set iter = GetItemContainers(itemTypeId).first
loop
exitwhen iter == 0
set containerType = iter.data
set max = GetItemContainerMaxStacks(containerType)
set charges = StackItem(whichUnit, whichItem, whichItem, containerType, max)
exitwhen charges == 0
set iter = iter.next
endloop
set result = true
endif
if IsItemStackable(itemTypeId) and charges > 0 then
set max = GetItemMaxStacks(itemTypeId)
call StackItem(whichUnit, whichItem, whichItem, itemTypeId, max)
set result = true
endif
else
set max = GetItemContainerMaxStacks(itemTypeId)
call StackItem(whichUnit, whichItem, whichItem, GetItemContainerItem(itemTypeId), max)
set result = true
endif
return result
endfunction
function UnitSplitItem takes unit whichUnit, item whichItem returns boolean
local integer charges = GetItemCharges(whichItem)
local integer itemTypeId = GetItemTypeId(whichItem)
local integer toSplit
local integer elementType
local IntegerListItem iter
local integer containerType
local item with
local trigger t
local integer minCharges = 1
if IsItemContainer(itemTypeId) then
set minCharges = GetItemContainerMinCharges(itemTypeId)
if charges <= minCharges then
return false
endif
set elementType = GetItemContainerItem(itemTypeId)
set toSplit = GetItemContainerSplitCount(itemTypeId)
elseif IsItemStackable(itemTypeId) and charges > minCharges then
set elementType = itemTypeId
set toSplit = GetItemSplitCount(itemTypeId)
else
return false
endif
if toSplit >= charges then
set toSplit = charges - minCharges
endif
call SetItemCharges(whichItem, charges - toSplit)
call FireEvent(EVENT_ITEM_CHARGES_REMOVED, whichUnit, whichItem, toSplit)
set with = CreateItem(elementType, GetUnitX(whichUnit), GetUnitY(whichUnit))
call SetItemCharges(with, toSplit)
// Redistribute splitted stacks if possible
if ItemHasContainer(elementType) then
set iter = GetItemContainers(elementType).first
loop
exitwhen iter == 0
set containerType = iter.data
set toSplit = StackItem(whichUnit, with, whichItem, containerType, GetItemContainerMaxStacks(containerType))
exitwhen toSplit == 0
set iter = iter.next
endloop
endif
if IsItemStackable(elementType) and toSplit > 0 then
set toSplit = StackItem(whichUnit, with, whichItem, elementType, GetItemMaxStacks(elementType))
endif
if toSplit > 0 then // something is left
set t = GetAnyPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_PICKUP_ITEM)
call DisableTrigger(t)
call UnitAddItem(whichUnit, with)
call EnableTrigger(t)
set t = null
endif
set with = null
return true
endfunction
private function PickupItem takes unit u, item itm returns nothing
local integer itemTypeId = GetItemTypeId(itm)
local integer charges
local integer elementType
local integer max
local item with
local integer withCharges
local integer diff
local integer slot = 0
local integer size
if IsItemContainer(itemTypeId) then
set max = GetItemContainerMaxStacks(itemTypeId)
set elementType = GetItemContainerItem(itemTypeId)
set charges = GetItemCharges(itm)
set size = UnitInventorySize(u)
loop
exitwhen charges >= max
exitwhen slot >= size
set with = UnitItemInSlot(u, slot)
set withCharges = GetItemCharges(with)
if with != itm and withCharges > 0 and GetItemTypeId(with) == elementType then
if charges + withCharges > max then
set diff = max - charges
call SetItemCharges(itm, max)
call SetItemCharges(with, withCharges - diff)
call FireEvent(EVENT_ITEM_CHARGES_REMOVED, u, with, diff)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, itm, diff)
exitwhen true
else
set charges = charges + withCharges
call SetItemCharges(itm, charges)
call RemoveItem(with)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, itm, withCharges)
endif
endif
set slot = slot + 1
endloop
else
call UnitStackItem(u, itm)
endif
endfunction
private function OnPickup takes nothing returns nothing
call PickupItem(GetTriggerUnit(), GetManipulatedItem())
endfunction
private function OnMoved takes nothing returns nothing
local unit u = GetInventoryManipulatingUnit()
local item itm = GetInventoryManipulatedItem()
local integer slotFrom = GetInventorySlotFrom()
local integer itemTypeId = GetItemTypeId(itm)
local integer charges
local item swapped
local integer swappedTypeId
local integer swappedCharges
local integer max = 0
local integer total
local integer diff
local trigger t
if slotFrom == GetInventorySlotTo() then // splitting
call UnitSplitItem(u, itm)
elseif not IsItemContainer(itemTypeId) then
set charges = GetItemCharges(itm)
set swapped = GetInventorySwappedItem()
set swappedTypeId = GetItemTypeId(swapped)
set swappedCharges = GetItemCharges(swapped)
if charges > 0 then
if swappedTypeId == itemTypeId and swappedCharges > 0 then
set max = GetItemMaxStacks(itemTypeId)
elseif GetItemContainerItem(swappedTypeId) == itemTypeId then
set max = GetItemContainerMaxStacks(swappedTypeId)
endif
endif
if max > 0 then
set total = charges + swappedCharges
if total > max then
if swappedCharges < max then // if not met, allow for standard replacement action
set t = GetAnyPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_DROP_ITEM)
call DisableTrigger(t)
call RemoveItem(itm) // Remove the item to prevent item swap from occurring
call EnableTrigger(t)
set t = GetAnyPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_PICKUP_ITEM)
call DisableTrigger(t)
call UnitAddItemToSlotById(u, itemTypeId, slotFrom) // Create and add new item replacing removed one
call EnableTrigger(t)
set t = null
set itm = UnitItemInSlot(u, slotFrom)
call SetItemCharges(itm, total - max)
call SetItemCharges(swapped, max)
set diff = max - charges
call FireEvent(EVENT_ITEM_CHARGES_REMOVED, u, itm, diff)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, swapped, diff)
endif
else
call SetItemCharges(swapped, total)
call RemoveItem(itm)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, swapped, charges)
endif
endif
set swapped = null
endif
set u = null
set itm = null
endfunction
static if LIBRARY_SmoothItemPickup then
private function OnSmoothPickup takes nothing returns nothing
call PickupItem(GetSmoothItemPickupUnit(), GetSmoothItemPickupItem())
endfunction
private struct SmoothChargesStack extends array
static method canPickup takes unit whichUnit, item whichItem returns boolean
local integer itemTypeId = GetItemTypeId(whichItem)
if IsItemContainer(itemTypeId) then
return not IsUnitItemFullyStacked(whichUnit, GetItemContainerItem(itemTypeId))
elseif IsItemStackable(itemTypeId) then
return not IsUnitItemFullyStacked(whichUnit, itemTypeId)
endif
return false
endmethod
implement optional SmoothPickupPredicateModule
endstruct
endif
private module StackNSplitInit
private static method onInit takes nothing returns nothing
set EVENT_ITEM_CHARGES_ADDED = CreateNativeEvent()
set EVENT_ITEM_CHARGES_REMOVED = CreateNativeEvent()
set table = TableArray[5]
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function OnPickup)
call RegisterNativeEvent(EVENT_ITEM_INVENTORY_MOVE, function OnMoved)
static if LIBRARY_SmoothItemPickup then
call RegisterNativeEvent(EVENT_ITEM_SMOOTH_PICKUP, function OnSmoothPickup)
call AddSmoothItemPickupCondition(SmoothChargesStack.create())
endif
endmethod
endmodule
private struct StackNSplit extends array
implement StackNSplitInit
endstruct
endlibrary
scope StackNSplitDemo initializer Init
globals
string description = "Potion of Healing stacks up to 10 times and splits for 2.\n" + /*
*/ "Healing Salve is a container for Potion of Healing. Holds up to 20 charges, splits for 5. Can be emptied.\n" + /*
*/ "Tiny Farm stacks up to 2 times and splits for 1.\n" + /*
*/ "Tine Castly is a container for Tiny Farm. Holds up to 7 charges, splits for 1. Has to have at least 1 charge to allow for splitting.\n" + /*
*/ "\nYou can order hero to stack up item charges despite their inventory being full."
endglobals
private function Init takes nothing returns nothing
call MakeItemStackable('phea', 10, 2)
call SetItemContainer('phea', 'hslv', 20, 5, 0)
call MakeItemStackable('tfar', 2, 1)
call SetItemContainer('tfar', 'tcas', 7, 1, 1)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 45, description)
endfunction
endscope
/*****************************************************************************
*
* ExtensionMethods
*
* General purpose functions that extend native jass interface.
*
******************************************************************************
*
* Item handle extension methods:
*
* function GetUnitItemCount takes unit whichUnit returns integer
* Returns the number of items equipped.
*
* function IsUnitInventoryFull takes unit whichUnit returns boolean
* Checks if unit inventory is full.
*
* function GetUnitItemSlot takes unit whichUnit, item whichItem returns integer
* Retrieves slot number of specified item equiped by unit whichUnit or -1 if not found.
*
* function IsItemAlive takes item whichItem returns boolean
* Verifies whether item is not destroyed and has a valid type id.
*
* function IsItemPickupable takes item whichItem returns boolean
* Verifies whether item can be picked up by a unit by validating its life and ownership status.
*
*****************************************************************************/
library ExtensionMethods
function GetUnitItemCount takes unit whichUnit returns integer
local integer size = UnitInventorySize(whichUnit)
local integer slot = 0
local integer result = 0
loop
exitwhen slot >= size
if UnitItemInSlot(whichUnit, slot) != null then
set result = result + 1
endif
set slot = slot + 1
endloop
return result
endfunction
function IsUnitInventoryFull takes unit whichUnit returns boolean
return GetUnitItemCount(whichUnit) == UnitInventorySize(whichUnit)
endfunction
function GetUnitItemSlot takes unit whichUnit, item whichItem returns integer
local integer slot = 0
local integer size
if UnitHasItem(whichUnit, whichItem) then
set size = UnitInventorySize(whichUnit)
loop
if UnitItemInSlot(whichUnit, slot) == whichItem then
return slot
endif
set slot = slot + 1
exitwhen slot >= size
endloop
endif
return -1 // NOT_FOUND
endfunction
function IsItemAlive takes item whichItem returns boolean
return GetItemTypeId(whichItem) != 0 and GetWidgetLife(whichItem) > 0.405
endfunction
function IsItemPickupable takes item whichItem returns boolean
return IsItemAlive(whichItem) and not IsItemOwned(whichItem) and IsItemVisible(whichItem)
endfunction
endlibrary
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Alloc ~~ By Sevion ~~ Version 1.09 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Alloc?
// - Alloc implements an intuitive allocation method for array structs
//
// =Pros=
// - Efficient.
// - Simple.
// - Less overhead than regular structs.
//
// =Cons=
// - Must use array structs (hardly a con).
// - Must manually call OnDestroy.
// - Must use Delegates for inheritance.
// - No default values for variables (use onInit instead).
// - No array members (use another Alloc struct as a linked list or type declaration).
//
// Methods:
// - struct.allocate()
// - struct.deallocate()
//
// These methods are used just as they should be used in regular structs.
//
// Modules:
// - Alloc
// Implements the most basic form of Alloc. Includes only create and destroy
// methods.
//
// Details:
// - Less overhead than regular structs
//
// - Use array structs when using Alloc. Put the implement at the top of the struct.
//
// - Alloc operates almost exactly the same as default structs in debug mode with the exception of onDestroy.
//
// How to import:
// - Create a trigger named Alloc.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Nestharus for the method of allocation and suggestions on further merging.
// - Bribe for suggestions like the static if and method names.
// - PurgeandFire111 for some suggestions like the merging of Alloc and AllocX as well as OnDestroy stuff.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Alloc
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
/*****************************************************************************
*
* InventoryEvent v1.0.1.8
* by Bannar
*
* For intuitive inventory event handling.
*
******************************************************************************
*
* Requirements:
*
* RegisterPlayerUnitEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
* Event API:
*
* integer EVENT_ITEM_INVENTORY_MOVE
* integer EVENT_ITEM_INVENTORY_USE
*
* Use RegisterNativeEvent or RegisterIndexNativeEvent for event registration.
* GetNativeEventTrigger and GetIndexNativeEventTrigger provide access to trigger handles.
*
*
* function GetInventoryManipulatingUnit takes nothing returns unit
* Returns unit which manipulated event item.
*
* function GetInventoryManipulatedItem takes nothing returns item
* Returns manupilated event item.
*
* function GetInventorySlotFrom takes nothing returns integer
* Returns slot index of manipulated item from which it was moved or used.
*
* function GetInventorySlotTo takes nothing returns integer
* Returns slot index of manipulated item to which it was moved.
*
* function GetInventorySwappedItem takes nothing returns item
* Returns item which manipulated item switched position with if any.
*
*****************************************************************************/
library InventoryEvent requires RegisterPlayerUnitEvent, ExtensionMethods
globals
integer EVENT_ITEM_INVENTORY_MOVE
integer EVENT_ITEM_INVENTORY_USE
endglobals
globals
private unit eventUnit = null
private item eventItem = null
private integer eventSlotFrom = -1
private integer eventSlotTo = -1
endglobals
function GetInventoryManipulatingUnit takes nothing returns unit
return eventUnit
endfunction
function GetInventoryManipulatedItem takes nothing returns item
return eventItem
endfunction
function GetInventorySlotFrom takes nothing returns integer
return eventSlotFrom
endfunction
function GetInventorySlotTo takes nothing returns integer
return eventSlotTo
endfunction
function GetInventorySwappedItem takes nothing returns item
return UnitItemInSlot(eventUnit, eventSlotTo)
endfunction
function GetEventInventoryUnit takes nothing returns unit
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetEventInventoryUnit is obsolete, use GetInventoryManipulatingUnit instead.")
return GetInventoryManipulatingUnit()
endfunction
function GetEventInventoryItem takes nothing returns item
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetEventInventoryItem is obsolete, use GetInventoryManipulatedItem instead.")
return GetInventoryManipulatedItem()
endfunction
function GetEventInventorySlotFrom takes nothing returns integer
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetEventInventorySlotFrom is obsolete, use GetInventorySlotFrom instead.")
return GetInventorySlotFrom()
endfunction
function GetEventInventorySlotTo takes nothing returns integer
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetEventInventorySlotTo is obsolete, use GetInventorySlotTo instead.")
return GetInventorySlotTo()
endfunction
function GetEventInventorySwapped takes nothing returns item
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetEventInventorySwapped is obsolete, use GetInventorySwappedItem instead.")
return GetInventorySwappedItem()
endfunction
function GetInventoryEventTrigger takes integer whichEvent returns trigger
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetInventoryEventTrigger is obsolete, use GetNativeEventTrigger instead.")
return GetNativeEventTrigger(whichEvent)
endfunction
function RegisterInventoryEvent takes code func, integer whichEvent returns nothing
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function RegisterInventoryEvent is obsolete, use RegisterNativeEvent instead.")
call RegisterNativeEvent(whichEvent, func)
endfunction
private function FireEvent takes integer evt, unit u, item itm, integer slotFrom, integer slotTo returns nothing
local unit prevUnit = eventUnit
local item prevItem = eventItem
local integer prevSlotFrom = eventSlotFrom
local integer prevSlotTo = eventSlotTo
local integer playerId = GetPlayerId(GetOwningPlayer(u))
set eventUnit = u
set eventItem = itm
set eventSlotFrom = slotFrom
set eventSlotTo = slotTo
call TriggerEvaluate(GetNativeEventTrigger(evt))
if IsNativeEventRegistered(playerId, evt) then
call TriggerEvaluate(GetIndexNativeEventTrigger(playerId, evt))
endif
set eventUnit = prevUnit
set eventItem = prevItem
set eventSlotFrom = prevSlotFrom
set eventSlotTo = prevSlotTo
set prevUnit = null
set prevItem = null
endfunction
private function OnItemOrder takes nothing returns nothing
local integer order = GetIssuedOrderId()
local unit u = GetTriggerUnit()
local item itm
local integer slotFrom
local integer slotTo
if order >= 852002 and order <= 852007 then // between moveslot1 and moveslot6
set itm = GetOrderTargetItem()
set slotFrom = GetUnitItemSlot(u, itm)
set slotTo = order - 852002 // moveslot1
call FireEvent(EVENT_ITEM_INVENTORY_MOVE, u, itm, slotFrom, slotTo)
else
set slotFrom = order - 852008 // useslot1
set itm = UnitItemInSlot(u, slotFrom)
call FireEvent(EVENT_ITEM_INVENTORY_USE, u, itm, slotFrom, -1)
endif
set u = null
set itm = null
endfunction
private function OnAnyOrder takes nothing returns nothing
local integer order = GetIssuedOrderId()
if order >= 852002 and order <= 852013 then // between moveslot1 and useslot6
call OnItemOrder()
endif
endfunction
private module InventoryEventInit
private static method onInit takes nothing returns nothing
set EVENT_ITEM_INVENTORY_MOVE = CreateNativeEvent()
set EVENT_ITEM_INVENTORY_USE = CreateNativeEvent()
set MOVED = EVENT_ITEM_INVENTORY_MOVE
set USED = EVENT_ITEM_INVENTORY_USE
// MOVED is order of type TARGET_ORDER yet USED can be anyone of them
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function OnAnyOrder)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function OnAnyOrder)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function OnAnyOrder)
endmethod
endmodule
struct InventoryEvent extends array
// Events below are depreated in favor of EVENT_ alike globals
readonly static integer MOVED
readonly static integer USED
implement InventoryEventInit
endstruct
endlibrary
/*****************************************************************************
*
* SmoothItemPickup v1.0.2.5
* by Bannar
*
* Allows for item pickup despite unit inventory being full.
*
* Special thanks for Jampion.
*
******************************************************************************
*
* Requirements:
*
* RegisterPlayerUnitEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
* ListT by Bannar
* hiveworkshop.com/threads/containers-list-t.249011/
*
******************************************************************************
*
* Event API:
*
* integer EVENT_ITEM_SMOOTH_PICKUP
*
* Use RegisterNativeEvent or RegisterIndexNativeEvent for event registration.
* GetNativeEventTrigger and GetIndexNativeEventTrigger provide access to trigger handles.
*
*
* function GetSmoothItemPickupUnit takes nothing returns unit
* Returns unit attempting to pickup event item.
*
* function GetSmoothItemPickupItem takes nothing returns item
* Returns item that is being picked up.
*
******************************************************************************
*
* Interface SmoothItemPickupPredicate:
*
* static method canPickup takes unit whichUnit, item whichItem returns boolean
* Determinates whether unit can pickup specified item.
*
* module SmoothPickupPredicateModule
* Declares body for new predicate type.
*
*
* Predicate implementation example:
*
* | struct MyPredicate extends array
* | static method canPickup takes unit whichUnit, item whichItem returns boolean
* | return true
* | endmethod
* |
* | implement SmoothPickupPredicateModule
* | endstruct
*
******************************************************************************
*
* Constants:
*
* constant real PICK_UP_RANGE = 150
*
* Functions:
*
* function AddSmoothItemPickupCondition takes SmoothItemPickupPredicate predicate returns nothing
* Adds new condition for item to be picked up smoothly.
* Conditions are aggregated in 'OR' fashion.
*
* function RemoveSmoothItemPickupCondition takes SmoothItemPickupPredicate predicate returns nothing
* Removes specified condition from predicate list.
*
*****************************************************************************/
library SmoothItemPickup requires /*
*/ RegisterPlayerUnitEvent /*
*/ ListT /*
*/ ExtensionMethods
globals
private constant real PICK_UP_RANGE = 150
endglobals
globals
integer EVENT_ITEM_SMOOTH_PICKUP
endglobals
native UnitAlive takes unit whichUnit returns boolean
globals
private IntegerList conditions = 0
private IntegerList ongoing = 0
private Table table = 0
private timer looper = CreateTimer()
private unit eventUnit = null
private item eventItem = null
private trigger array triggers
private unit argUnit = null
private item argItem = null
endglobals
struct SmoothItemPickupPredicate extends array
implement Alloc
static method canPickup takes unit whichUnit, item whichItem returns boolean
return false
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 SmoothPickupPredicateModule
private delegate SmoothItemPickupPredicate predicate
private static method onInvoke takes nothing returns boolean
return thistype.canPickup(argUnit, argItem)
endmethod
static method create takes nothing returns thistype
local thistype this = SmoothItemPickupPredicate.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
function GetSmoothItemPickupUnit takes nothing returns unit
return eventUnit
endfunction
function GetSmoothItemPickupItem takes nothing returns item
return eventItem
endfunction
function AddSmoothItemPickupCondition takes SmoothItemPickupPredicate predicate returns nothing
if predicate != 0 then
call conditions.push(predicate)
endif
endfunction
function RemoveSmoothItemPickupCondition takes SmoothItemPickupPredicate predicate returns nothing
if predicate != 0 then
call conditions.erase(conditions.find(predicate))
endif
endfunction
private struct PeriodicData extends array
unit picker
item itm
real range
implement Alloc
static method create takes unit u, real range returns thistype
local thistype this = allocate()
set this.picker = u
set this.range = range
call ongoing.push(this)
set table[GetHandleId(u)] = this
return this
endmethod
method destroy takes nothing returns nothing
call table.remove(GetHandleId(picker))
set picker = null
set itm = null
call ongoing.erase(ongoing.find(this))
if ongoing.empty() then
call PauseTimer(looper)
endif
call deallocate()
endmethod
static method get takes integer index returns thistype
if table.has(index) then
return table[index]
endif
return 0
endmethod
endstruct
private function FireEvent takes unit u, item itm returns nothing
local unit prevUnit = eventUnit
local item prevItem = eventItem
local integer playerId = GetPlayerId(GetOwningPlayer(u))
set eventUnit = u
set eventItem = itm
call TriggerEvaluate(GetNativeEventTrigger(EVENT_ITEM_SMOOTH_PICKUP))
if IsNativeEventRegistered(playerId, EVENT_ITEM_SMOOTH_PICKUP) then
call TriggerEvaluate(GetIndexNativeEventTrigger(playerId, EVENT_ITEM_SMOOTH_PICKUP))
endif
set eventUnit = prevUnit
set eventItem = prevItem
set prevUnit = null
set prevItem = null
endfunction
private function Test takes unit u, item itm, real range returns boolean
local real dx
local real dy
if UnitHasItem(u, itm) then
return true
endif
set dx = GetItemX(itm) - GetUnitX(u)
set dy = GetItemY(itm) - GetUnitY(u)
// Assumes range is multipled to avoid invoking SquareRoot
return (dx * dx + dy * dy) <= range
endfunction
private function OnCallback takes nothing returns nothing
local IntegerListItem iter = ongoing.first
local PeriodicData data
loop
exitwhen iter == 0
set data = iter.data
if not UnitAlive(data.picker) or GetUnitCurrentOrder(data.picker) != 851986 /*
*/ or not IsItemPickupable(data.itm) then // order move
call data.destroy()
else
if Test(data.picker, data.itm, data.range) then
call FireEvent(data.picker, data.itm)
call data.destroy()
endif
endif
set iter = iter.next
endloop
endfunction
private function OnNullTimer takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
local unit u = table.unit[id]
local item itm = table.item[-id]
if UnitAlive(u) and IsItemPickupable(itm) then
call FireEvent(u, itm)
endif
call table.unit.remove(id)
call table.item.remove(-id)
call DestroyTimer(t)
set t = null
set u = null
set itm = null
endfunction
private function OnTargetOrder takes nothing returns nothing
local PeriodicData data
local boolean proceed = false
local IntegerListItem iter
local SmoothItemPickupPredicate condition
local real collision
local real range
local timer tmr
local real angle
local real x
local real y
local trigger t
set argUnit = GetTriggerUnit()
set argItem = GetOrderTargetItem()
if not IsUnitInventoryFull(argUnit) or GetIssuedOrderId() != 851971 then // order smart
return
elseif argItem == null or IsItemPowerup(argItem) then
return
endif
set iter = conditions.first
loop
exitwhen iter == 0
set condition = iter.data
if TriggerEvaluate(triggers[condition]) then
set proceed = true
exitwhen true
endif
set iter = iter.next
endloop
if not proceed then
return
endif
set collision = BlzGetUnitCollisionSize(argUnit)
set range = (PICK_UP_RANGE + collision) * (PICK_UP_RANGE + collision)
if Test(argUnit, argItem, range) then
// Ensures order is finished before item is picked up.
// Fixes the issue with unit moving towards the item location, rather than stopping
set tmr = CreateTimer()
set table.unit[GetHandleId(tmr)] = argUnit
set table.item[-GetHandleId(tmr)] = argItem
call TimerStart(tmr, 0.0, false, function OnNullTimer)
set tmr = null
else // if unit is not nearby target item, issue artificial move order
set data = PeriodicData.get(GetHandleId(argUnit))
if data == 0 then
if ongoing.empty() then
call TimerStart(looper, 0.031250000, true, function OnCallback)
endif
set data = PeriodicData.create(argUnit, range)
endif
set data.itm = argItem
set angle = bj_RADTODEG * Atan2(GetUnitY(argUnit) - GetItemY(argItem), GetUnitX(argUnit) - GetItemX(argItem))
set x = GetItemX(argItem) + PICK_UP_RANGE * Cos(angle * bj_DEGTORAD)
set y = GetItemY(argItem) + PICK_UP_RANGE * Sin(angle * bj_DEGTORAD)
set t = GetAnyPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call DisableTrigger(t)
call IssuePointOrderById(argUnit, 851986, x, y) // order move
call EnableTrigger(t)
endif
endfunction
private module SmoothItemPickupInit
private static method onInit takes nothing returns nothing
set EVENT_ITEM_SMOOTH_PICKUP = CreateNativeEvent()
set conditions = IntegerList.create()
set ongoing = IntegerList.create()
set table = Table.create()
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function OnTargetOrder)
endmethod
endmodule
private struct SmoothItemPickup extends array
implement SmoothItemPickupInit
endstruct
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