- Joined
- May 9, 2014
- Messages
- 1,820
Knowing of the possible new API for the newer patches of Warcraft III and finally having used it, I have decided to rewrite the Destructable Group. Perhaps, this could be useful towards those who would like to create a group of destructibles or items.
JASS:
library WidgetUtils requires /*
* _______________________
* | |
* | Requirements Tab |
* |_______________________|
*
* Required:
* ______________________________
*
*/ Table, /* https://www.hiveworkshop.com/threads/snippet-new-table.188084/
*
* - Allows for the abstraction of the typecasting of handle types
* - If for native types, a Typecast lib would work better.
* - Also grants the library nearly unlimited amount of instances to work with, which would drastically
* save a lot of space.
*
*/ AllocationAndLinks, /* https://www.hiveworkshop.com/threads/allocation-and-linked-list-bundle.293621/#post-3159130
*
* - Required for the creation of the links through textmacros.
* - Also provides a faster allocation scheme if not in debug mode.
*
* ______________________________
*
* Optional:
* ______________________________
*
*/ optional TimerStruct, /*
*
* - Used for simplifying the process of getting a timer.
* - Due to personal use, the CodeEx class is bundled with it.
*
* - CodeEx is a simple class / struct that eases the evaluation of code.
* - It does this via abstraction of the code into a triggercondition...
* - which gets evaluated when a method run() is called.
* - It optionally requires Table for unlimited instances.
*
* - Otherwise not really necessary
*
*/ optional RegenTracker /*
*
* - Truly optional. Only added for personal / ordering purposes.
*
* ___________________________
* | |
* | Widget Utils |
* | |
* | Version: |
* | - 1.01.1 |
* |___________________________|
*
* __________________________________________________
* | |
* | Motto: |
* | |
* | - Why do units get to have all the fun? |
* | |
* |__________________________________________________|
*
* Widget Utils:
* ___________________________________________________________________________________
*
* A library that grants you more control over your destructables and items.
* If you have a group for units, why not have one for your destructables and
* items too?
* ___________________________________________________________________________________
*
*
*
* Features:
* ___________________________________________________________________________________
*
* - a global debug flag
* - two generated linked list modules.
* - Two module initializers.
*
* - Two private structs
* - Two public structs
* - emulation functions for the public structs.
* ___________________________________________________________________________________
*
*
*
* What does each feature do?
* ___________________________________________________________________________________
*
* - Global debug flag
* If true, the library will print out more detailed versions of its' debug messages.
* (Only prints if DEBUG_MODE is already enabled)
*
* - two generated linked list modules
* These modules (generated from textmacros) make the hassle of creating linked lists
* for the private structs a breeze. (Explanation for this to be given below)
*
* - Two module initializers
* These modules instantiate the Table indices used by the private structs in order
* to simulate variable creation. The textmacros for generating such indices are in
* the AllocationAndLinks library.
*
* - Two private structs.
* These structs will abstract active destructables and items into their internal
* representation. It handles the linkage of a destructable to its' interpreted selves,
* and possible groups. Thus, the generation of the linked lists makes it a lot
* easier to write.
*
* - Two public structs.
* These are the effective structs (of use to user) that allow you to do more with
* a cluster of destructables or items with less verbosity. Think of them as
* a native type group.
*
* - emulation functions for the public structs.
* If you're more into functions, then the emulation functions will make the library
* easier for you to follow. With names that closely mimic that of the native group
* functionalities, emulation functions are surely not going to be hard to follow.
*
* ___________________________________________________________________________________
*
* _______________________________________
* | |
* | More details on functions: (API) |
* |_______________________________________|
*
* Legend:
* thistype -> static
* thistype() -> instance
*
* [] -> Actual encapsulation property.
* ~{} -> Intended encapsulation property.
*
* struct Dest:
*
* behavior = ~{private}
*
* integer Dest().int
* destructable Dest().destructable
* integer Dest().count
* - Dest().int is the return value of Dest.get()
* - Dest().destructable is the destructable being represented by the instance.
* - Dest().count is the number of nodes in the current list.
* Only affects head nodes.
*
* |behavior
*
* [private] Dest.get(destructable d) -> Dest
* - Gets the head value of the internal representation of a destructable.
*
* [private] Dest().removeFromHead(Dest headVar)
* - Removes an internal representation of a desired destructable from a
* specific group.
*
* ~{private} Dest().destroy()
* - Destroys an internal representation of a desired destructable. Inter-
* nally calls Dest().removeFromHead.
*
* [private] Dest().addToHead(Dest headVar)
* - A submethod that adds the internal representation of a desired destructable
* into a specific list.
*
* Dest.destroyObject(destructable d)
* - Destroys all internal representations of a desired destructable.
* May not be op-limit safe.
*
* [private] Dest.create(destructable d) -> Dest
* - Creates a new internal representation of a desired destructable.
* If there are no internal representations, the new internal repre-
* sentation acts as the head of the list of internal representations
* of the destructable in the future.
*
* [private] Dest.exec_destroyHeadNode()
* - An internal loop that destroys a certain list (composed of an empty
* head node and internal representations of destructables).
*
* It cheats the op-limit by refreshing every 80 iterations.
*
* ~{private} Dest().destroyHeadNode()
* - Initiator function for the above function.
* It is OP-limit-safe.
*
* ~{private} Dest.createHeadNode()
* - Creates a new head node for the generation of a linked list.
* - Basically allocates an instance and treats it as the head of
* a list of internal representations of destructables.
*
* ~{private} Dest().isDestructableInList(destructable d) -> boolean
* - Checks if any internal representation of the instance belongs
* to the list.
*
* ~{private} Dest().addDestructable(destructable d) -> boolean
* - Adds a destructable into the list if any of its' internal repre
* sentations have not been added already.
*
* ~{private} Dest().removeDestructable(destructable d) -> boolean
* - Removes a destructable from a list if any of its' internal repre
* sentations have been placed therein.
*
* [private] Dest.isDestInList_return_this
* - The current internal representation of a destructable as returned
* in the method Dest().isDestructableInList()
*
* struct Item:
*
* - To save word space, everything in Item is operationally the same.
* - For convenience of understanding, consider that this focuses on
* items instead.
*
* ~{type} struct destgroup
*
* [private] Dest destgroup().headNode
* [readonly] destgroup.forGroup_this
* [readonly] Dest.forGroup_iter
* - destgroup().headNode -> Returns the head node of the destgroup.
* -> Actually returns a value of type Dest.
* - destgroup.forGroup_this -> Returns the group currently being iterated upon
* - destgroup.forGroup_iter -> Returns the current Dest interpretation of a
* destructable.
*
* Only allows you access to the destructable.
* (Operational intention)
*
* destgroup.getEnumDestGroup() -> destgroup
* - returns destGroup.forGroup_this
*
* destgroup.getEnumDest() -> destructable
* - returns destGroup.forGroup_iter.destructable
* - Only works within a for call.
*
* destgroup().getCount() -> integer
* - returns the current group count.
* - Does not check for invalid instances. [O(1) though]
*
* destgroup().first() -> destructable
* - To follow the convention of Blizzard, this returns the destructable
* being represented by the last instance of the group.
*
* destgroup().last() -> destructable
* - returns the destructable represented by the first instance of the
* group.
*
* destgroup().clear()
* - Clears the list by destroying the head node and recreating it again.
* - Done because of convenience (less verbosity over exposing a new
* method in Dest specifically for clearing)
*
* [private] [code] destgroup.exec_for()
* - The function which iterates over a certain list. It checks if the
* destructable is still valid before execution of the code given.
*
* destgroup().for(code func)
* - This executes a certain function for a group.
*
* destgroup().getCountSafe() -> integer
* - Cleans out the invalid handles before returning the current number count.
* - Internally calls destgroup().for(function DoNothing)
*
* destgroup().destroy()
* - Destroys a destgroup instance. Clears out the head node without recreating
* it.
*
* destgroup().isDestIn(destructable d) -> boolean
* - Internally calls Dest(destgroup().headNode).isDestructableInList(d)
*
* destgroup().removeDest(destructable d) -> boolean
* - If an internal representation of a destructable is in the list, the instance
* is removed, with success of removal being the boolean result.
* - Internally calls Dest(destgroup.headNode).removeDestructable(d)
*
* destgroup().addDest(destructable d) -> boolean
* - Adds a destructable into the list.
* - Internally calls Dest(destgroup.headNode).addDestructable(d)
*
* [private] [filter] destgroup.isInCircle -> boolean
* - Checks if an enumerated destructable is within the area of a circle.
* - The dimensions of a circle are specified in the function enumInCircle.
*
* [private] [code] destgroup.onAddToFunc()
* - Adds the enumerated destructables into the list.
*
* destgroup().enumInRect(rect c, boolexpr filter)
* - Adds the appropriate destructables into the group as specified by
* the filter.
* - Internally calls destgroup().clear() before adding.
*
* destgroup().enumInCircle(real x, real y, real rad, boolexpr filter)
* - Adds the appropriate destructables into the group as specified by
* the filter.
*
* - Combines the filter and destgroup.isInCircle() before
* internally calling destgroup().enumInRect()
*
* destgroup.create() -> destgroup
* - Returns a new destgroup instance.
*
* ~[type] struct itemgroup
*
* - Again, for convenience, the behavior of every method follows that of destgroup.
* - For ease of understanding, simply replace Dest or dest with Item or item respectively.
*
* functions:
*
* Legend:
* -> Internal function called.
* return type
*
* - The functions explicitly provided are wrapper functions to the methods above.
* - With that being said, the functions found are:
*
* Destructables:
* _______________________________________________________________________________
*
* DestroyDestGroup(destgroup whichGroup)
* -> destgroup(whichGroup).destroy()
*
* CreateDestGroup() -> destgroup
* -> destgroup.create()
*
* GroupEnumDestructablesInRect(destgroup whichGroup, rect r, boolexpr filter)
* -> destgroup(whichGroup).enumInRect(r, filter)
*
* GroupEnumDestructablesInRange(destgroup whichGroup, real x, real y, real rad, boolexpr filter)
* -> destgroup(whichGroup).enumInCircle(x, y, rad, filter)
*
* ForDestGroup(destgroup whichGroup, code func)
* -> destgroup(whichGroup).for(func)
*
* GetEnumDestructableEx() -> destructable
* -> destgroup.getEnumDest()
*
* GetEnumDestGroup() -> destructable
* -> destgroup.getEnumDestGroup()
*
* FirstOfDestGroup(destgroup whichGroup) -> destructable.
* -> destgroup(whichGroup).first()
*
* LastOfDestGroup(destgroup whichGroup) -> destructable.
* -> destgroup(whichGroup).last()
*
* CountDestInDestGroup(destgroup whichGroup, boolean safe) -> integer.
* If safe is true:
* -> destgroup(whichGroup).getCountSafe()
* else if safe is false:
* -> destgroup(whichGroup).getCount()
*
* GroupAddDestructable(destgroup whichGroup, destructable dest) -> boolean
* -> destgroup(whichGroup).addDest(dest)
*
* GroupRemoveDestructable(destgroup whichGroup, destructable dest) -> boolean
* -> destgroup(whichGroup).removeDest(dest)
*
* IsDestructableInGroup(destructable dest, destgroup whichGroup) -> boolean
* -> destgroup(whichGroup).isDestIn(dest)
*
* _______________________________________________________________________________
*
*
*
* Items:
* _______________________________________________________________________________
*
* DestroyItemGroup(itemgroup whichGroup)
* -> itemgroup(whichGroup).destroy()
*
* CreateItemGroup() -> itemgroup
* -> itemgroup.create()
*
* GroupEnumItemsInRect(itemgroup whichGroup, rect r, boolexpr filter)
* -> itemgroup(whichGroup).enumInRect(r, filter)
*
* GroupEnumItemsInRange(itemgroup whichGroup, real x, real y, real rad, boolexpr filter)
* -> itemgroup(whichGroup).enumInCircle(x, y, rad, filter)
*
* ForItemGroup(itemgroup whichGroup, code func)
* -> itemgroup(whichGroup).for(func)
*
* GetEnumDestructableEx() -> item
* -> itemgroup.getEnumItem()
*
* GetEnumItemGroup() -> item
* -> itemgroup.getEnumItemGroup()
*
* FirstOfItemGroup(itemgroup whichGroup) -> item.
* -> itemgroup(whichGroup).first()
*
* LastOfItemGroup(itemgroup whichGroup) -> item.
* -> itemgroup(whichGroup).last()
*
* CountDestInDestGroup(destgroup whichGroup, boolean safe) -> integer.
* If safe is true:
* -> destgroup(whichGroup).getCountSafe()
* else if safe is false:
* -> destgroup(whichGroup).getCount()
*
* _______________________________________________________________________________
*/
globals
private constant boolean ALLOW_DEBUG = false
endglobals
//! runtextmacro link_moduleH("local", "private")
//! runtextmacro link_moduleH("list", "private")
private module Dest_init
private static method onInit takes nothing returns nothing
set Num1 = Table.create()
set Num2 = Table.create()
set Num3 = Table.create()
endmethod
endmodule
private struct Dest extends array
implement AllocH
implement DoubleLinkH_local
implement DoubleLinkH_list
//! runtextmacro op_read("Num1", "int", "integer", "0")
//! runtextmacro op_read("Num2", "destructable", "destructable", "null")
//! runtextmacro op_read("Num3", "count", "integer", "0")
implement Dest_init
private static thistype exec_this = 0
private static thistype exec_head = 0
private static integer exec_count = 0
// Returns the head of the destructable object
private static method get takes destructable d returns thistype
return thistype(GetHandleId(d)).int
endmethod
private method removeFromHead takes thistype headVar returns nothing
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Value for requesting thistype is null.")
return
elseif headVar == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Parameter (thistype headVar) is null.")
return
elseif headVar.list_head != headVar then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Cannot remove instance from the desired head.")
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Parameter (thistype headVar) is not a head of its' list.")
return
elseif list_head != headVar then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Cannot remove instance from the desired head.")
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Instannce exists in another list [" + I2S(list_head) + "]")
return
endif
set thistype(this.list_head).count = thistype(this.list_head).count - 1
set this.list_head = 0
call list_pop()
endmethod
method destroy takes nothing returns nothing
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").destroy: Cannot destroy object 0!")
return
endif
static if ALLOW_DEBUG then
debug call BJDebugMsg("thistype(" + I2S(this) + ").destroy:")
endif
if get(destructable) == this then
if this.local_next != this then
set thistype(GetHandleId(destructable)).int = this.local_next
else
set thistype(GetHandleId(destructable)).int = 0
endif
endif
call this.removeFromHead(this.list_head)
set this.destructable = null
set this.local_head = 0
call this.local_pop()
call this.deallocate()
endmethod
// Will only add an instance to a head that exists locally.
private method addToHead takes thistype headVar returns nothing
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Value for requesting thistype is null.")
return
elseif headVar == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Parameter (thistype headVar) is null.")
return
elseif headVar.list_head != headVar then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Cannot add instance to the desired head.")
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Parameter (thistype headVar) is not a head of its' list.")
return
elseif list_head != 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Cannot add instance to the desired head.")
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Instannce exists in another list [" + I2S(list_head) + "]")
return
endif
call this.list_insert(headVar)
set this.list_head = headVar
set headVar.count = headVar.count + 1
endmethod
static method destroyObject takes destructable d returns nothing
local thistype head = get(d)
local thistype this = head
if d == null then
debug call BJDebugMsg("thistype.destroyObject: Provided parameter (destructable d) is null.")
return
elseif head == 0 then
debug call BJDebugMsg("thistype.destroyObject: Provided parameter (destructable d) is not cached in the system.")
return
endif
set this = head.local_next
loop
exitwhen this == head
call destroy()
set this = head.local_next
endloop
call head.destroy()
endmethod
private static method create takes destructable d returns thistype
local thistype head = get(d)
local thistype this = 0
if head == 0 then
set head = allocate()
set head.destructable = d
set thistype(GetHandleId(d)).int = head
call head.local_insert(head)
set this = head
else
set this = allocate()
set this.destructable = d
call head.local_insert(this)
endif
return this
endmethod
private static method exec_destroyHeadNode takes nothing returns nothing
loop
exitwhen exec_this == exec_head or exec_count >= 80
call exec_this.destroy()
set exec_this = exec_head.list_next
set exec_count = exec_count + 1
endloop
if exec_count >= 80 then
set exec_count = 0
call ForForce(bj_FORCE_PLAYER[0], function thistype.exec_destroyHeadNode)
return
endif
set exec_this = 0
set exec_head = 0
endmethod
method destroyHeadNode takes nothing returns nothing
local thistype head = this
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ".destroyHeadNode: Attempted to destroy allocator!")
return
elseif this.list_head != this then
debug call BJDebugMsg("thistype(" + I2S(this) + ".destroyHeadNode: Attempted to destroy a node!")
debug call BJDebugMsg("thistype(" + I2S(this) + ".destroyHeadNode: Node provided is not the head of its' list!")
return
endif
set this = head.list_next
set exec_head = head
set exec_this = this
set exec_count = 0
call ForForce(bj_FORCE_PLAYER[0], function thistype.exec_destroyHeadNode)
set head.count = 0
set head.list_head = 0
call head.list_pop()
call head.deallocate()
endmethod
static method createHeadNode takes nothing returns thistype
local thistype this = allocate()
call this.list_insert(this)
set this.list_head = this
return this
endmethod
private static thistype isDestInList_return_this = 0
method isDestructableInList takes destructable d returns boolean
local thistype head = get(d)
local thistype that = this
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").isDestructableInList: Instance provided is null.")
return false
elseif this.list_head != this then
debug call BJDebugMsg("thistype(" + I2S(this) + ").isDestructableInList: Instance provided is not head of its list.")
return false
elseif d == null then
debug call BJDebugMsg("thistype.isDestructableInList: Parameter (destructable d) is null.")
return false
endif
set this = head.local_next
loop
exitwhen this == head or this.list_head == that
set this = this.local_next
endloop
if this == head and this.list_head != that then
set this = 0
endif
set isDestInList_return_this = this
return this != 0
endmethod
method addDestructable takes destructable d returns boolean
local thistype head = get(d)
local thistype that = head
local thistype last = 0
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addDestructable: Instance provided is null.")
return false
elseif this.list_head != this then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addDestructable: Instance provided is not head of its list.")
return false
elseif d == null then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addDestructable: Parameter (destructable d) is null.")
return false
elseif isDestructableInList(d) then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addDestructable: Parameter (destructable d) is in the list of the provided instance.")
return false
endif
if head == 0 then
set head = create(d)
set that = head
call that.addToHead(this)
return true
endif
set that = that.local_next
loop
if that.list_head == 0 then
set last = that
exitwhen true
endif
exitwhen that == head
set that = that.local_next
endloop
if last != 0 then
call last.addToHead(this)
return true
endif
return false
endmethod
method removeDestructable takes destructable d returns boolean
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeDestructable: Instance provided is null.")
return false
elseif this.list_head != this then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeDestructablee: Instance provided is not head of its list.")
return false
elseif d == null then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeDestructable: Parameter (destructable d) is null.")
return false
elseif not isDestructableInList(d) then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeDestructable: Parameter (destructable d) is not in the list of the provided instance.")
return false
endif
set this = isDestInList_return_this
call this.destroy()
return true
endmethod
endstruct
private module Item_init
private static method onInit takes nothing returns nothing
set Num1 = Table.create()
set Num2 = Table.create()
endmethod
endmodule
private struct Item extends array
implement AllocH
implement DoubleLinkH_local
implement DoubleLinkH_list
//! runtextmacro op_read("Num1", "int", "integer", "0")
//! runtextmacro op_read("Num2", "item", "item", "null")
//! runtextmacro op_read("Num3", "count", "integer", "0")
implement Item_init
private static thistype exec_this = 0
private static thistype exec_head = 0
private static integer exec_count = 0
// Returns the head of the item object
private static method get takes item d returns thistype
return thistype(GetHandleId(d)).int
endmethod
private method removeFromHead takes thistype headVar returns nothing
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Value for requesting thistype is null.")
return
elseif headVar == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Parameter (thistype headVar) is null.")
return
elseif headVar.list_head != headVar then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Cannot remove instance from the desired head.")
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Parameter (thistype headVar) is not a head of its' list.")
return
elseif list_head != headVar then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Cannot remove instance from the desired head.")
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeFromHead: Instannce exists in another list [" + I2S(list_head) + "]")
return
endif
set thistype(this.list_head).count = thistype(this.list_head).count - 1
set this.list_head = 0
call list_pop()
endmethod
method destroy takes nothing returns nothing
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").destroy: Cannot destroy object 0!")
return
endif
if get(item) == this then
if this.local_next != this then
set thistype(GetHandleId(item)).int = this.local_next
else
set thistype(GetHandleId(item)).int = 0
endif
endif
call this.removeFromHead(this.list_head)
set this.item = null
set this.local_head = 0
call this.local_pop()
call this.deallocate()
endmethod
// Will only add an instance to a head that exists locally.
private method addToHead takes thistype headVar returns nothing
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Value for requesting thistype is null.")
return
elseif headVar == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Parameter (thistype headVar) is null.")
return
elseif headVar.list_head != headVar then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Cannot add instance to the desired head.")
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Parameter (thistype headVar) is not a head of its' list.")
return
elseif list_head != 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Cannot add instance to the desired head.")
debug call BJDebugMsg("thistype(" + I2S(this) + ").addToHead: Instannce exists in another list [" + I2S(list_head) + "]")
return
endif
call this.list_insert(headVar)
set this.list_head = headVar
set headVar.count = headVar.count + 1
endmethod
static method destroyObject takes item d returns nothing
local thistype head = get(d)
local thistype this = head
if d == null then
debug call BJDebugMsg("thistype.destroyObject: Provided parameter (item d) is null.")
return
elseif head == 0 then
debug call BJDebugMsg("thistype.destroyObject: Provided parameter (item d) is not cached in the system.")
return
endif
set this = head.local_next
loop
exitwhen this == head
call destroy()
set this = head.local_next
endloop
call head.destroy()
endmethod
static method create takes item d returns thistype
local thistype head = get(d)
local thistype this = 0
if head == 0 then
set head = allocate()
set head.item = d
set thistype(GetHandleId(d)).int = head
call head.local_insert(head)
set this = head
else
set this = allocate()
set this.item = d
call head.local_insert(this)
endif
return this
endmethod
private static method exec_destroyHeadNode takes nothing returns nothing
loop
exitwhen exec_this == exec_head or exec_count >= 80
call exec_this.destroy()
set exec_this = exec_head.list_next
set exec_count = exec_count + 1
endloop
if exec_count >= 80 then
set exec_count = 0
call ForForce(bj_FORCE_PLAYER[0], function thistype.exec_destroyHeadNode)
return
endif
set exec_this = 0
set exec_head = 0
endmethod
method destroyHeadNode takes nothing returns nothing
local thistype head = this
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ".destroyHeadNode: Attempted to destroy allocator!")
return
elseif this.list_head != this then
debug call BJDebugMsg("thistype(" + I2S(this) + ".destroyHeadNode: Attempted to destroy a node!")
debug call BJDebugMsg("thistype(" + I2S(this) + ".destroyHeadNode: Node provided is not the head of its' list!")
return
endif
set this = head.list_next
set exec_head = head
set exec_this = this
set exec_count = 0
call ForForce(bj_FORCE_PLAYER[0], function thistype.exec_destroyHeadNode)
set head.count = 0
set head.list_head = 0
call head.list_pop()
call head.deallocate()
endmethod
static method createHeadNode takes nothing returns thistype
local thistype this = allocate()
call this.list_insert(this)
set this.list_head = this
return this
endmethod
private static thistype isItemInList_return_this = 0
method isItemInList takes item d returns boolean
local thistype head = get(d)
local thistype that = this
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").isItemInList: Instance provided is null.")
return false
elseif this.list_head != this then
debug call BJDebugMsg("thistype(" + I2S(this) + ").isItemInList: Instance provided is not head of its list.")
return false
elseif d == null then
debug call BJDebugMsg("thistype.isItemInList: Parameter (item d) is null.")
return false
endif
set this = head.local_next
loop
exitwhen this == head or this.list_head == that
set this = this.local_next
endloop
if this == head and this.list_head != that then
set this = 0
endif
set isItemInList_return_this = this
return this != 0
endmethod
method addItem takes item d returns boolean
local thistype head = get(d)
local thistype that = head
local thistype last = 0
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addItem: Instance provided is null.")
return false
elseif this.list_head != this then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addItem: Instance provided is not head of its list.")
return false
elseif d == null then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addItem: Parameter (item d) is null.")
return false
elseif isItemInList(d) then
debug call BJDebugMsg("thistype(" + I2S(this) + ").addItem: Parameter (item d) is in the list of the provided instance.")
return false
endif
if head == 0 then
set head = create(d)
set that = head
call that.addToHead(this)
return true
endif
set that = that.local_next
loop
if that.list_head == 0 then
set last = that
exitwhen true
endif
exitwhen that == head
set that = that.local_next
endloop
if last != 0 then
call last.addToHead(this)
return true
endif
return false
endmethod
method removeItem takes item d returns boolean
if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeItem: Instance provided is null.")
return false
elseif this.list_head != this then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeIteme: Instance provided is not head of its list.")
return false
elseif d == null then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeItem: Parameter (item d) is null.")
return false
elseif not isItemInList(d) then
debug call BJDebugMsg("thistype(" + I2S(this) + ").removeItem: Parameter (item d) is not in the list of the provided instance.")
return false
endif
set this = isItemInList_return_this
call this.destroy()
return true
endmethod
endstruct
struct destgroup extends array
implement Alloc_MyPad
static if LIBRARY_TimerStruct then
/* Includes the CodeEx class. */
private static CodeEx array executor
else
/* Basically what CodeEx contains. */
private static trigger array trig
private static triggercondition array trig_cond
endif
private static integer trig_count = 0
private Dest headNode
private static thistype array forGroup_this
private static Dest array forGroup_iter
static method getEnumDestGroup takes nothing returns destgroup
return forGroup_this[trig_count]
endmethod
static method getEnumDest takes nothing returns destructable
return forGroup_iter[trig_count].destructable
endmethod
method getCount takes nothing returns integer
return headNode.count
endmethod
method first takes nothing returns destructable
return headNode.list_prev.destructable
endmethod
method last takes nothing returns destructable
return headNode.list_next.destructable
endmethod
method clear takes nothing returns nothing
call headNode.destroyHeadNode()
set headNode = Dest.createHeadNode()
endmethod
private static method exec_for takes nothing returns nothing
local integer i = 0
loop
exitwhen forGroup_iter[trig_count] == forGroup_this[trig_count].headNode or i >= 70
if GetDestructableTypeId(forGroup_iter[trig_count].destructable) == 0 then
call Dest.destroyObject(forGroup_iter[trig_count].destructable)
else
static if LIBRARY_TimerStruct then
call executor[trig_count].run()
else
call TriggerEvaluate(trig[trig_count])
endif
endif
set forGroup_iter[trig_count] = forGroup_iter[trig_count].list_next
set i = i + 1
endloop
if i >= 70 then
call ForForce(bj_FORCE_PLAYER[0], function destgroup.exec_for)
endif
endmethod
method for takes code c returns nothing
set trig_count = trig_count + 1
set forGroup_this[trig_count] = this
set forGroup_iter[trig_count] = headNode.list_next
static if LIBRARY_TimerStruct then
if executor[trig_count] == 0 then
set executor[trig_count] = CodeEx.create()
endif
set executor[trig_count].code = c
else
if trig[trig_count] == null then
set trig[trig_count] = CreateTrigger()
endif
if trig_cond[trig_count] != null then
call TriggerRemoveCondition(trig[trig_count], trig_cond[trig_count])
endif
set trig_cond[trig_count] = TriggerAddCondition(trig[trig_count], Condition(c))
endif
call exec_for()
set trig_count = trig_count - 1
set forGroup_this[trig_count + 1] = forGroup_this[trig_count]
set forGroup_iter[trig_count + 1] = forGroup_iter[trig_count]
endmethod
method getCountSafe takes nothing returns integer
call for(function DoNothing)
return headNode.count
endmethod
method destroy takes nothing returns nothing
debug if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ".destroy: Instance provided is 0.")
debug return
debug endif
call headNode.destroyHeadNode()
set headNode = 0
call deallocate()
endmethod
method isDestIn takes destructable d returns boolean
return headNode.isDestructableInList(d)
endmethod
method removeDest takes destructable d returns boolean
return headNode.removeDestructable(d)
endmethod
method addDest takes destructable d returns boolean
return headNode.addDestructable(d)
endmethod
private static destgroup enum_current = 0
private static rect enum_rect = null
readonly static destructable enum_dest = null
private static real center_x = 0.
private static real center_y = 0.
private static real radius = 0.
private static real dest_x = 0.
private static real dest_y = 0.
private static method isInCircle takes nothing returns boolean
set dest_x = GetDestructableX(GetFilterDestructable())
set dest_y = GetDestructableY(GetFilterDestructable())
return SquareRoot((dest_x - center_x)*(dest_x - center_x) + (dest_y - center_y)*(dest_y - center_y)) <= radius
endmethod
private static method onAddToFunc takes nothing returns nothing
static if ALLOW_DEBUG then
debug call BJDebugMsg("Adding: " + I2S(GetHandleId(GetEnumDestructable())))
endif
call enum_current.headNode.addDestructable(GetEnumDestructable())
endmethod
method enumInRect takes rect r, boolexpr cond returns nothing
call clear()
set enum_current = this
call EnumDestructablesInRect(r, cond, function destgroup.onAddToFunc)
endmethod
method enumInCircle takes real x, real y, real rad, boolexpr cond returns nothing
local boolexpr eval = null
local boolean isNull = cond == null
set eval = And(cond, Filter(function destgroup.isInCircle))
static if ALLOW_DEBUG then
debug call BJDebugMsg("Handle id of created boolean Expression is " + I2S(GetHandleId(eval)))
endif
if enum_rect == null then
set enum_rect = Rect(x - rad, y - rad, x + rad, y + rad)
else
call SetRect(enum_rect, x - rad, y - rad, x + rad, y + rad)
endif
set center_x = x
set center_y = y
set radius = rad
call enumInRect(enum_rect, eval)
call DestroyBoolExpr(eval)
set eval = null
endmethod
static method create takes nothing returns destgroup
local destgroup this = allocate()
set this.headNode = Dest.createHeadNode()
return this
endmethod
endstruct
struct itemgroup extends array
implement Alloc_MyPad
static if LIBRARY_TimerStruct then
/* Includes the CodeEx class. */
private static CodeEx array executor
else
/* Basically what CodeEx contains. */
private static trigger array trig
private static triggercondition array trig_cond
endif
private static integer trig_count = 0
private Item headNode
private static thistype array forGroup_this
private static Item array forGroup_iter
static method getEnumItemGroup takes nothing returns itemgroup
return forGroup_this[trig_count]
endmethod
static method getEnumItem takes nothing returns item
return forGroup_iter[trig_count].item
endmethod
method getCount takes nothing returns integer
return headNode.count
endmethod
method first takes nothing returns item
return headNode.list_prev.item
endmethod
method last takes nothing returns item
return headNode.list_next.item
endmethod
method clear takes nothing returns nothing
call headNode.destroyHeadNode()
set headNode = Item.createHeadNode()
endmethod
private static method exec_for takes nothing returns nothing
local integer i = 0
loop
exitwhen forGroup_iter[trig_count] == forGroup_this[trig_count].headNode or i >= 70
if GetItemTypeId(forGroup_iter[trig_count].item) == 0 then
call Item.destroyObject(forGroup_iter[trig_count].item)
else
static if LIBRARY_TimerStruct then
call executor[trig_count].run()
else
call TriggerEvaluate(trig[trig_count])
endif
endif
set forGroup_iter[trig_count] = forGroup_iter[trig_count].list_next
set i = i + 1
endloop
if i >= 70 then
call ForForce(bj_FORCE_PLAYER[0], function itemgroup.exec_for)
endif
endmethod
method for takes code c returns nothing
set trig_count = trig_count + 1
set forGroup_this[trig_count] = this
set forGroup_iter[trig_count] = headNode.list_next
static if LIBRARY_TimerStruct then
if executor[trig_count] == 0 then
set executor[trig_count] = CodeEx.create()
endif
set executor[trig_count].code = c
else
if trig[trig_count] == null then
set trig[trig_count] = CreateTrigger()
endif
if trig_cond[trig_count] != null then
call TriggerRemoveCondition(trig[trig_count], trig_cond[trig_count])
endif
set trig_cond[trig_count] = TriggerAddCondition(trig[trig_count], Condition(c))
endif
call exec_for()
set trig_count = trig_count - 1
set forGroup_this[trig_count + 1] = forGroup_this[trig_count]
set forGroup_iter[trig_count + 1] = forGroup_iter[trig_count]
endmethod
method getCountSafe takes nothing returns integer
call for(function DoNothing)
return headNode.count
endmethod
method destroy takes nothing returns nothing
debug if this == 0 then
debug call BJDebugMsg("thistype(" + I2S(this) + ".destroy: Instance provided is 0.")
debug return
debug endif
call headNode.destroyHeadNode()
set headNode = 0
call deallocate()
endmethod
method isItemIn takes item d returns boolean
return headNode.isItemInList(d)
endmethod
method removeItem takes item d returns boolean
return headNode.removeItem(d)
endmethod
method addItem takes item d returns boolean
return headNode.addItem(d)
endmethod
private static itemgroup enum_current = 0
private static rect enum_rect = null
readonly static item enum_dest = null
private static real center_x = 0.
private static real center_y = 0.
private static real radius = 0.
private static real dest_x = 0.
private static real dest_y = 0.
private static method isInCircle takes nothing returns boolean
set dest_x = GetItemX(GetFilterItem())
set dest_y = GetItemY(GetFilterItem())
return SquareRoot((dest_x - center_x)*(dest_x - center_x) + (dest_y - center_y)*(dest_y - center_y)) <= radius
endmethod
private static method onAddToFunc takes nothing returns nothing
static if ALLOW_DEBUG then
debug call BJDebugMsg("Adding: " + I2S(GetHandleId(GetEnumItem())))
endif
call enum_current.headNode.addItem(GetEnumItem())
endmethod
method enumInRect takes rect r, boolexpr cond returns nothing
call clear()
set enum_current = this
call EnumItemsInRect(r, cond, function itemgroup.onAddToFunc)
endmethod
method enumInCircle takes real x, real y, real rad, boolexpr cond returns nothing
local boolexpr eval = null
local boolean isNull = cond == null
set eval = And(cond, Filter(function itemgroup.isInCircle))
static if ALLOW_DEBUG then
debug call BJDebugMsg("Handle id of created boolean Expression is " + I2S(GetHandleId(eval)))
endif
if enum_rect == null then
set enum_rect = Rect(x - rad, y - rad, x + rad, y + rad)
else
call SetRect(enum_rect, x - rad, y - rad, x + rad, y + rad)
endif
set center_x = x
set center_y = y
set radius = rad
call enumInRect(enum_rect, eval)
call DestroyBoolExpr(eval)
set eval = null
endmethod
static method create takes nothing returns itemgroup
local itemgroup this = allocate()
set this.headNode = Item.createHeadNode()
return this
endmethod
endstruct
/*
* ____________________________________________________________________________
* | |
* | Additional Destructable Group API |
* | |
* | - Contains some of the API that the native type group already has. |
* | - Explanation of the functions are already defined above. |
* |____________________________________________________________________________|
*
*/
function DestroyDestGroup takes destgroup whichGroup returns nothing
call whichGroup.destroy()
endfunction
function CreateDestGroup takes nothing returns destgroup
return destgroup.create()
endfunction
function GroupEnumDestructablesInRect takes destgroup whichGroup, rect r, boolexpr filter returns nothing
call whichGroup.enumInRect(r, filter)
endfunction
function GroupEnumDestructablesInRange takes destgroup whichGroup, real x, real y, real radius, boolexpr filter returns nothing
call whichGroup.enumInCircle(x, y, radius, filter)
endfunction
function ForDestGroup takes destgroup whichGroup, code func returns nothing
call whichGroup.for(func)
endfunction
function GetEnumDestructableEx takes nothing returns destructable
return destgroup.getEnumDest()
endfunction
function GetEnumDestGroup takes nothing returns destgroup
return destgroup.getEnumDestGroup()
endfunction
function FirstOfDestGroup takes destgroup whichGroup returns destructable
return whichGroup.last()
endfunction
function LastOfDestGroup takes destgroup whichGroup returns destructable
return whichGroup.first()
endfunction
function CountDestInDestGroup takes destgroup whichGroup, boolean safe returns integer
if not safe then
return whichGroup.getCount()
else
return whichGroup.getCountSafe()
endif
endfunction
function GroupRemoveDestructable takes destgroup whichGroup, destructable dest returns boolean
return whichGroup.removeDest(dest)
endfunction
function GroupAddDestructable takes destgroup whichGroup, destructable dest returns boolean
return whichGroup.addDest(dest)
endfunction
function IsDestructableInGroup takes destructable dest, destgroup whichGroup returns boolean
return whichGroup.isDestIn(dest)
endfunction
/*
* ____________________________________________________________________________
* | |
* | Additional Item Group API |
* | |
* | - Contains some of the API that the native type group already has. |
* | - Explanation of the functions are already defined above. |
* |____________________________________________________________________________|
*
*/
function DestroyItemGroup takes itemgroup whichGroup returns nothing
call whichGroup.destroy()
endfunction
function CreateItemGroup takes nothing returns itemgroup
return itemgroup.create()
endfunction
function GroupEnumItemsInRect takes itemgroup whichGroup, rect r, boolexpr filter returns nothing
call whichGroup.enumInRect(r, filter)
endfunction
function GroupEnumItemsInRange takes itemgroup whichGroup, real x, real y, real radius, boolexpr filter returns nothing
call whichGroup.enumInCircle(x, y, radius, filter)
endfunction
function ForItemGroup takes itemgroup whichGroup, code func returns nothing
call whichGroup.for(func)
endfunction
function GetEnumItemEx takes nothing returns item
return itemgroup.getEnumItem()
endfunction
function GetEnumItemGroup takes nothing returns itemgroup
return itemgroup.getEnumItemGroup()
endfunction
function FirstOfItemGroup takes itemgroup whichGroup returns item
return whichGroup.last()
endfunction
function LastOfItemGroup takes itemgroup whichGroup returns item
return whichGroup.first()
endfunction
function CountItemsInItemGroup takes itemgroup whichGroup, boolean safe returns integer
if not safe then
return whichGroup.getCount()
else
return whichGroup.getCountSafe()
endif
endfunction
function GroupRemoveItem takes itemgroup whichGroup, item items returns boolean
return whichGroup.removeItem(items)
endfunction
function GroupAddItem takes itemgroup whichGroup, item items returns boolean
return whichGroup.addItem(items)
endfunction
function IsItemInGroup takes item items, itemgroup whichGroup returns boolean
return whichGroup.isItemIn(items)
endfunction
endlibrary
If you can see some faults with the system, feel free to report it to me. Any suggestions are also welcome.
UPDATE HIGHLIGHTS:
- Rewrote the library to accommodate item groups.
- Improved documentation.
- Removed certain functions due to rewrite. They may be added in future versions.
- Removed the demo map. (Will post a new one)
- Considered recursion in a ForGroup call.
VERSION:
v.1.01.1
Last edited: