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

[vJASS] Order Matrix

This is a basic library that allows one to get the data from any issued order as long as it persists.

JASS:
library OrderMatrix /*

    */ requires /*

         ---------------------------
        |   Legend:                 |
        |    - #     -> Requirement |
        |    - #?    -> Optional    |
         ---------------------------


        --------------------------------------
        */ Table                            /*
        --------------------------------------   
            -> Bribe
         
            link: https://www.hiveworkshop.com/threads/snippet-new-table.188084/#post-1835068
     
        --------------------------------------
        */  UnitDex                         /*
        --------------------------------------
            -> TriggerHappy
         
            link: https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
     
        --------------------------------------
        */ optional RegisterPlayerUnitEvent /*
        --------------------------------------
            # RegisterNativeEvent
         
            -> Bannar
         
            link: https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
                     
        Table                       -> Simplifies storage by a mile.
        UnitDex                     -> Allows the system to use GetUnitId instead of GetHandleId
        RegisterPlayerUnitEvent     -> Not really required, but helpful anyway.
     
         -----------------------
        |   OrderMatrix         |
        |   - MyPad             |
        |                       |
        |   Version: 1.4.4      |
         -----------------------
       
        -----------------------------------------------------------
        Description:
     
            OrderMatrix is a library that automatically
            maps out the relevant information of issued orders.
         
            It also stores the previous 5 orders of each unit
            for convenience, although the number is configurable.
        -----------------------------------------------------------
     
        -----------------------------------------------------------
        struct StoreOrders extends array
     
        Members:
         
             -------------------------------------------------------
            |
            |   @configurable static constant integer MAX_ORDERS
            |       (private)
            |
            |       The maximum amount of previous orders to be
            |       stored. You can change this to any positive
            |       value.
            |
            |   integer relevantNode (private)
            |       The node in question when calling
            |       OrderMatrix[unit].past[index].
            |
            |       Made an instance member so as not to cause any
            |       conflicts.
            |
            |   integer lastIndex (private)
            |       The previously stored member of the operator
            |       [].
            |
            |       Made an instance member so as not to cause any
            |       conflicts.
            |
            |   TableArray dataArray (private)
            |       The main storage of data. Note that this is
            |       a per-unit instance.
            |
             -------------------------------------------------------
     
        Methods:
         
             -------------------------------------------------------
            |
            |   method operator [integer index] -> thistype
            |       Stores the relevantNode and lastIndex
            |       and assigns a new value to relevantNode
            |       if the lastIndex is different from the
            |       previous value
            |
            |   method getTargetX() -> real
            |       -> the x-coordinate of past issued order.
            |       The depth of past order depends on the parameter
            |       of the operator [].
            |
            |   method getTargetY() -> real
            |       -> the y-coordinate of past issued order.
            |       The depth of past order depends on the parameter
            |       of the operator [].
            |
            |   method getTarget() -> widget
            |       -> the past targeted widget.
            |       (operator [] dependent)
            |
            |   method getTargetUnit() -> unit
            |       -> the past targeted unit.
            |       (operator [] dependent)
            |
            |   method getTargetDest() -> destructable
            |       -> the past targeted destructable.
            |       (operator [] dependent)
            |
            |   method getTargetItem() -> item
            |       -> the past targeted item.
            |       (operator [] dependent)
            |
            |   method getOrderId() -> integer
            |       -> the past order..
            |       (operator [] dependent)
            |
            |   method getOrderCount() -> integer
            |       -> the amount of orders issued.
            |        Maximum is MAX_ORDERS
            |
            |   method getOrderType() -> integer
            |       -> the order type of the past order
            |       (Either Immediate, Target or Point)
            |       (operator [] dependent)
            |
             -------------------------------------------------------
         
     
        struct OrderMatrix extends array
     
        Members:
     
             -------------------------------------------------------     
            |
            |   static constant integer INFO_SIZE (private)
            |       The amount of space used by the next member.
            |
            |   static TableArray orderData (private)
            |       Stores relevant information, from the x and y
            |       to the target type.
            |
            |   static unit lastUnit (private)
            |       The unit that was passed in the operator []
            |
             -------------------------------------------------------
             
        Methods:
         
             -------------------------------------------------------
            |
            |   static method operator [] (unit u) -> OrderMatrix
            |       Returns the unit id of the unit typecasted
            |       to OrderMatrix.
            |
            |   method operator past() -> StoreOrders
            |       Returns the same instance but typecasted.
            |
            |   method operator [] (integer index) -> StoreOrders
            |       Another way of writing OrderMatrix[unit].past
            |
            |   method getTargetX() -> real
            |       -> the x-coordinate of last issued order.
            |       This may not work with auto-acquire events.
            |
            |   method getTargetY() -> real
            |       -> the y-coordinate of last issued order.
            |       This may not work with auto-acquire events.
            |
            |   method getTarget() -> widget
            |       -> the targeted widget.
            |
            |   method getTargetUnit() -> unit
            |       -> the targeted unit.
            |
            |   method getTargetDest() -> destructable
            |       -> the targeted destructable.
            |
            |   method getTargetItem() -> item
            |       -> the targeted item.
            |
            |   method getOrderId() -> integer
            |       -> the issued order id.
            |
            |   method getOrderType() -> integer
            |       -> the order type of the issued id.
            |
             -------------------------------------------------------
         
        -----------------------------------------------------------
     
        -----------------------------------------------------------
        How to use:
     
        Simply use the struct as follows:
     
            call OrderMatrix[unit].getTargetX()
            call OrderMatrix[unit].getTargetY()
         
        NOTE:
     
            (For the above functions)
         
            It is recommended not to use this within an Issued
            order event, since the current order could cause it
            to break.
         
        Or
     
            call OrderMatrix[unit].past[int].getTargetX()
     
        -----------------------------------------------------------
     
        -----------------------------------------------------------
        Credits:
     
            PurgeandFire for the typecast method for hashtables
                         (Now rewritten to no longer use such an
                          approach, but give credit anyway)
                         
            TriggerHappy for suggesting the expansion of the resource
            Jampion      for pointing out weird bugs (implicit)
                         that caused script-breaking behavior.
                       
        -----------------------------------------------------------
    */

globals
    private constant boolean ALLOW_DEBUGGING    = false
endglobals

private keyword InitModule

private struct StoreOrders extends array
    //  CONFIGURABLES

    //  Configure this amount if you want to store more than five orders.
    private static constant integer MAX_ORDERS  = 8

    //  END CONFIGURABLES
    private static constant integer MAX_SIZE    = 9

    readonly integer lastIndex       
    private integer relevantNode
    private boolean isUpdated

    readonly TableArray dataArray

    //  The following methods are only for readability in the upcoming private methods...
    private method getNext takes integer index returns integer
        return this.dataArray[2].integer[index]
    endmethod

    private method getPrev takes integer index returns integer
        return this.dataArray[3].integer[index]
    endmethod

    private method setNext takes integer index, integer value returns nothing
        set this.dataArray[2].integer[index] = value
    endmethod

    private method setPrev takes integer index, integer value returns nothing
        set this.dataArray[3].integer[index] = value
    endmethod

    private method popOrder takes nothing returns nothing
        local integer lastNode = this.dataArray[3].integer[0]
     
        call this.dataArray[6].unit.remove(lastNode + thistype.MAX_ORDERS)
        call this.dataArray[6].destructable.remove(lastNode + thistype.MAX_ORDERS*2)
        call this.dataArray[6].item.remove(lastNode + thistype.MAX_ORDERS*3)
       
        call this.setNext(this.getPrev(lastNode), this.getNext(lastNode))
        call this.setPrev(this.getNext(lastNode), this.getPrev(lastNode))
     
        //  Deallocate the instance ...
        set this.dataArray[1].integer[lastNode]   = this.dataArray[1].integer[0]
        set this.dataArray[1].integer[0]          = lastNode
       
        //  Decrement the size
        set this.dataArray[1].integer[-1]         = this.dataArray[1].integer[-1] - 1
        set this.dataArray[1].integer[-2]         = ModuloInteger(this.dataArray[1].integer[-2], thistype.MAX_ORDERS) + 1
    endmethod

    method operator [] takes integer index returns thistype
    static if ALLOW_DEBUGGING then
        debug call BJDebugMsg("Index to look at: " + I2S(index) + "\n")  // - This got leaked into release :P
       
        debug if index <= 0 or index >= thistype.MAX_ORDERS then
            debug call BJDebugMsg("thistype.op_getindex: Invalid index parameter.")
            debug call BJDebugMsg("thistype.op_getindex: Index provided: " + I2S(index) + "\n" /*
                            */  + "    -> Maximum orders stored by the system: " + I2S(thistype.MAX_ORDERS))
        debug endif
     
        debug if index > this.dataArray[1].integer[-1] then
            debug if index < thistype.MAX_ORDERS then
                debug call BJDebugMsg("thistype.op_getindex: Attempted to go beyond the order scope of the unit.")
                debug call BJDebugMsg("thistype.op_getindex: Unit hasn't been ordered around much yet.")
            debug call BJDebugMsg("thistype.op_getindex: Index provided: " + I2S(index) + "\n" /*
                    */  + "    -> Current amount of orders stored: " + I2S(this.dataArray[1].integer[-1]))

            debug endif
        debug endif
    endif
         
        //  A simple list behavior allows for potentially O(1) search.
        if (this.lastIndex != index) or (this.isUpdated) then
            set this.isUpdated = false
            set this.lastIndex = index

            set index = this.dataArray[1].integer[-1] + 1 - IMaxBJ(IMinBJ(index, this.dataArray[1].integer[-1]), 1)
            if this.dataArray[1].integer[-2] != 0 then
                set index = ModuloInteger(ModuloInteger(index, thistype.MAX_ORDERS) + this.dataArray[1].integer[-2], thistype.MAX_ORDERS)
                if index == 0 then
                    set index = thistype.MAX_ORDERS
                endif
            endif
     
            set this.relevantNode = index
        endif

    static if ALLOW_DEBUGGING then
        debug call BJDebugMsg("Index result: " + I2S(this.relevantNode))
    endif
   
        return this
    endmethod

    //  Protected method ... don't use outside of the map script...
    method store takes real x, real y, widget targ, integer lastOrder, integer lastOrderType, unit u, destructable d, item i returns nothing
        local integer index
     
        if this.dataArray == 0 then
            set this.dataArray = TableArray[thistype.MAX_SIZE]
        endif
         
        //  Pop the oldest order off.
        if this.dataArray[1].integer[-1] >= MAX_ORDERS then
            call this.popOrder()
        endif
     
        set index = this.dataArray[1].integer[0]
       
        if this.dataArray[1].integer[index] == 0 then
            set index = index + 1
            set this.dataArray[1].integer[0] = index
        else
            set this.dataArray[1].integer[0] = this.dataArray[1].integer[index]
            set this.dataArray[1].integer[index] = 0
        endif
     
        set this.dataArray[1].integer[-1] = this.dataArray[1].integer[-1] + 1
       
        call this.setNext(index, this.getNext(0))
        call this.setPrev(index, 0)
        call this.setNext(this.getPrev(index), index)
        call this.setPrev(this.getNext(index), index)
     
        //  Store the data...
        set this.dataArray[4].real[index]       = x
        set this.dataArray[5].real[index]       = y
        set this.dataArray[6].widget[index]     = targ
        set this.dataArray[7].integer[index]    = lastOrder
        set this.dataArray[8].integer[index]    = lastOrderType

        set this.dataArray[6].unit[index + thistype.MAX_ORDERS]           = u
        set this.dataArray[6].destructable[index + thistype.MAX_ORDERS*2] = d
        set this.dataArray[6].item[index + thistype.MAX_ORDERS*3]         = i

        set this.isUpdated                      = true
    endmethod
   
    method getTargetX takes nothing returns real
        return this.dataArray[4].real[this.relevantNode]
    endmethod

    method getTargetY takes nothing returns real
        return this.dataArray[5].real[this.relevantNode]
    endmethod

    method getTarget takes nothing returns widget
        return this.dataArray[6].widget[this.relevantNode]
    endmethod

    method getTargetUnit takes nothing returns unit
        return this.dataArray[6].unit[this.relevantNode + thistype.MAX_ORDERS]
    endmethod

    method getTargetDest takes nothing returns destructable
        return this.dataArray[6].destructable[this.relevantNode + thistype.MAX_ORDERS*2]
    endmethod

    method getTargetItem takes nothing returns item
        return this.dataArray[6].item[this.relevantNode + thistype.MAX_ORDERS*3]
    endmethod

    method getOrderId takes nothing returns integer
        return this.dataArray[7].integer[relevantNode]
    endmethod

    method getOrderType takes nothing returns integer
        return this.dataArray[8].integer[relevantNode]
    endmethod

    method getOrderCount takes nothing returns integer
        return this.dataArray[1].integer[-1]
    endmethod
   
    private static method onEnter takes nothing returns nothing
        local thistype instance = GetIndexedUnitId()
     
        if instance.dataArray == 0 then
            set instance.dataArray  = TableArray[thistype.MAX_SIZE]
         
            call instance.setNext(0, 0)
            call instance.setPrev(0, 0)
        endif
    endmethod

    private static method onExit takes nothing returns nothing
        local thistype instance = GetIndexedUnitId()
     
        if instance.dataArray != 0 then
            call instance.dataArray.destroy()
         
            set instance.dataArray = 0
            set instance.isUpdated = false
        endif
    endmethod

    static method init takes nothing returns nothing
        call RegisterUnitIndexEvent(Condition(function thistype.onEnter), EVENT_UNIT_INDEX)
        call RegisterUnitIndexEvent(Condition(function thistype.onExit), EVENT_UNIT_DEINDEX)
    endmethod

    implement InitModule
endstruct

struct OrderMatrix extends array
    private static constant integer INFO_SIZE           = 5

    readonly static constant integer IMMEDIATE_ORDER    = 1
    readonly static constant integer POINT_ORDER        = 2
    readonly static constant integer TARGET_ORDER       = 3

    private static TableArray orderData                 = 0
    private static unit       lastUnit                  = null

    static method operator [] takes unit u returns thistype
        set lastUnit = u
        return thistype(GetUnitId(u))
    endmethod

    method operator past takes nothing returns StoreOrders
        return StoreOrders(this)
    endmethod

    method operator [] takes integer index returns StoreOrders
        return this.past[index]
    endmethod

    /*
   
        Deprecated: No more functionality!
       
    //  Clears out the current data of issued order.
    private method clear takes nothing returns nothing
        if UnitDex.Initialized then
            call this.past.store(orderData[1].real[this], orderData[2].real[this], orderData[3].widget[this]/*
                             */, orderData[INFO_SIZE].integer[this], orderData[4].integer[this] /*
                             */, orderData[3].unit[this + (JASS_MAX_ARRAY_SIZE - 1)]            /*
                             */, orderData[3].destructable[this + (JASS_MAX_ARRAY_SIZE - 1)*2]  /*
                             */, orderData[3].item[this + (JASS_MAX_ARRAY_SIZE - 1)*3])
        endif
     
        set orderData[1].real[this]    = 0.
        set orderData[2].real[this]    = 0.
        set orderData[3].widget[this]  = null
        set orderData[4].integer[this] = 0
     
        set orderData[INFO_SIZE].integer[this] = GetUnitCurrentOrder(lastUnit)
    endmethod

    //  Checks if the current order is the same as the issued order.
    private method assertOrder takes nothing returns boolean
        return GetUnitCurrentOrder(lastUnit) == orderData[INFO_SIZE].integer[this]
    endmethod
    */
     
    method getOrderId takes nothing returns integer
        return orderData[INFO_SIZE].integer[this]
    endmethod

    method getOrderType takes nothing returns integer
        return orderData[4].integer[this]
    endmethod

    method getTargetX takes nothing returns real
        return orderData[1].real[this]
    endmethod

    method getTargetY takes nothing returns real
        return orderData[2].real[this]
    endmethod

    method getTarget takes nothing returns widget
        return thistype.orderData[3].widget[this]
    endmethod

    method getTargetUnit takes nothing returns unit
        return thistype.orderData[3].unit[this + (JASS_MAX_ARRAY_SIZE - 1)]
    endmethod

    method getTargetDest takes nothing returns destructable
        return thistype.orderData[3].destructable[this + (JASS_MAX_ARRAY_SIZE - 1)*2]
    endmethod

    method getTargetItem takes nothing returns item
        return thistype.orderData[3].item[this + (JASS_MAX_ARRAY_SIZE - 1)*3]
    endmethod

    private static method onIssueOrder takes nothing returns nothing
        local thistype inst = thistype[GetTriggerUnit()]
        local integer wId
        local widget w
     
        if UnitDex.Initialized then
            set w       = GetOrderTarget()
            set wId     = GetHandleId(w)
         
            call inst.past.store(orderData[1].real[inst], orderData[2].real[inst], orderData[3].widget[inst]/*
                             */, orderData[INFO_SIZE].integer[inst], orderData[4].integer[inst] /*
                             */, orderData[3].unit[inst + (JASS_MAX_ARRAY_SIZE - 1)]            /*
                             */, orderData[3].destructable[inst + (JASS_MAX_ARRAY_SIZE - 1)*2]  /*
                             */, orderData[3].item[inst + (JASS_MAX_ARRAY_SIZE - 1)*3])

            set orderData[INFO_SIZE].integer[inst] = GetIssuedOrderId()
         
            set orderData[1].real[inst] = GetOrderPointX()
            set orderData[2].real[inst] = GetOrderPointY()
         
            set orderData[3].widget[inst]  = w
         
            call orderData[3].unit.remove(inst + (JASS_MAX_ARRAY_SIZE - 1))
            call orderData[3].destructable.remove(inst + (JASS_MAX_ARRAY_SIZE - 1)*2)
            call orderData[3].item.remove(inst + (JASS_MAX_ARRAY_SIZE - 1)*3)
         
            if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_ORDER then
                set orderData[4].integer[inst] = thistype.IMMEDIATE_ORDER
             
            elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER then
                set orderData[4].integer[inst] = thistype.POINT_ORDER
             
            elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER then
                set orderData[4].integer[inst] = thistype.TARGET_ORDER
             
                if wId == GetHandleId(GetOrderTargetUnit()) then
                    set orderData[3].unit[inst + (JASS_MAX_ARRAY_SIZE - 1)]               = GetOrderTargetUnit()
                elseif wId == GetHandleId(GetOrderTargetDestructable()) then
                    set orderData[3].destructable[inst + (JASS_MAX_ARRAY_SIZE - 1)*2]     = GetOrderTargetDestructable()
                elseif wId == GetHandleId(GetOrderTargetItem()) then
                    set orderData[3].item[inst + (JASS_MAX_ARRAY_SIZE - 1)*3]             = GetOrderTargetItem()
                endif
            endif
         
            set w = null
        endif
    endmethod

    private static method initVar takes nothing returns nothing
        set orderData = TableArray[INFO_SIZE + 1]
    endmethod

    private static method initTrig takes nothing returns nothing
     
    static if LIBRARY_RegisterPlayerUnitEvent then
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onIssueOrder)
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onIssueOrder)
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onIssueOrder)
    else
        local trigger t = CreateTrigger()
     
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_ORDER)
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
     
        call TriggerAddCondition(t, Condition(function thistype.onIssueOrder))
     
        set t = null
    endif
    endmethod

    private static method init takes nothing returns nothing
        call initVar()
        call initTrig()     
    endmethod

    implement InitModule
endstruct

private module InitModule
    private static method onInit takes nothing returns nothing
        call init()   
    endmethod
endmodule

endlibrary
 
Last edited:
Unless I'm missing something, the way you associate instance data with units is unsafe. You are using the unit's handle ID as the struct instance. First of all this will not work on patches prior to 1.29 where the maximum array limit is lower. Even on the current patch it still has a chance to overflow with bigger maps. You should be using a unit indexer or a hashtable. As for your system's functionality, it appears to be only caching data about the last issued order of each unit. I don't really see the usefulness in that. If you take a look at something like Rising_Dusk's LastOrder, he keeps a list of orders for each unit.

You are missing some methods as well:
  • method getId takes nothing returns integer
  • method getTarget takes nothing returns widget
 
TriggerHappy said:
As for your system's functionality, it appears to be only caching data about the last issued order of each unit. I don't really see the usefulness in that. If you take a look at something like Rising_Dusk's LastOrder, he keeps a list of orders for each unit.

In this you are right. It actually uses a hashtable, primarily from Table, which is why it (apparently) exceeds the maximum array size. I actually had another purpose for this resource, to act as the base for my own attempt at builder detection.

I would like to know what getId does, since I cannot think of a possible meaning to it. However, the getTarget functionality is coming.

EDIT:

The update to the script added retrieving of previous orders and their data.

EDIT #2:

Adjusted the script so that IndexError messages won't get spammed in debug mode.
 
Last edited:

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
Looks good. I like the operator usage here. Makes it very easy to access data from the order matrix.
I think the documentation needs to be updated. I did not find getOrderId in it.

(For the above functions)

It is recommended not to use this within an Issued
order event, since the current order could cause it
to break.
Can you elaborate? I think order events are one of the situations one would like to use this system the most.
 
It looks like you messed up slightly when registering events. With RegisterPlayerUnitEvent you are catching:
  • EVENT_PLAYER_UNIT_ISSUED_ORDER
  • EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER
  • EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER
While with normal event registering:
  • EVENT_PLAYER_UNIT_ISSUED_ORDER
  • EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER
  • EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER
You can see they don't match. I also noticed some orders weren't being recorded while testing.

Also unless OrderIndex was a public library used by other people, then maybe you should remove library_once OrderIndex requires OrderMatrix.
 
I can't seem to get the past orders to work properly. The past orders always return zero. I have tried using past.lastIndex as well (after making it readonly) and it still returned zero. Am I doing something wrong or is this a bug?

JASS:
    local unit u = FirstOfGroup(GetUnitsSelectedAll(GetTriggerPlayer()))
    local integer last = OrderMatrix[u].past[1].getOrderId()
    local integer current = OrderMatrix[u].getOrderId()
  
    call BJDebugMsg("Last Order: " + I2S(last) + " (" + OrderId2String(last) + ")") // always 0
    call BJDebugMsg("Current Order: " + I2S(current) + " (" + OrderId2String(current) + ")") // displays correctly
  
    set u = null

I attached my demo map below. Maybe you could include your own demo map showing how to properly use the system.

You are also missing getOrderId in the documentation.
 

Attachments

  • OrderMatrix.w3x
    45.8 KB · Views: 54

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
That is weird. In the test I made it worked without a problem.

I added your test trigger to my demo map and after pressing escape the system had wrong data.
This was my test trigger:
JASS:
function Trig_OnOrder_Actions takes nothing returns nothing
    call BJDebugMsg(GetUnitName(GetTriggerUnit()))
    call BJDebugMsg(OrderId2String(OrderMatrix[GetTriggerUnit()].getOrderId()))
    call BJDebugMsg(OrderId2String(OrderMatrix[GetTriggerUnit()].past[1].getOrderId()))
    call BJDebugMsg(OrderId2String(OrderMatrix[GetTriggerUnit()].past[2].getOrderId()))
endfunction
//===========================================================================
function InitTrig_OnOrder takes nothing returns nothing
    set gg_trg_OnOrder = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_OnOrder, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_OnOrder, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_OnOrder, EVENT_PLAYER_UNIT_ISSUED_ORDER )
    call TriggerAddAction( gg_trg_OnOrder, function Trig_OnOrder_Actions )
endfunction

After issuing 3 orders, they were correctly displayed:
Code:
holdposition
stop
smart
Then pressing escape gives the correct orders as well:
Code:
Last Order: stop
Current Order: holdposition
Now ordering patrol gives me:
Code:
patrol
stop
stop
Correct would be:
Code:
patrol
holdposition
stop

It seems to be a problem, if you only use past[1]. If you add past[2] it works again.
I attached my demo map as well.
 

Attachments

  • OrderMatrix.w3x
    50.8 KB · Views: 52
Sorry that it took a while. I was too focused in my entry to consider the test map.

That is quite a pressing bug. I'll see if I can reproduce it in your map.
Bug was successfully reproduced. It should hopefully be resolved now.


v.1.4
Update Notes:
  • Updated the library with a smarter search algorithm for StoreOrders (It takes advantage of the fact that the last node is always the one to be removed) in method operator [].
  • Fixed a flaw in method store of struct StoreOrders where the newly-allocated node's pointer to its' previous pointed instead to the last instance.
  • Added a new boolean flag, (isUpdated).
  • Documentation updated to reflect the changes and suggestions above.


EDIT:

Attached updated test map (not mine)
 

Attachments

  • OrderMatrix.w3x
    46.7 KB · Views: 66

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
I couldn't reprdouce this bug, so I think it should be fixed now.

I might have found another issue or maybe I am just doing it wrong. I tried to create a trigger, that kills all units that have had a certain unit as their target in the last 5 orders (or in their current one).
This is what I have come up with:
JASS:
scope Castle initializer Init

   globals
       private unit CASTLE
   endglobals

    private function KillOne takes nothing returns nothing
        local unit u = GetEnumUnit()
        local unit current = OrderMatrix[u].getTargetUnit()
        local unit last
        local integer i = 1
       
        if current == CASTLE then
            call BJDebugMsg("your current order targets the castle")
            call KillUnit(u)
        endif
       
        loop
       
            exitwhen i > 5
            set last = OrderMatrix[u].past[1].getTargetUnit()
            if last == CASTLE then
                call BJDebugMsg("your "+I2S(i)+"th order targeted the castle")
                call KillUnit(u)
            endif
            set i = i + 1  
        endloop
       
       
    endfunction

    private function KillAll takes nothing returns nothing
        local group g = CreateGroup()
        call GroupEnumUnitsOfPlayer(g, Player(0), null)
        call ForGroup(g, function KillOne)
    endfunction
   
   private function Init takes nothing returns nothing
       local trigger trg = CreateTrigger()
       call TriggerRegisterPlayerEvent(trg, Player(0), EVENT_PLAYER_END_CINEMATIC)
       call TriggerAddAction(trg, function KillAll)
       set CASTLE = gg_unit_hcas_0005 //another GUI trigger makes sure this variable is used correctly
       call BJDebugMsg(GetUnitName(CASTLE ))
   endfunction

endscope

Basically loop through all units and then loop through their five last orders and the current order. When getTargetUnit() is equal to the Castle the unit is killed.

When testing a lot of units die that didn't even get an order. After ordering the castle to right click itself once, it tells me that all past orders of the castle had the castle as target.
 

Attachments

  • OrderMatrixCastle.w3x
    47.2 KB · Views: 47
JASS:
    method getTargetUnit takes nothing returns unit
        set thistype.orderData[3].widget[-1] = thistype.orderData[3].widget[this]
        return thistype.orderData[3].unit[-1]
    endmethod

    method getTargetDest takes nothing returns destructable
        set thistype.orderData[3].widget[-1] = thistype.orderData[3].widget[this]
        return thistype.orderData[3].destructable[-1]
    endmethod

    method getTargetItem takes nothing returns item
        set thistype.orderData[3].widget[-1] = thistype.orderData[3].widget[this]
        return thistype.orderData[3].item[-1]
    endmethod

Oddly enough, this is based on the nature of how hashtables work and how typecasting occurs within hashtables.
What I could infer from this is the following:

A null value of a certain type which cannot be inferred is considered an erroneous assignment to a hashtable, and will not overwrite the previous value;
in this case, it is [3][-1].

To circumvent this, I will have to make use of what space I already have. That means instead of returning a pseudo-global entry, this will return the entry specific to each unit.



-------------------------------------------------------------------------------------------------
Update 1.4.2:

Changelog:

  • Fixed a minor bug that allowed data to persist when using any of these methods:
    JASS:
    OrderMatrix[u]
          ..getTargetUnit()
          ..getTargetDest()
          ..getTargetItem()
-------------------------------------------------------------------------------------------------
Update 1.4.1:

Changelog:

  • Fixed a script-breaking bug where the values retrieved from different units are just the same thing. (This was due to the type-safe storage management of hashtables, which caused said error).
  • Credits updated!
 

Attachments

  • OrderMatrixCastle.w3x
    48.9 KB · Views: 49
Last edited:

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
I tested the castle trigger with 1.4.2.
It now tells me, that there are 2 orders targeting the castle, even if I only have one order targeting the castle.
To reproduce: use a unit, right click the castle and then give for example 2 other orders.
Then it shows that the 2nd and 3rd last order both have targeted the castle. Both these orders also have the same order id (smart), even if only one smart order was given.

I found the following issues:
even if more than 5 orders have been given to the unit, OrderMatrix[u].past[i] gave the same relevant Node for two different values of i.
With the order sequence of:
2 some order
1 target castle order
5+ some order
1 target castle order
2 some order
I get that all my last 5 orders have targeted the castle.
 

Attachments

  • OrderMatrixCastle2.w3x
    49.7 KB · Views: 39
I would say that that is some odd arithmetic I got going there.
I'll further test the map and see if I can improve on the [] operator functionality.

Another thing, you can also do this:

JASS:
OrderMatrix[u][i].getMethod()

It's quite a nice syntactical-sugar that I added in the previous versions.

Test Results:


After activating (and modifying) the handler function found on the OnOrder scope, I found out that killing the unit also caused an issued order event, which in the Order Matrix, does not have any filters for such exceptions, thus explaining the problem above.

After testing the possibility of an error in obtaining the indices, I could not successfully reproduce that error. However, I did find out that the [] operator in struct StoreOrders occasionally spit out a 0 when it should result into MAX_ORDERS.

Anyway, I'll post the updated script, along with the map (Slightly improved in-map debug messages to be a bit more clear):

Version 1.4.3:
JASS:
library OrderMatrix /*

    */ requires /*

         ---------------------------
        |   Legend:                 |
        |    - #     -> Requirement |
        |    - #?    -> Optional    |
         ---------------------------


        --------------------------------------
        */ Table                            /*
        --------------------------------------
            -> Bribe
      
            link: https://www.hiveworkshop.com/threads/snippet-new-table.188084/#post-1835068
  
        --------------------------------------
        */  UnitDex                         /*
        --------------------------------------
            -> TriggerHappy
      
            link: https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
  
        --------------------------------------
        */ optional RegisterPlayerUnitEvent /*
        --------------------------------------
            # RegisterNativeEvent
      
            -> Bannar
      
            link: https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
                  
        Table                       -> Simplifies storage by a mile.
        UnitDex                     -> Allows the system to use GetUnitId instead of GetHandleId
        RegisterPlayerUnitEvent     -> Not really required, but helpful anyway.
  
         -----------------------
        |   OrderMatrix         |
        |   - MyPad             |
        |                       |
        |   Version: 1.4.3      |
         -----------------------
    
        -----------------------------------------------------------
        Description:
  
            OrderMatrix is a library that automatically
            maps out the relevant information of issued orders.
      
            It also stores the previous 5 orders of each unit
            for convenience, although the number is configurable.
        -----------------------------------------------------------
  
        -----------------------------------------------------------
        struct StoreOrders extends array
  
        Members:
      
             -------------------------------------------------------
            |
            |   @configurable static constant integer MAX_ORDERS
            |       (private)
            |
            |       The maximum amount of previous orders to be
            |       stored. You can change this to any positive
            |       value.
            |
            |   integer relevantNode (private)
            |       The node in question when calling
            |       OrderMatrix[unit].past[index].
            |
            |       Made an instance member so as not to cause any
            |       conflicts.
            |
            |   integer lastIndex (private)
            |       The previously stored member of the operator
            |       [].
            |
            |       Made an instance member so as not to cause any
            |       conflicts.
            |
            |   TableArray dataArray (private)
            |       The main storage of data. Note that this is
            |       a per-unit instance.
            |
             -------------------------------------------------------
  
        Methods:
      
             -------------------------------------------------------
            |
            |   method operator [integer index] -> thistype
            |       Stores the relevantNode and lastIndex
            |       and assigns a new value to relevantNode
            |       if the lastIndex is different from the
            |       previous value
            |
            |   method getTargetX() -> real
            |       -> the x-coordinate of past issued order.
            |       The depth of past order depends on the parameter
            |       of the operator [].
            |
            |   method getTargetY() -> real
            |       -> the y-coordinate of past issued order.
            |       The depth of past order depends on the parameter
            |       of the operator [].
            |
            |   method getTarget() -> widget
            |       -> the past targeted widget.
            |       (operator [] dependent)
            |
            |   method getTargetUnit() -> unit
            |       -> the past targeted unit.
            |       (operator [] dependent)
            |
            |   method getTargetDest() -> destructable
            |       -> the past targeted destructable.
            |       (operator [] dependent)
            |
            |   method getTargetItem() -> item
            |       -> the past targeted item.
            |       (operator [] dependent)
            |
            |   method getOrderId() -> integer
            |       -> the past order..
            |       (operator [] dependent)
            |
            |   method getOrderCount() -> integer
            |       -> the amount of orders issued.
            |        Maximum is MAX_ORDERS
            |
            |   method getOrderType() -> integer
            |       -> the order type of the past order
            |       (Either Immediate, Target or Point)
            |       (operator [] dependent)
            |
             -------------------------------------------------------
      
  
        struct OrderMatrix extends array
  
        Members:
  
             -------------------------------------------------------  
            |
            |   static constant integer INFO_SIZE (private)
            |       The amount of space used by the next member.
            |
            |   static TableArray orderData (private)
            |       Stores relevant information, from the x and y
            |       to the target type.
            |
            |   static unit lastUnit (private)
            |       The unit that was passed in the operator []
            |
             -------------------------------------------------------
          
        Methods:
      
             -------------------------------------------------------
            |
            |   static method operator [] (unit u) -> OrderMatrix
            |       Returns the unit id of the unit typecasted
            |       to OrderMatrix.
            |
            |   method operator past() -> StoreOrders
            |       Returns the same instance but typecasted.
            |
            |   method operator [] (integer index) -> StoreOrders
            |       Another way of writing OrderMatrix[unit].past
            |
            |   method getTargetX() -> real
            |       -> the x-coordinate of last issued order.
            |       This may not work with auto-acquire events.
            |
            |   method getTargetY() -> real
            |       -> the y-coordinate of last issued order.
            |       This may not work with auto-acquire events.
            |
            |   method getTarget() -> widget
            |       -> the targeted widget.
            |
            |   method getTargetUnit() -> unit
            |       -> the targeted unit.
            |
            |   method getTargetDest() -> destructable
            |       -> the targeted destructable.
            |
            |   method getTargetItem() -> item
            |       -> the targeted item.
            |
            |   method getOrderId() -> integer
            |       -> the issued order id.
            |
            |   method getOrderType() -> integer
            |       -> the order type of the issued id.
            |
             -------------------------------------------------------
      
        -----------------------------------------------------------
  
        -----------------------------------------------------------
        How to use:
  
        Simply use the struct as follows:
  
            call OrderMatrix[unit].getTargetX()
            call OrderMatrix[unit].getTargetY()
      
        NOTE:
  
            (For the above functions)
      
            It is recommended not to use this within an Issued
            order event, since the current order could cause it
            to break.
      
        Or
  
            call OrderMatrix[unit].past[int].getTargetX()
  
        -----------------------------------------------------------
  
        -----------------------------------------------------------
        Credits:
  
            PurgeandFire for the typecast method for hashtables
                         (Now rewritten to no longer use such an
                          approach, but give credit anyway)
                      
            TriggerHappy for suggesting the expansion of the resource
            Jampion      for pointing out a weird bug (implicit)
                         that caused script-breaking behavior.
                    
        -----------------------------------------------------------
    */

globals
    private constant boolean ALLOW_DEBUGGING    = false
endglobals

private keyword InitModule

private struct StoreOrders extends array
    //  CONFIGURABLES

    //  Configure this amount if you want to store more than five orders.
    private static constant integer MAX_ORDERS  = 8

    //  END CONFIGURABLES
    private static constant integer MAX_SIZE    = 9

    readonly integer lastIndex    
    private integer relevantNode
    private boolean isUpdated

    readonly TableArray dataArray

    //  The following methods are only for readability in the upcoming private methods...
    private method getNext takes integer index returns integer
        return this.dataArray[2].integer[index]
    endmethod

    private method getPrev takes integer index returns integer
        return this.dataArray[3].integer[index]
    endmethod

    private method setNext takes integer index, integer value returns nothing
        set this.dataArray[2].integer[index] = value
    endmethod

    private method setPrev takes integer index, integer value returns nothing
        set this.dataArray[3].integer[index] = value
    endmethod

    private method popOrder takes nothing returns nothing
        local integer lastNode = this.dataArray[3].integer[0]
  
        call this.setNext(this.getPrev(lastNode), this.getNext(lastNode))
        call this.setPrev(this.getNext(lastNode), this.getPrev(lastNode))
  
        //  Deallocate the instance ...
        set this.dataArray[1].integer[lastNode]   = this.dataArray[1].integer[0]
        set this.dataArray[1].integer[0]          = lastNode
  
        //  Decrement the size
        set this.dataArray[1].integer[-1]         = this.dataArray[1].integer[-1] - 1
        set this.dataArray[1].integer[-2]         = ModuloInteger(this.dataArray[1].integer[-2], thistype.MAX_ORDERS) + 1
    endmethod

    method operator [] takes integer index returns thistype
    static if ALLOW_DEBUGGING then
        debug call BJDebugMsg("Index to look at: " + I2S(index) + "\n")  // - This got leaked into release :P
    
        debug if index <= 0 or index >= thistype.MAX_ORDERS then
            debug call BJDebugMsg("thistype.op_getindex: Invalid index parameter.")
            debug call BJDebugMsg("thistype.op_getindex: Index provided: " + I2S(index) + "\n" /*
                            */  + "    -> Maximum orders stored by the system: " + I2S(thistype.MAX_ORDERS))
        debug endif
  
        debug if index > this.dataArray[1].integer[-1] then
            debug if index < thistype.MAX_ORDERS then
                debug call BJDebugMsg("thistype.op_getindex: Attempted to go beyond the order scope of the unit.")
                debug call BJDebugMsg("thistype.op_getindex: Unit hasn't been ordered around much yet.")
            debug call BJDebugMsg("thistype.op_getindex: Index provided: " + I2S(index) + "\n" /*
                    */  + "    -> Current amount of orders stored: " + I2S(this.dataArray[1].integer[-1]))

            debug endif
        debug endif
    endif
      
        //  A simple list behavior allows for potentially O(1) search.
        if (this.lastIndex != index) or (this.isUpdated) then
            set this.isUpdated = false
            set this.lastIndex = index

            set index = this.dataArray[1].integer[-1] + 1 - IMaxBJ(IMinBJ(index, this.dataArray[1].integer[-1]), 1)
            if this.dataArray[1].integer[-2] != 0 then
                set index = ModuloInteger(ModuloInteger(index, thistype.MAX_ORDERS) + this.dataArray[1].integer[-2], thistype.MAX_ORDERS)
                if index == 0 then
                    set index = thistype.MAX_ORDERS
                endif
            endif
  
            set this.relevantNode = index
        endif

    static if ALLOW_DEBUGGING then
        debug call BJDebugMsg("Index result: " + I2S(this.relevantNode))
    endif
 
        return this
    endmethod

    //  Protected method ... don't use outside of the map script...
    method store takes real x, real y, widget targ, integer lastOrder, integer lastOrderType, unit u, destructable d, item i returns nothing
        local integer index
  
        if this.dataArray == 0 then
            set this.dataArray = TableArray[thistype.MAX_SIZE]
        endif
  
        set index = this.dataArray[1].integer[0]
  
        //  Pop the oldest order off.
        if this.dataArray[1].integer[-1] >= MAX_ORDERS then
            call this.popOrder()
        endif
  
        //  Allocate an instance
        if this.dataArray[1].integer[index] == 0 then
            set index = index + 1
            set this.dataArray[1].integer[0] = index
        else
            set this.dataArray[1].integer[0] = this.dataArray[1].integer[index]
            set this.dataArray[1].integer[index] = 0
        endif
  
        set this.dataArray[1].integer[-1] = this.dataArray[1].integer[-1] + 1
    
        call this.setNext(index, this.getNext(0))
        call this.setPrev(index, 0)
        call this.setNext(this.getPrev(index), index)
        call this.setPrev(this.getNext(index), index)
  
        //  Store the data...
        set this.dataArray[4].real[index]       = x
        set this.dataArray[5].real[index]       = y
        set this.dataArray[6].widget[index]     = targ
        set this.dataArray[7].integer[index]    = lastOrder
        set this.dataArray[8].integer[index]    = lastOrderType
          
        set this.dataArray[6].unit[index + thistype.MAX_ORDERS]           = u
        set this.dataArray[6].destructable[index + thistype.MAX_ORDERS*2] = d
        set this.dataArray[6].item[index + thistype.MAX_ORDERS*3]         = i

        set this.isUpdated                      = true
    endmethod

    method getTargetX takes nothing returns real
        return this.dataArray[4].real[this.relevantNode]
    endmethod

    method getTargetY takes nothing returns real
        return this.dataArray[5].real[this.relevantNode]
    endmethod

    method getTarget takes nothing returns widget
        return this.dataArray[6].widget[this.relevantNode]
    endmethod

    method getTargetUnit takes nothing returns unit
        return this.dataArray[6].unit[this.relevantNode + thistype.MAX_ORDERS]
    endmethod

    method getTargetDest takes nothing returns destructable
        return this.dataArray[6].destructable[this.relevantNode + thistype.MAX_ORDERS*2]
    endmethod

    method getTargetItem takes nothing returns item
        return this.dataArray[6].item[this.relevantNode + thistype.MAX_ORDERS*3]
    endmethod

    method getOrderId takes nothing returns integer
        return this.dataArray[7].integer[relevantNode]
    endmethod

    method getOrderType takes nothing returns integer
        return this.dataArray[8].integer[relevantNode]
    endmethod

    method getOrderCount takes nothing returns integer
        return this.dataArray[1].integer[-1]
    endmethod
 
    private static method onEnter takes nothing returns nothing
        local thistype instance = GetIndexedUnitId()
  
        if instance.dataArray == 0 then
            set instance.dataArray  = TableArray[thistype.MAX_SIZE]
      
            call instance.setNext(0, 0)
            call instance.setPrev(0, 0)
        endif
    endmethod

    private static method onExit takes nothing returns nothing
        local thistype instance = GetIndexedUnitId()
  
        if instance.dataArray != 0 then
            call instance.dataArray.destroy()
      
            set instance.dataArray = 0
            set instance.isUpdated = false
        endif
    endmethod

    static method init takes nothing returns nothing
        call RegisterUnitIndexEvent(Condition(function thistype.onEnter), EVENT_UNIT_INDEX)
        call RegisterUnitIndexEvent(Condition(function thistype.onExit), EVENT_UNIT_DEINDEX)
    endmethod

    implement InitModule
endstruct

struct OrderMatrix extends array
    private static constant integer INFO_SIZE           = 5

    readonly static constant integer IMMEDIATE_ORDER    = 1
    readonly static constant integer POINT_ORDER        = 2
    readonly static constant integer TARGET_ORDER       = 3

    private static TableArray orderData                 = 0
    private static unit       lastUnit                  = null

    static method operator [] takes unit u returns thistype
        set lastUnit = u
        return thistype(GetUnitId(u))
    endmethod

    method operator past takes nothing returns StoreOrders
        return StoreOrders(this)
    endmethod

    method operator [] takes integer index returns StoreOrders
        return this.past[index]
    endmethod

    /*
 
        Deprecated: No more functionality!
    
    //  Clears out the current data of issued order.
    private method clear takes nothing returns nothing
        if UnitDex.Initialized then
            call this.past.store(orderData[1].real[this], orderData[2].real[this], orderData[3].widget[this]/*
                             */, orderData[INFO_SIZE].integer[this], orderData[4].integer[this] /*
                             */, orderData[3].unit[this + (JASS_MAX_ARRAY_SIZE - 1)]            /*
                             */, orderData[3].destructable[this + (JASS_MAX_ARRAY_SIZE - 1)*2]  /*
                             */, orderData[3].item[this + (JASS_MAX_ARRAY_SIZE - 1)*3])
        endif
  
        set orderData[1].real[this]    = 0.
        set orderData[2].real[this]    = 0.
        set orderData[3].widget[this]  = null
        set orderData[4].integer[this] = 0
  
        set orderData[INFO_SIZE].integer[this] = GetUnitCurrentOrder(lastUnit)
    endmethod

    //  Checks if the current order is the same as the issued order.
    private method assertOrder takes nothing returns boolean
        return GetUnitCurrentOrder(lastUnit) == orderData[INFO_SIZE].integer[this]
    endmethod
    */
  
    method getOrderId takes nothing returns integer
        return orderData[INFO_SIZE].integer[this]
    endmethod

    method getOrderType takes nothing returns integer
        return orderData[4].integer[this]
    endmethod

    method getTargetX takes nothing returns real
        return orderData[1].real[this]
    endmethod

    method getTargetY takes nothing returns real
        return orderData[2].real[this]
    endmethod

    method getTarget takes nothing returns widget
        return thistype.orderData[3].widget[this]
    endmethod

    method getTargetUnit takes nothing returns unit
        return thistype.orderData[3].unit[this + (JASS_MAX_ARRAY_SIZE - 1)]
    endmethod

    method getTargetDest takes nothing returns destructable
        return thistype.orderData[3].destructable[this + (JASS_MAX_ARRAY_SIZE - 1)*2]
    endmethod

    method getTargetItem takes nothing returns item
        return thistype.orderData[3].item[this + (JASS_MAX_ARRAY_SIZE - 1)*3]
    endmethod

    private static method onIssueOrder takes nothing returns nothing
        local thistype inst = thistype[GetTriggerUnit()]
        local integer wId
        local widget w
  
        if UnitDex.Initialized then
            set w       = GetOrderTarget()
            set wId     = GetHandleId(w)
      
            call inst.past.store(orderData[1].real[inst], orderData[2].real[inst], orderData[3].widget[inst]/*
                             */, orderData[INFO_SIZE].integer[inst], orderData[4].integer[inst] /*
                             */, orderData[3].unit[inst + (JASS_MAX_ARRAY_SIZE - 1)]            /*
                             */, orderData[3].destructable[inst + (JASS_MAX_ARRAY_SIZE - 1)*2]  /*
                             */, orderData[3].item[inst + (JASS_MAX_ARRAY_SIZE - 1)*3])

            set orderData[INFO_SIZE].integer[inst] = GetIssuedOrderId()
      
            set orderData[1].real[inst] = GetOrderPointX()
            set orderData[2].real[inst] = GetOrderPointY()
      
            set orderData[3].widget[inst]  = w
      
            call orderData[3].unit.remove(inst + (JASS_MAX_ARRAY_SIZE - 1))
            call orderData[3].destructable.remove(inst + (JASS_MAX_ARRAY_SIZE - 1)*2)
            call orderData[3].item.remove(inst + (JASS_MAX_ARRAY_SIZE - 1)*3)
      
            if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_ORDER then
                set orderData[4].integer[inst] = thistype.IMMEDIATE_ORDER
          
            elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER then
                set orderData[4].integer[inst] = thistype.POINT_ORDER
          
            elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER then
                set orderData[4].integer[inst] = thistype.TARGET_ORDER
          
                if wId == GetHandleId(GetOrderTargetUnit()) then
                    set orderData[3].unit[inst + (JASS_MAX_ARRAY_SIZE - 1)]               = GetOrderTargetUnit()
                elseif wId == GetHandleId(GetOrderTargetDestructable()) then
                    set orderData[3].destructable[inst + (JASS_MAX_ARRAY_SIZE - 1)*2]     = GetOrderTargetDestructable()
                elseif wId == GetHandleId(GetOrderTargetItem()) then
                    set orderData[3].item[inst + (JASS_MAX_ARRAY_SIZE - 1)*3]             = GetOrderTargetItem()
                endif
            endif
      
            set w = null
        endif
    endmethod

    private static method initVar takes nothing returns nothing
        set orderData = TableArray[INFO_SIZE + 1]
    endmethod

    private static method initTrig takes nothing returns nothing
  
    static if LIBRARY_RegisterPlayerUnitEvent then
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onIssueOrder)
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onIssueOrder)
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onIssueOrder)
    else
        local trigger t = CreateTrigger()
  
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_ORDER)
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
  
        call TriggerAddCondition(t, Condition(function thistype.onIssueOrder))
  
        set t = null
    endif
    endmethod

    private static method init takes nothing returns nothing
        call initVar()
        call initTrig()  
    endmethod

    implement InitModule
endstruct

private module InitModule
    private static method onInit takes nothing returns nothing
        call init()
    endmethod
endmodule

endlibrary
 

Attachments

  • OrderMatrixCastle2.w3x
    49.6 KB · Views: 45
Last edited:

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
I am still having the problem, that you cannot really overwrite the order target. When I target the castle once and then give more than MaxOrders orders, every order targets the castle and the unit is killed.
I used the map you uploaded and added some debug messages. I order the paladin to right click the castle once and then order him to right click on the map a lot of times. The index at which the information is stored in StoreOrders seems to increase all the time. What can be seen is, that the target array (this.dataArray[6].unit) is filled up with Castle whenever an order is given.

I added debug messages, when a target unit is stored and loaded. When it is stored the entire this.dataArray[6].unit array is printed.
Simply order the paladin to target the castle once and then give other orders. You should see how the array is filled with Castle as target.
I don't know what the exact problem in the system is, but I hope this information helps anyway.
 

Attachments

  • OrderMatrixCastle4.w3x
    50.2 KB · Views: 39
JASS:
method store takes real x, real y, widget targ, integer lastOrder, integer lastOrderType, unit u, destructable d, item i returns nothing
        local integer index
local integer j
   
        if this.dataArray == 0 then
            set this.dataArray = TableArray[thistype.MAX_SIZE]
        endif
               
        // Error starts here
        set index = this.dataArray[1].integer[0]

        //  Pop the oldest order off.
        if this.dataArray[1].integer[-1] >= MAX_ORDERS then
            call this.popOrder()
        endif
   
     
        if this.dataArray[1].integer[index] == 0 then
            set index = index + 1
            set this.dataArray[1].integer[0] = index
        else
            debug call BJDebugMsg("Should have recycled an index.")
            set this.dataArray[1].integer[0] = this.dataArray[1].integer[index]
            set this.dataArray[1].integer[index] = 0
        endif
   
        set this.dataArray[1].integer[-1] = this.dataArray[1].integer[-1] + 1
     
        call this.setNext(index, this.getNext(0))
        call this.setPrev(index, 0)
        call this.setNext(this.getPrev(index), index)
        call this.setPrev(this.getNext(index), index)
   
        //  Store the data...
        set this.dataArray[4].real[index]       = x
        set this.dataArray[5].real[index]       = y
        set this.dataArray[6].widget[index]     = targ
        set this.dataArray[7].integer[index]    = lastOrder
        set this.dataArray[8].integer[index]    = lastOrderType

        set this.dataArray[6].unit[index + thistype.MAX_ORDERS]           = u
    call BJDebugMsg("store")

set j = 0       
loop
    exitwhen j >= index + thistype.MAX_ORDERS
    call BJDebugMsg("[" + I2S(j) + "] = " + GetUnitName(this.dataArray[6].unit[j]))
    set j = j + 1
endloop

    call BJDebugMsg(GetUnitName(this.dataArray[6].unit[index + thistype.MAX_ORDERS]))
    call BJDebugMsg("store at "+I2S(index) + " + " + I2S(thistype.MAX_ORDERS) + " = "+ I2S(index + thistype.MAX_ORDERS)+ "\n ")

        set this.dataArray[6].destructable[index + thistype.MAX_ORDERS*2] = d
        set this.dataArray[6].item[index + thistype.MAX_ORDERS*3]         = i

        set this.isUpdated                      = true
    endmethod

I found out the reason for this .. overflow. I had retrieved the next instance to be allocated before deallocating (when necessary) in the first place.

This could have resulted into the following:

Current allocator node0123456789
Result12345678910

Instead of the following:

Current allocator node0123456789
Result1234567812

EDIT:

Update has been released, apparently fixing some minor bugs.

Changelog:

  • Indices for each order issued to each unit no longer overflow.
  • Storage list should observe correct behavior.
 

Attachments

  • gUpdated.w3x
    49.7 KB · Views: 43
Last edited:
Top