• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Widget Utilities

Status
Not open for further replies.
I thought of writing up something that would allow destructable groups to be utilized for more than what Blizzard has given us access to. Here is what I've written:

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:

Changelogs:


-v.1.00.0

- Initial Release

-v.1.00.1

- Added GroupAdd and GroupRemove functions.
- DestructableGroup renamed to DestGroup
- Made destructable struct truly private .. no access now
- Reworked the ForGroup function to use a local trigger and triggeraction and clearing them upon conclusion.

-v. 1.00.2

- Added Documentation.
- Added More functions to make it behave like a group handle.
- Redone the demo map to add an example of its' application.

-v. 1.00.3

- Improved documentation (I think?)
- Oops, wrong library. Minor fix...

-v. 1.01.0
- Further reworked the ForGroup function.
- Now uses a trigger evaluation instead of a trigger execution.
- DestGroup renamed to destgroup.
- Destructable struct renamed to Dest.
- Further improved documentation.
- Now uses Table instead of Hashtable (original library)
- Now uses AllocationAndLinks instead of Alloc (Sevion's library)
- Added a new struct: itemgroup.
 
Last edited:
Sorry for the late reply

Arraying them works as well. not that handy as groups, but your solution doesnt fix that too.

I don't quite get what you mean by this.

Any idea how to apply it as a project?

Here are some examples:

I thought of using some sort of tree regrowth system as one of its' applications.
Another application would be creating destructables and registering them to a group. It would be difficult to filter out those trees manually, so the destructable group would be used to loop over them.

I wonder if I should change the code so that it will become a trigger evaluation instead of a trigger execution. This will make it a bit faster too.

And also, @DracoL1ch, can you test out the code if it recycles the instances quite well? (private struct Destructable)
 
Last edited:
Level 8
Joined
Jan 23, 2015
Messages
121
I'm sure you better be using much more popular versions of your optional requirements: Table by Bribe and Alloc by Sevion. A list module (yours named Push just sounds wrong to me) if needed can be taken from Nestharus's GitHub repository. I think it's surely better if you will use them instead of reinventing wheels - people managed to invent most of the basic things the best way long ago before. Also/else I recommend properly document that requirements and leave credits on who's snippets you're using or at least where to get them.
Also it looks like you're not a follower of JPAG. Check it and then fix your code with standard we're all using.

Didn't check the system actually, cause it's really rare thing to use and I'm not interested in it at all right now. The only cases I managed to imagine is likely a script rather than mechanics or even a system. Still the idea seems good.
Nevertheless, your code needs perfection.
 
Last edited:
Level 8
Joined
Jan 23, 2015
Messages
121
Should I optimize the code or something else?
The usage of libraries instead of your own code is already an optimisation. I didn't study it to say whether your algorithms needs optimisation by themselves.
Perfection means not only speed and no bugs but also readability, clearness and standardized format, for other people that will work with it.
Also your commentaries should be reconsidered because some of them tells pretty obvious things while I believe there's still other things understandable much less yet not mentioned in comments.
 
The usage of libraries instead of your own code is already an optimisation. I didn't study it to say whether your algorithms needs optimisation by themselves.
Perfection means not only speed and no bugs but also readability, clearness and standardized format, for other people that will work with it.
Also your commentaries should be reconsidered because some of them tells pretty obvious things while I believe there's still other things understandable much less yet not mentioned in comments.

Ah, I see now what you mean. I'll really update it now.
 
Level 8
Joined
Jan 23, 2015
Messages
121
I'll try to include those as optional libraries later
I believe they're so popular that anyone who would use your system will already have them included in their project, so I think you don't need to make them optional. It will shorten your system's code pretty much, leaving only your code that only do the thing the system is supposed to do, increasing both effectivness and readability.
If case they won't have them, still I think it's even better to force them to use that requirements, because they are good, nice and clear. Table is the most used system, probably always used when it comes to some big data storage. Alloc isn't used that much, but is used almost everywhere where people use extends array thing.
So I insist you get rid both of word optional and of your own replacements for that optional requirements.
 
Necropost!

Intent: Update on library!


latest
:"tremble before the necropost!"
 
Level 14
Joined
Jan 16, 2009
Messages
716
This is my notes on this after a quick look (this is not a review, just some opinions and suggestions from someone who use something similar in my map) :
  • This should be two libraries and not one (Items and Destructables)
  • AllocationAndLinks : should be optional
  • Table : should be optional (not forcing any libraries is always better even though Table is a must have)
  • ALLOW_DEBUG should be called MORE_DEBUG or DETAILLED_DEBUG
  • What happen if a destructable/item get removed ? Does it stays in the group ? Does it get removed form the group ? Basically do you handle ghost widgets ?
 
This should be two libraries and not one (Items and Destructables)

I thought of bundling them together into one since the API for the newly created item group is going to be basically the same as destructible group.

  • AllocationAndLinks : should be optional
  • Table : should be optional (not forcing any libraries is always better even though Table is a must have)

Try as I might, I wouldn't be able to do away with making every part of the library optional without sacrificing modularity or readability.

I could, however, revert the allocation scheme to not extending the array.

As for Table, I could perhaps make it optional, since what is really required is a huge number of instances, which a hash table is more than capable of offering.

  • ALLOW_DEBUG should be called MORE_DEBUG or DETAILLED_DEBUG
  • What happen if a destructable/item get removed ? Does it stays in the group ? Does it get removed form the group ? Basically do you handle ghost widgets ?

I could consider changing ALLOW_DEBUG.

As with the ghost handles, the system can only catch them in a For Group call.

All in all, the new library is virtually inspired by the group and corresponding natives.

EDIT:
- Switched the logic gate of the safety mechanism in the function Count$Widget$In$Widget$Group. Done!
- Considered recursion when using For$Widget$Group.
 
Last edited:
Status
Not open for further replies.
Top