• 🏆 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!

[Snippet] HandleStack

Please check the script header for description, how to use, requirements, changelogs and more!

JASS:
// --------------------------------------------------------------------------------------------------------------------
//  
//  HandleStack
//  ===========
// 
// 	Version:	1.4.0
// 	Author:		Anachron
// 
// 	Requirements:
// 		Stack [by Anachron] (v. 1.3.0)
// 		(New) Table [by Bribe] (v. 3.1) [url]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/[/url]
// 
// 	Description:
// 		HandleStack is a handletype based stack collection.
//      You can easily create unitgroups, playergroups, effectgroups
//      and basically group and handletype together.
//      Instead of returning ids of the handles in stack, you get 
//      the real handle that is saved.
// 
// 	History:
// 		1.0.0: Initial Release
//      1.1.0: Added missing clear method
//      1.2.0: Adopt API changes of new Stack
//      1.3.0: Added switching of handles
//      1.4.0: Added sorting of handles
// 
// 	API:
// 		Exactly the same than Stack, but returns handletypes instead
// 
// ---------------------------------------------------------------------------------------------------------------------------
library HandleStack requires Stack

    //! textmacro CreateHandleStack takes NAME, TYPE
    private function interface $NAME$SortFunc takes $NAME$Stack stack, $TYPE$ toAdd, $TYPE$ toCheck, boolean asc returns boolean defaults false
    
    struct $NAME$Stack
        private delegate    Stack   Index   = 0
        private             Table   $NAME$s = 0
        
        public static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            
            set .Index = Stack.create()
            set .$NAME$s = Table.create()
            
            return this
        endmethod
        
        public method add takes $TYPE$ theHandle returns boolean
            local integer id = GetHandleId(theHandle)
            local boolean added = .Index.add(id)
            if added then
                set .$NAME$s.$TYPE$[id] = theHandle
            endif
            return added
        endmethod
        
        public method delete takes $TYPE$ theHandle returns boolean
            local integer id = GetHandleId(theHandle)
            local boolean deleted = .Index.delete(id)
            if deleted then
                call .$NAME$s.remove(id)
            endif
            return deleted
        endmethod
        
        public method clear takes nothing returns nothing
            call .Index.clear()
            call .$NAME$s.flush()
        endmethod
        
        public method get takes integer id returns $TYPE$
            if .Index.has(id) then
                return .$NAME$s.$TYPE$[id]
            endif
            return null
        endmethod
        
        public method getNext takes nothing returns $TYPE$
            return .get(.Index.getNext())
        endmethod
        
        public method getPrev takes nothing returns $TYPE$
            return .get(.Index.getPrev())
        endmethod
        
        public method getFirst takes nothing returns $TYPE$
            return .get(.Index.getFirst())
        endmethod
        
        public method getLast takes nothing returns $TYPE$
            return .get(.Index.getLast())
        endmethod
        
        public method random takes nothing returns $TYPE$
            return .get(.Index.random())
        endmethod
        
        public method switch takes $TYPE$ first, $TYPE$ second returns boolean
            return .Index.switch(GetHandleId(first), GetHandleId(second))
        endmethod
        
        public method sort takes $NAME$SortFunc sortFunc, boolean asc returns nothing
            local integer out = -1
            local integer ins = -1
            local $TYPE$ outVal = null
            local $TYPE$ insVal = null
            debug local boolean print = .print
        
            debug set .print = false
            
            set out = 0
            set ins = out +1
            set outVal = .get(out)
            set insVal = .get(ins)
            loop
                exitwhen out >= .Index.count
                
                if sortFunc.evaluate(this, insVal, outVal, asc) then
                    call .switch(insVal, outVal)
                    debug if .print then
                        debug set STACK_MSG = STACK_COLOR + "Stack|r[" + STACK_COLOR+ I2S(this) + "|r]: "
                        debug set STACK_MSG = STACK_MSG + "Replaced " + STACK_COLOR + I2S(GetHandleId(outVal)) + "|r "
                        debug set STACK_MSG = STACK_MSG + "with " + STACK_COLOR + I2S(GetHandleId(insVal)) + "|r"
                        debug call BJDebugMsg(STACK_MSG)
                    debug endif
                endif
                set ins = ins +1
                
                if ins >= .Index.count then
                    set out = out +1
                    set ins = out +1
                endif
                
                set insVal = .get(ins)
                set outVal = .get(out)
            endloop
            
            set insVal = null
            set outVal = null
            
            debug set .print = print
        endmethod
        
        public method destroy takes nothing returns nothing
            call .Index.destroy()
            call .$NAME$s.destroy()
        endmethod
    endstruct
    //! endtextmacro
    
endlibrary

Example
JASS:
    //! runtextmacro CreateHandleStack("Unit", "unit")

    private function testHandleStack takes nothing returns nothing
        local UnitStack myGroup = UnitStack.create()
        
        call myGroup.add(CreateUnit(Player(0), 'hpea', 0., 0., 0.))
        call myGroup.add(CreateUnit(Player(0), 'hpea', 0., 0., 0.))
        call myGroup.add(CreateUnit(Player(0), 'hpea', 0., 0., 0.))
        call myGroup.add(CreateUnit(Player(0), 'hpea', 0., 0., 0.))
        call myGroup.add(CreateUnit(Player(0), 'hpea', 0., 0., 0.))
        
        call myGroup.reset()
        loop
            exitwhen not myGroup.hasNext()
            call SetUnitState(myGroup.getNext(), UNIT_STATE_LIFE, 123.456)
        endloop
    endfunction
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
It will be much faster than a ForGroup, because for each unit one thread is created, and it's quite costly in jass.
It will be much slower for all other operations.
Now it will be much slower than a FirstOfGroup/GroupRemoveUnit loop.

There is already UnitLL for units, it handles ghost units.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
You mean more features for units or because it's generic (other handles), that's not clear for me.
For now, i just assumed you said that yours is better for units, in some cases at least.
That's why i ask you to elaborate because i think the opposite.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Mine is better if you want to use other handle types too or need some advanced functionality that only Stack provides.

I'm not trying to be an harsh, but again what Stack provides which UnitLL doesn't (for units only ofc).

Now, to talk about the features, wouldn't be better to run textmacros in the library itself for all useful handle types, such as item, destructable, whatever.
 
This is a part of my API that you do not have in any type of you code
JASS:
//      each(EachFunc x)        --> Execute this function for each value in stack
//      max[= x]                --> Get/Set maximum amount of elements in stack
//      merge(Stack x)          --> Merge other stack into current stack
//      split(Stack x, int y)   --> Split this stack with elements after y into the new stack
//      switch(int x, int y)    --> Switch indexes for value x and y
//      sort(bool asc)          --> Sort Stack ascend or descending

But since this is just advanced stuff and I already told you your resource fits the complete group usage, I can only agree that yours may be better for unit groups.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
// each(EachFunc x) --> Execute this function for each value in stack

No need for this, this is just better to loop through the list.

// max[= x] --> Get/Set maximum amount of elements in stack

I have .count, but yes you can't set it, i could add it, but well i had 0 features feedback, and since i didn't need it myself ...

// merge(Stack x) --> Merge other stack into current stack

.addUnitLL


// split(Stack x, int y) --> Split this stack with elements after y into the new stack

Yes i don't have it but i think you can do it yourself easy enough.

// switch(int x, int y) --> Switch indexes for value x and y
// sort(bool asc) --> Sort Stack ascend or descending

And how it works for units ?

Now, i think i will stop the hijack.

Don't forget my suggestion about textmacros.
 
  1. Will generate way too much code, unless the macro is only supposed to be ran once?
One time per handle type (unit, effect, player, whatever).

  1. The macro has to be placed inside a scope or library or else it won't work.
Yeah, is this a bad thing? I could change it to adopt into a struct, is that a better thing?
 
Yes you need textmacros since it's not limited to units but you can create a Stack for all kinds of handletypes...

Why do you make the user run the macros themselves? It could easily lead to collisions in multiple systems and it's just plain ugly.

Wouldn't it be better to have everything contained?

Kinda like this.
 
Actually I thought about that too, but then again, do you know which handletypes the user needs collections of? Let him choose what he needs. Units, items, destructables, whatever is on his/her mind.

Exactly, place the practical ones in there yourself and let users create ones they need.

By default I think it should support
  1. groups
  2. units
  3. items
  4. widgets
  5. handles
Also, do you know if this has any speed benefit over the native groups? If not then this may be kinda useless.
 
Exactly, place the practical ones in there yourself and let users create ones they need.

By default I think it should support
  1. groups
  2. units
  3. items
  4. widgets
  5. handles
Okay, will do.

Also, do you know if this has any speed benefit over the native groups? If not then this may be kinda useless.
Better API, more functions, less confusion.
 
Level 11
Joined
Dec 3, 2011
Messages
366
I think you should list all handle in your system.
JASS:
//! runtextmacro CreateHandleStack("Player", "player")
//! runtextmacro CreateHandleStack("Widget", "widget")
//! runtextmacro CreateHandleStack("Destructable", "destructable")
//! runtextmacro CreateHandleStack("Item", "item")
//! runtextmacro CreateHandleStack("Unit", "unit")
//! runtextmacro CreateHandleStack("Ability", "ability")
//! runtextmacro CreateHandleStack("Timer", "timer")
//! runtextmacro CreateHandleStack("Trigger", "trigger")
//! runtextmacro CreateHandleStack("TriggerCondition", "triggercondition")
//! runtextmacro CreateHandleStack("TriggerAction", "triggeraction")
//! runtextmacro CreateHandleStack("TriggerEvent", "event")
//! runtextmacro CreateHandleStack("Force", "force")
//! runtextmacro CreateHandleStack("Group", "group")
//! runtextmacro CreateHandleStack("Location", "location")
//! runtextmacro CreateHandleStack("Rect", "rect")
//! runtextmacro CreateHandleStack("BooleanExpr", "boolexpr")
//! runtextmacro CreateHandleStack("Sound", "sound")
//! runtextmacro CreateHandleStack("Effect", "effect")
//! runtextmacro CreateHandleStack("UnitPool", "unitpool")
//! runtextmacro CreateHandleStack("ItemPool", "itempool")
//! runtextmacro CreateHandleStack("Quest", "quest")
//! runtextmacro CreateHandleStack("QuestItem", "questitem")
//! runtextmacro CreateHandleStack("DefeatCondition", "defeatcondition")
//! runtextmacro CreateHandleStack("TimerDialog", "timerdialog")
//! runtextmacro CreateHandleStack("Leaderboard", "leaderboard")
//! runtextmacro CreateHandleStack("Multiboard", "multiboard")
//! runtextmacro CreateHandleStack("MultiboardItem", "multiboarditem")
//! runtextmacro CreateHandleStack("Trackable", "trackable")
//! runtextmacro CreateHandleStack("Dialog", "dialog")
//! runtextmacro CreateHandleStack("Button", "button")
//! runtextmacro CreateHandleStack("TextTag", "texttag")
//! runtextmacro CreateHandleStack("Lightning", "lightning")
//! runtextmacro CreateHandleStack("Image", "image")
//! runtextmacro CreateHandleStack("Ubersplat", "ubersplat")
//! runtextmacro CreateHandleStack("Region", "region")
//! runtextmacro CreateHandleStack("FogState", "fogstate")
//! runtextmacro CreateHandleStack("FogModifier", "fogmodifier")
//! runtextmacro CreateHandleStack("Hashtable", "hashtable")

It will be better.

P/s: Im using your Stack and Handle Stack
 
Top