• 🏆 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] OrderQueueLite

Level 7
Joined
Jan 28, 2012
Messages
266
OrderQueueLite, is a simple system that replicates the functionality of shift orders, with the advantage that it is uninteruptable, and you can issue them by triggers.

Why Lite? I am making another version that will use a linked list, enable you to change the order, and have a memory of previous Orders(the size of the memory will be user definable.)
JASS:
library OrderQueueLite /* v 1.0.0.5
**************************************************************
*       ___            _____                                 *
*      |   \          | ____|                                *
*      |   /  \\  / / | |__    _   _   __       _    _   _   *
*      |   \   \\/ /  |  __|  |  \| | |   \   /   \ | |/ /   *
*      |___/   / /    | |___  | \ \ | |    | | |__/ |  /     *
*             / /     |_____| |_|\ \| |___/   \__\  |_|      *
*                                                            *
**************************************************************
*           */ uses /*
*           */ CTL /* hiveworkshop.com/forums/jass-resources-412/snippet-constant-timer-loop-32-a-201381/
*           */ Table /* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/?highlight=Table
*           */ UnitIndexer /* hiveworkshop.com/forums/jass-resources-412/system-unit-indexer-172090/
*           * Order Id Repo * hiveworkshop.com/forums/jass-resources-412/repo-order-ids-197002/
**************************************************************
*
* Credits- Almia for giving some useful suggestions.
*
**************************************************************
*
*             what is OrderQueueLite?
*               - it is a system that copies shift order functionallity
*               - Unlike OrderQueue(not released yet) it can not
*                   insert new orders in the units order chain.
*
***************************************************************
*
*       struct OrderQueue extends array
*
*           readonly integer last
*           readonly integer current
*
*           static method create takes unit u returns thistype
*               - a unit will only ever have one unit queue
*               - order queues are destroyed automatically
*                   so call create whenever you want to 
*                    add new orders to an OrderQueue
*
*           static method isValidOrder takes integer i returns boolean
*               - tells you whether i is valid Order for a Queue.
*
*          method getOrderId takes integer i returns integer
*              - gives you the orderId of a specific order
*
*          method getOrderType takes integer i returns integer
*              - tells you what Type of order is in that slot
*
*           method destroy takes nothing returns nothing
*               -destroys the OrderQueue
*
*           method pop takes nothing returns nothing
*               removes the Current Order the Unit is doing
*                  and orders them to do the next
*
*
*
*           method issuePointOrder takes integer order, real x, real y returns nothing
*           method issueTargetOrder takes integer order, widget target returns nothing
*           method issueImmediateOrder takes integer order returns nothing
*           method issueItemPointOrder takes item toUse, real x, real y returns nothing
*           method issueItemTargetOrder takes item toUse, widget target returns nothing
*           method issueItemOrder takes item toUse returns nothing
*           
***************************************************************/



    private module OrderQueueInit
        static method onInit takes nothing returns nothing
            set tg = TimerGroup32.create(function thistype.periodic)
            call TriggerRegisterAnyUnitEventBJ(stopOrder, EVENT_PLAYER_UNIT_ISSUED_ORDER)
            call TriggerRegisterAnyUnitEventBJ(stopOrder, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
            call TriggerRegisterAnyUnitEventBJ(stopOrder, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
            call TriggerAddCondition(stopOrder, Condition(function thistype.stopOrders))
        endmethod
    endmodule

    struct OrderQueue extends array
        private static constant integer Point = 0
        private static constant integer Target = 1
        private static constant integer Immediate = 2
        private static constant integer ItemPoint = 3
        private static constant integer ItemTarget = 4
        private static constant integer Item = 5

        private static constant integer totalC = 4
        private static TimerGroup32 tg
        static trigger stopOrder = CreateTrigger()
        private Table list
        
        private integer count
        private integer recycle
        readonly integer current
        readonly integer last
        boolean active
        boolean paused

        thistype nex
        thistype pre
        
        method operator canOrder takes nothing returns boolean
            return (not paused) and (GetUnitCurrentOrder(GetUnitById(this))==0)
        endmethod

        private method getRecycle takes integer i returns integer
            return list[i+2]
        endmethod
        private method setRecycle takes integer i, integer toWhat returns nothing
            set list[i+2]=toWhat
        endmethod
        private method next takes integer i returns integer
            return list[i]
        endmethod
        private method setNext takes integer i, integer a returns nothing
            set list[i]=a
        endmethod
        
        static method isValidOrder takes integer i returns boolean
            return (R2I(I2R(i)/I2R(totalC)) == i/totalC)
        endmethod
        
        method getOrderId takes integer i returns integer
            debug if not isValidOrder(i) then
                debug call BJDebugMsg(I2S(i) + " Is an invalid order")
                debug return 1/0
            debug endif
            return list[i+1]
        endmethod
        
        method getOrderType takes integer i returns integer
            debug if not isValidOrder(i) then
                debug call BJDebugMsg(I2S(i) + " Is an invalid order")
                debug return 1/0
            debug endif
            return list[i+3]
        endmethod

        method pause takes nothing returns nothing
            local integer i = 0
            set paused = true
  
            if current != 0 then
                call setNext(0,current)
                set current = 0
            endif
        endmethod

        method unpause takes nothing returns nothing
            set paused = false
        endmethod

        static method create takes unit u returns thistype
            local thistype this = GetUnitUserData(u)

            if active then
                return this
            endif
            if .nex == 0 then
                call tg.start()
            endif
            set paused = false
            call UnitIndex(this).lock()
            set list = Table.create()
            set active = true
            set .nex = 0
            set .pre = thistype(0).pre
            set thistype(0).pre = this
            set .pre.nex = this
            return this
        endmethod
        
        method destroy takes nothing returns nothing
            if not active then
                return
            endif
            if thistype(0).nex == this and thistype(0).pre == this then
                call tg.stop()
            endif

            set active = false
            call UnitIndex(this).unlock()
            call list.destroy()
            set .current = 0
            set .last = 0
            set .pre.nex = .nex
            set .nex.pre = .pre
        endmethod

        method pop takes nothing returns nothing
            local integer i = current
            local integer n = next(i)
            local integer b = totalC-1
            local integer s = list[n+3]

            set current = n

            if i != 0 then
                call setRecycle(i,recycle)
                set recycle = i
                call list.handle.remove(i)
                call list.handle.remove(i+1)
            endif
            
            if s > Immediate then
                if s < Item then
                    if s == ItemPoint then
                        call UnitUseItemPoint(GetUnitById(this),list.item[n+1],list.real[n],list.real[n+1])
                        return
                    endif
                    call UnitUseItemTarget(GetUnitById(this),list.item[n+1],list.widget[n])
                    return
                endif
                call UnitUseItem(GetUnitById(this),list.item[n+1])
                return
            else
                if s < Immediate then
                    if s == Point then
                        call IssuePointOrderById(GetUnitById(this),list[n+1],list.real[n],list.real[n+1])
                        return
                    endif
                    call IssueTargetOrderById(GetUnitById(this),list[n+1],list.widget[n])
                    return
                endif
                call IssueImmediateOrderById(GetUnitById(this),list[n+1])
            endif
        endmethod
        
        private method push takes nothing returns integer
            local integer i = recycle
            if i == 0 then
                set i = count+totalC
                set count = i
            else
                set recycle = getRecycle(i)
            endif
            call setNext(last,i)
            set last = i
            return i
        endmethod
        
        static method periodic takes nothing returns boolean
            local thistype this = thistype(0).nex

            call DisableTrigger(stopOrder)

            loop
                exitwhen this ==0
                if .canOrder then
                    if current == last then
                        call destroy()
                    else
                        call pop()
                    endif
                endif
                set this = .nex
            endloop

            call EnableTrigger(stopOrder)
            return false
        endmethod
        
        method issuePointOrderById takes integer order, real x, real y returns nothing
            local integer i = push()
            set list.real[i] = x
            set list.real[i + 1]= y
            set list[i + 1] = order
            set list[i + 3] = Point
        endmethod
        method issueImmediateOrderById takes integer order returns nothing    
            local integer i = push()
            set list[i + 1] = order
            set list[i + 3] = Immediate
        endmethod
        method issueTargetOrderById takes integer order, widget target returns nothing
            local integer i= push()
            set list.widget[i] = target
            set list[i + 1] = order
            set list[i + 3] = Target
        endmethod
        method issueItemPointOrder takes item toUse, real x, real y returns nothing
            local integer i = push()
            set list.real[i] = x
            set list.real[i + 1] = y
            set list.item[i + 1] = toUse
            set list[i + 3] = ItemPoint
        endmethod
        method issueItemOrder takes item toUse returns nothing    
            local integer i = push()
            set list.item[i + 1] = toUse
            set list[i + 3] = Item
        endmethod
        method issueItemTargetOrder takes item toUse, widget target returns nothing
            local integer i= push()
            set list.widget[i]= target
            set list.item[i + 1] = toUse
            set list[i + 3] = ItemTarget
        endmethod
        
        private static method stopOrders takes nothing returns boolean
            local thistype this =GetUnitUserData(GetTriggerUnit())
            local integer n = .current
            local integer s

            if n != 0 then
                call DisableTrigger(stopOrder)
                set s = list[n+3]
                if s > Immediate then
                    if s < Item then
                        if s == ItemPoint then
                            call UnitUseItemPoint(GetUnitById(this),list.item[n+1],list.real[n],list.real[n+1])
                        else
                            call UnitUseItemTarget(GetUnitById(this),list.item[n+1],list.widget[n])
                        endif
                    else
                        call UnitUseItem(GetUnitById(this),list.item[n+1])
                    endif
                else
                    if s < Immediate then
                        if s == Point then
                            call IssuePointOrderById(GetUnitById(this),list[n+1],list.real[n],list.real[n+1])
                        else
                            call IssueTargetOrderById(GetUnitById(this),list[n+1],list.widget[n])
                        endif
                    else
                        call IssueImmediateOrderById(GetUnitById(this),list[n+1])
                    endif
                endif
                call EnableTrigger(stopOrder)
            endif
            return false
        endmethod

        private method deindex takes nothing returns nothing
            call this.destroy()
        endmethod
        
        implement UnitIndexStruct
        implement OrderQueueInit
    endstruct
endlibrary

Uploaded
Fixed spacing
-fixed a bug involving the binary tree
-added pause and unpause
Fixedd a minor bug with issueTargetOrder
Made Sys RegisterAnyUnitEvent
added a link to an awsome Repository by Nestherus
Removed some changes from previous version

JASS:
scope TestOrderQueue initializer init
    globals
        private integer count = 21
        private OrderQueue pauseThis
    endglobals
    private function wait takes nothing returns nothing
        call pauseThis.unpause()
        call DestroyTimer(GetExpiredTimer())
    endfunction
    private function createLots takes nothing returns nothing
        local unit u  = CreateUnit(Player(0),'hfoo',0,0,0)
        local OrderQueue this = OrderQueue.create(u)
        local integer i = 10

        loop
            call this.issuePointOrderById(851986 , 700, -700)
            call this.issuePointOrderById(851986 , 700, 700)
            call this.issuePointOrderById(851986 , -700, 700)
            call this.issuePointOrderById(851986 , -700, -700)
            call this.issuePointOrderById(851986 , 700, -700)
            set i =i-1
            exitwhen i == 0
        endloop

        set u = null
        set count = count-1
        if count == 0 then
            set pauseThis = this
            call this.pause()
            call TimerStart(CreateTimer(),1,false,function wait)
            call DestroyTimer(GetExpiredTimer())
        endif
    endfunction
    
    private function init takes nothing returns nothing
        call createLots()
        call TimerStart(CreateTimer(),1,true,function createLots)
    endfunction
endscope
 
Last edited:
I think empty would be a better name for the hasNoOrder method operator because empty refers to the queue directly while hasNoOrder refers to the unit :eek:

This resource looks cool. I coded something like this a long time ago for one of my maps. It was pretty lame though :p

The spacing is a bit inconsistent though.
Usually, I'd recommend an empty line between consecutive methods unless they're a long chain of method operators, getters and setters.
I'd recommend this kind of spacing:
set variable = value
call Function(param, param, param)
set myInteger = 1 + 5/6*7 - 4*3 + 2/1

- and + get spaces before and after while * and / don't.

Also, are the strings really necessary? :eek:
Wouldn't some constant integers accomplish the same thing?

One last thing concerning your list iteration. I think the exitwhen this == 0 line should be the first line to appear in the loop because it gives correct behavior for an empty list.
 
Level 7
Joined
Jan 28, 2012
Messages
266
I could use a flag to enable it to use IssueAnyOrder
or i could do
JASS:
call DisableTrigger(so1)
call DisableTrigger(so2)
call DisableTrigger(so3)
/*
*Where  triggers so1-3 are set using GetPlayerUnitEventTrigger
* The problem with this is it would mess up other UnitOrderEvents
*/
 
You should probably be using GetUnitId instead of GetUnitUserData (it will inline anyways). It could cause issues in certain rare cases.

For example using the UnitIndexer compatibility library with UnitDex's hashtable option set to true.

vJass also supports 2D-arrays so you don't have to do things like [i + 3].

The code looks good but I'll need to test it before approving.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I have to test if this is working as intended. I think there is no chance that the author
does any further updates, if something is not working.

What I miss is an internal optional cooldown between too orders. Currently you order
the next order once the units current order is 0 and the instance is not paused, with an inaccuracy up to of 0.0315
Nicer would be ( order 1, cooldown 5 seconds ) --> 5 seconds later + condition ( order == 0 ) fire next order. --> run optional cooldown

Edit: Ok I just saw you can pause an order queue from outside via boolean.
Bt it seems hard to determine, when to pause an unit. Like I said an internal cooldown would be a win.

In my opinion not only order 0 but also smart and attack ( don' know the ids by heart ) are also
valid to fire a new order from the queue
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
This could definitely use OrderEvent (the function RegisterAnyOrderEvent is useful here). Instead of using disable/enable trigger, just set a boolean and don't switch into the if/else block if the boolean is flagged.

Also: return (R2I(I2R(i)/I2R(totalC)) == i/totalC)

That's exactly the same as "return i/totalC == i/totalC" and thus "return true". The only time you use I2R (or int + 0.00) in division is to avoid truncating. But then you... truncate. I think you may be looking for ModuloInteger here, but it is hard to tell.
 
No changes were made since the last discussions in thread.

@Bribe, do you remember testing the code? If it's just tiny critiques we might eventualy fix them ourselves and update the main post.
But if Ender doesn't care anymore we might also graveyard it again, as it seems he didn't make wanted updates after he posted above.

edit: as no changed were made and author is inactive I won't review it again, but move it to graveyard again.
 
Last edited:
Top