• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Solved] Anyone have LastOrder by Rising_Dusk or similar?

Level 6
Joined
Dec 6, 2009
Messages
173
Hello,

Since WC3c seems to be removed I can not find LastOrder by Rising_Dusk so does anyone have this saved and could share it with me? I belive this would be the solution I am looking for.

What I need is to have the last order of a unit and transfer that order to a new unit that replaces the old one. So how do I do that? If the last unit was attacking a unit, trying to pick up an item or or moving to a point I want the new unit to do the same.
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
I checked all my old folders and maps and apparently don't have a copy of LastOrder saved anywhere, which is unfortunate. I imagine that the library never changed much, so I found a thread from 2014 with an attachment that I extracted the code from. (Now that I see the code I'm fairly confident there was a newer version of this that allowed you to issue any arbitrary "N-th most recent order" rather than just the last two, but maybe I've imagined that and it sounds like you don't need it.)

The user in that thread changed the requirement to use AutoIndex instead of UnitUserData, and I've left it that way. In theory any indexer can be used, the system just expects a GetUnitId() function to exist:
JASS:
library LastOrder initializer Init needs AutoIndex
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library has a lot of usefulness for when you want to interface with the
//* last order a unit was given. This can be useful for simulating spell errors
//* and where you'd want to give them back the order they had prior to the spell
//* cast (whereas without this library, they'd just forget their orders).
//*
//* There are some handy interfacing options for your use here --
//*     function GetLastOrderId takes unit u returns integer
//*     function GetLastOrderString takes unit u returns string
//*     function GetLastOrderType takes unit u returns integer
//*     function GetLastOrderX takes unit u returns real
//*     function GetLastOrderY takes unit u returns real
//*     function GetLastOrderTarget takes unit u returns widget
//*     function AbortOrder takes unit u returns boolean
//*
//* There are also some order commands that can be useful --
//*     function IssueLastOrder takes unit u returns boolean
//*     function IssueSecondLastOrder takes unit u returns boolean
//*     function IsLastOrderFinished takes unit u returns boolean
//*
//* You can access any information you'd like about the orders for your own
//* order handling needs.
//*
globals
    //* Storage for last order
    private          integer array Order
    private          integer array Type
    private          widget  array Targ
    private          boolean array Flag
    private          real    array X
    private          real    array Y
   
    //* Storage for second last order
    private          integer array P_Order
    private          integer array P_Type
    private          widget  array P_Targ
    private          boolean array P_Flag
    private          real    array P_X
    private          real    array P_Y
   
    //* Order type variables
            constant integer       ORDER_TYPE_TARGET    = 1
            constant integer       ORDER_TYPE_POINT     = 2
            constant integer       ORDER_TYPE_IMMEDIATE = 3
   
    //* Trigger for the order catching
    private          trigger       OrderTrg             = CreateTrigger()
endglobals

//**********************************************************
function GetLastOrderId takes unit u returns integer
    return Order[GetUnitId(u)]
endfunction
function GetLastOrderString takes unit u returns string
    return OrderId2String(Order[GetUnitId(u)])
endfunction
function GetLastOrderType takes unit u returns integer
    return Type[GetUnitId(u)]
endfunction
function GetLastOrderX takes unit u returns real
    return X[GetUnitId(u)]
endfunction
function GetLastOrderY takes unit u returns real
    return Y[GetUnitId(u)]
endfunction
function GetLastOrderTarget takes unit u returns widget
    return Targ[GetUnitId(u)]
endfunction
//**********************************************************
private function OrderExclusions takes unit u, integer id returns boolean
    //* Excludes specific orders or unit types from registering with the system
    //*
    //* 851972: stop
    //*         Stop is excluded from the system, but you can change it by
    //*         adding a check for it below. id == 851972
    //*
    //* 851971: smart
    //* 851986: move
    //* 851983: attack
    //* 851984: attackground
    //* 851990: patrol
    //* 851993: holdposition
    //*         These are the UI orders that are passed to the system.
    //*
    //* >= 852055, <= 852762
    //*         These are all spell IDs from defend to incineratearrowoff with
    //*         a bit of leeway at the ends for orders with no strings.
    //*
    return id == 851971 or id == 851986 or id == 851983 or id == 851984 or id == 851990 or id == 851993 or (id >= 852055 and id <= 852762)
endfunction
private function LastOrderFilter takes unit u returns boolean
    //* Some criteria for whether or not a unit's last order should be given
    //*
    //* INSTANT type orders are excluded because generally, reissuing an instant
    //* order doesn't make sense. You can remove that check below if you'd like,
    //* though.
    //*
    //* The Type check is really just to ensure that no spell recursion can
    //* occur with IssueLastOrder. The problem with intercepting the spell cast
    //* event is that it happens after the order is 'caught' and registered to
    //* this system. Therefore, to just IssueLastOrder tells it to recast the
    //* spell! That's a problem, so we need a method to eliminate it.
    //*
    local integer id = GetUnitId(u)
    return u != null and GetWidgetLife(u) > 0.405 and Type[id] != ORDER_TYPE_IMMEDIATE
endfunction
private function SecondLastOrderFilter takes unit u returns boolean
    //* Same as above but with regard to the second last order issued
    local integer id = GetUnitId(u)
    return u != null and GetWidgetLife(u) > 0.405 and P_Type[id] != ORDER_TYPE_IMMEDIATE and P_Order[id] != Order[id]
endfunction
//**********************************************************

function IsLastOrderFinished takes unit u returns boolean
    return (GetUnitCurrentOrder(u) == 0 and Order[GetUnitId(u)] != 851972) or Flag[GetUnitId(u)]
endfunction

function IssueLastOrder takes unit u returns boolean
    local integer id = GetUnitId(u)
    local boolean b  = false
    if LastOrderFilter(u) and Order[id] != 0 and not Flag[id] then
        if Type[id] == ORDER_TYPE_TARGET then
            set b = IssueTargetOrderById(u, Order[id], Targ[id])
        elseif Type[id] == ORDER_TYPE_POINT then
            set b = IssuePointOrderById(u, Order[id], X[id], Y[id])
        elseif Type[id] == ORDER_TYPE_IMMEDIATE then
            set b = IssueImmediateOrderById(u, Order[id])
        endif
    endif
    return b
endfunction

function IssueSecondLastOrder takes unit u returns boolean
    //* This function has to exist because of spell recursion
    local integer id = GetUnitId(u)
    local boolean b  = false
    if SecondLastOrderFilter(u) and P_Order[id] != 0 and not P_Flag[id] then
        if P_Type[id] == ORDER_TYPE_TARGET then
            set b = IssueTargetOrderById(u, P_Order[id], P_Targ[id])
        elseif P_Type[id] == ORDER_TYPE_POINT then
            set b = IssuePointOrderById(u, P_Order[id], P_X[id], P_Y[id])
        elseif P_Type[id] == ORDER_TYPE_IMMEDIATE then
            set b = IssueImmediateOrderById(u, P_Order[id])
        endif
    endif
    return b
endfunction

function AbortOrder takes unit u returns boolean
    local boolean b = true
    if IsUnitPaused(u) then
        set b = false
    else
        call PauseUnit(u, true)
        call IssueImmediateOrder(u, "stop")
        call PauseUnit(u, false)
    endif
    return b
endfunction
//**********************************************************

private function Conditions takes nothing returns boolean
    return OrderExclusions(GetTriggerUnit(), GetIssuedOrderId())
endfunction

private function Actions takes nothing returns nothing
    local unit    u  = GetTriggerUnit()
    local integer id = GetUnitId(u)
   
    //* Store second to last order to eliminate spell recursion
    set P_Order[id]  = Order[id]
    set P_Targ[id]   = Targ[id]
    set P_Type[id]   = Type[id]
    set P_Flag[id]   = Flag[id]
    set P_X[id]      = X[id]
    set P_Y[id]      = Y[id]
   
    set Flag[id]     = false
    set Order[id]    = GetIssuedOrderId()
    if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER then
        set Targ[id] = GetOrderTarget()
        set Type[id] = ORDER_TYPE_TARGET
        set X[id]    = GetWidgetX(GetOrderTarget())
        set Y[id]    = GetWidgetY(GetOrderTarget())
    elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER then
        set Targ[id] = null
        set Type[id] = ORDER_TYPE_POINT
        set X[id]    = GetOrderPointX()
        set Y[id]    = GetOrderPointY()
    elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_ORDER then
        set Targ[id] = null
        set Type[id] = ORDER_TYPE_IMMEDIATE
        set X[id]    = GetUnitX(u)
        set Y[id]    = GetUnitY(u)
    debug else
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Order Doesn't Exist")
    endif
   
    set u = null
endfunction
//**********************************************************

private function SpellActions takes nothing returns nothing
    set Flag[GetUnitId(GetTriggerUnit())] = true
endfunction
//**********************************************************

private function Init takes nothing returns nothing
    local trigger trg = CreateTrigger()
    call TriggerAddAction(OrderTrg, function Actions)
    call TriggerAddCondition(OrderTrg, Condition(function Conditions))
    call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
    call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
    call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_ORDER)
   
    call TriggerAddAction(trg, function SpellActions)
    call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_EFFECT)
   
    set trg = null
endfunction
endlibrary
For future reference on inquiries like this: there is a wc3c discord server... but I can't link it here because invites are closed. If you would like to join I can ask Anopob to throw one your way. It's supposed to be for people who were part of wc3c not just like a Q&A discord, but probably nobody cares that much.

One more thing to note: this obviously doesn't track things like units auto-acquiring targets. So if you reissue an attack-move command to a different unit it's not likely to end up attacking the same targets. (Or at least not initially.)
 
Level 6
Joined
Dec 6, 2009
Messages
173
Thank you very much Pyrogasm, I belive this is exactly what I need. But I am sorry to say that I have been away for way to long so I don't remember how to do this anymore. Some years ago I had very good knowledge of vJass and used the JNPG editor alot, but I have never coded outside of WC3 so my memory is failing me. So I am doing my triggers in GUI now... There used to be highlight syntaxing and jass helper that fills in stuff in the old JNPG which was very helpful but now I don't seem to find that and TESH does not seem to work for me either since my world editor needs to be started from the Battle.net launcher.

I added the Unit Indexer to the Last Order since I did have that in an old map. Could you please guide me or help me do what I want in my trigger? The first part you can ignore, that works just fine. I want to take the order of the triggering unit that just leveld up and transfer that order to the newly created unit. I don't care about the auto-acquiring targets bit. I just don't want the player that is running away with low HP to die because a random nearby unit died and they leveld to 3 and their unit got replaced and now they are standing still.

  • Hero Level 3 Copy
    • Events
      • Unit - A unit Gains a level
    • Conditions
      • (Hero level of (Triggering unit)) Equal to 3
    • Actions
      • -------- ---------------------------------------- --------
      • -------- Set the location of the unit that will be replaced --------
      • -------- ---------------------------------------- --------
      • Set VariableSet LevelUp_Loc[(Player number of (Owner of (Triggering unit)))] = (Position of (Triggering unit))
      • -------- ---------------------------------------- --------
      • -------- Set the facing of the unit that will be replaced --------
      • -------- ---------------------------------------- --------
      • Set VariableSet Hero_Facing = (Facing of (Triggering unit))
      • -------- ---------------------------------------- --------
      • -------- Create the unit that will replace the current unit --------
      • -------- ---------------------------------------- --------
      • Unit - Create 1 Player_Level3_Unit[(Player number of (Owner of (Triggering unit)))] for (Owner of (Triggering unit)) at LevelUp_Loc[(Player number of (Owner of (Triggering unit)))] facing Hero_Facing degrees
      • Set VariableSet Player_Hero[(Player number of (Owner of (Triggering unit)))] = (Last created unit)
      • -------- ---------------------------------------- --------
      • -------- Select the new unit for the player and also set the life to be the same --------
      • -------- ---------------------------------------- --------
      • Selection - Select Player_Hero[(Player number of (Owner of (Triggering unit)))] for (Owner of (Triggering unit))
      • Unit - Set life of Player_Hero[(Player number of (Owner of (Triggering unit)))] to (Life of (Triggering unit))
      • -------- ---------------------------------------- --------
      • -------- Remove the triggering unit that is replaced and location leak --------
      • -------- ---------------------------------------- --------
      • Unit - Remove (Triggering unit) from the game
      • Custom script: call RemoveLocation(udg_LevelUp_Loc[GetConvertedPlayerId(GetOwningPlayer(GetTriggerUnit()))])
      • -------- ---------------------------------------- --------
      • -------- ------------------------------------EVERYTHING ABOVE IS FINE, BELOW IS WHERE I NEED HELP------------------------------------ --------
      • -------- ------------------------------------EVERYTHING ABOVE IS FINE, BELOW IS WHERE I NEED HELP------------------------------------ --------
      • -------- ------------------------------------EVERYTHING ABOVE IS FINE, BELOW IS WHERE I NEED HELP------------------------------------ --------
      • -------- ---------------------------------------- --------
      • -------- ---------------------------------------- --------
      • -------- Here I need help. So here I want to know the last order of the triggering unit and transfer this to the newly created unit --------
      • -------- I understand that my below triggers will not work but I sadly don't remember how to do this in custom script --------
      • -------- ---------------------------------------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Current order of (Triggering unit)) Equal to (Order(stop))
        • Then - Actions
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Current order of (Triggering unit)) Equal to (Order(attack))
            • Then - Actions
              • Unit - Order Player_Hero[(Player number of (Owner of (Triggering unit)))] to Attack (Targeted unit)
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Current order of (Triggering unit)) Equal to (Order(move))
                • Then - Actions
                  • Unit - Order Player_Hero[(Player number of (Owner of (Triggering unit)))] to Move To (Target point of issued order)
                • Else - Actions
 
Level 13
Joined
Feb 5, 2018
Messages
567
This might be a fetch, but have you tried just killing the triggering unit instead of removing it? I am unsure if you can collect data from units that are actually removed from the game. Seems to mike like the reference might be lost after the unit is removed.

EDIT: I actually think you should hide the unit first, then get the order of the unit and then kill/remove the unit afterwards.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,877
This might be a fetch, but have you tried just killing the triggering unit instead of removing it? I am unsure if you can collect data from units that are actually removed from the game. Seems to mike like the reference might be lost after the unit is removed.

EDIT: I actually think you should hide the unit first, then get the order of the unit and then kill/remove the unit afterwards.
The mentioned system tracks information about the Order. Without it, all you can do is cache the Order Id (string/integer).

So the target Unit/Point will be unavailable when trying to Re-Issue the Order.

It's not too difficult to track this information yourself (ie: Unit Indexing), but I imagine that system had a lot of extra functionality.
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
Hey @TheGoldenGoblin I got a response from the wc3c discord server and Deaod actually had the "arbitrary Nth most recent order reissue" version of LastOrder. Sounds like you didn't need it, but for future google searches that end up on this page I'm going to add it here. Note that it doesn't have a unit indexer as a requirement any more.

Praise be to Deaod.
JASS:
library LastOrder initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* LastOrder is a library that was designed to allow interfacing with the last
//* N orders any unit on your map has received. This library was also designed
//* to be used as a means to reissue lost orders to a unit either after
//* preventing a spell cast or in many other situations.
//*
//* There are two configuration constants for you to play with in using this
//* script. ORDERS_TO_HOLD is basically the size of the game's memory for each
//* individual unit. The larger the number is, the further back you can retrace
//* a unit's order list. Setting this value to 3 suffices for all actual
//* mechanics purposes. Raise it only if you have a specific application where
//* you need more. Lowering it to 2 covers most cases, but may miss a few, and
//* lowering it to 1 prevents you from adequately canceling spells and properly
//* reissuing previous orders. I recommend leaving it alone at 3. The MAX_ORDERS
//* constant is the number of orders that the system holds over all units at a
//* given time. If you are worried about running out, go ahead and increase it,
//* but be aware that it will use big arrays (which are slower) if you use a
//* number over 8191. Don't lower it under 8191, there's no reason to. Don't
//* change the non-private constants, there's no reason to.
//*
//*     function GetPastOrder takes unit u, integer whichOrder returns order
//*     function GetPastOrderId takes unit u, integer whichOrder returns integer
//*     function GetPastOrderString takes unit u, integer whichOrder returns string
//*     function GetPastOrderType takes unit u, integer whichOrder returns integer
//*     function GetPastOrderX takes unit u, integer whichOrder returns real
//*     function GetPastOrderY takes unit u, integer whichOrder returns real
//*     function GetPastOrderTarget takes unit u, integer whichOrder returns widget
//*
//* The above API is the main list of functions the user can use to interface
//* with past orders. If you want the immediate last order for a player, use 1
//* for the whichOrder integer. The 'order' returned by GetPastOrder is a struct
//* that contains all information of an issued order. If you want to interface
//* directly with the struct and skip all of the interfacing wrappers, you have
//* access to them via the following commands:
//*
//*     [unit]     .u       The unit being ordered.
//*     [integer]  .id      The order id of the past order.
//*     [integer]  .typ     The type of the past order. (See constants)
//*     [boolean]  .fin     A flag indicating whether the order was finished.
//*     [widget]   .tar     The target widget of the past order.
//*     [real]     .x       The x coordinate of the target point of the order.
//*     [real]     .y       The y coordinate of the target point of the order.
//*
//* There is also a sizable API for backwards compatibility with older versions
//* of the library. This API assumes that you're talking about a specific past
//* order, either the lastmost order or the second lastmost. In most cases,
//* these are still useful because they remove an argument from the call.
//*
//*     function GetLastOrder takes unit u returns order
//*     function GetLastOrderId takes unit u returns integer
//*     function GetLastOrderString takes unit u returns string
//*     function GetLastOrderType takes unit u returns integer
//*     function GetLastOrderX takes unit u returns real
//*     function GetLastOrderY takes unit u returns real
//*     function GetLastOrderTarget takes unit u returns widget
//*     function IsLastOrderFinished takes unit u returns boolean
//*
//* Besides being able to get information about all of the past orders a unit
//* has been issued, the most useful part of this system is actually reissuing
//* those orders as a means to fix lost orders or intercept and prevent spell
//* casting. The following API is then available to the user:
//*
//*     function IssuePastOrder takes unit u, integer whichOrder returns boolean
//*     function IssueLastOrder takes unit u returns boolean
//*     function IssueSecondLastOrder takes unit u returns boolean
//*     function AbortOrder takes unit u returns boolean
//*
//* If you want to reissue a past order for a given unit, IssuePastOrder is the
//* function that you'll want to use on a unit. To issue the last or second last
//* orders, there are functions for those as well. (They mostly exist for
//* backwards compatibility) AbortOrder is a means for anyone using this script
//* to stop a unit from running any given order that they happen to have at any
//* moment. AbortOrder is used in conjunction with a supplementary library,
//* AbortSpell, to seamlessly replicate WC3's spell error messages.
//*
//*     function IssueArbitraryOrder takes unit u, order o returns boolean
//*
//* IssueArbitraryOrder is special in that it ignores many of the normal checks
//* and automatically forces a unit to take on a specific order. This can be
//* convenient if you want to make one unit take on another unit's order, for
//* instance. Be forewarned that this overwrites whatever the unit was doing and
//* issues the order no matter what, so use only as needed.
//*
//* If you have any further questions regarding LastOrder or how to use it, feel
//* free to visit [url]www.wc3c.net[/url] and ask questions there. This library should only
//* ever be released at WC3C and at no other site. Please give credits if this
//* library finds its way into your maps, and otherwise thanks for reading!
//*
//* Enjoy!
//*
globals  
    //Order type variables
            constant integer ORDER_TYPE_TARGET    = 1
            constant integer ORDER_TYPE_POINT     = 2
            constant integer ORDER_TYPE_IMMEDIATE = 3
  
    //System constants
    private constant integer ORDERS_TO_HOLD       = 3    //How many past orders the
                                                         //system holds for each unit.
                                                         //Should be at least 2.
    private constant integer MAX_ORDERS           = 8191 //The max number of orders
                                                         //the system can maintain.
                                                         //Going over 8191 uses big
                                                         //arrays, which are slower
                                                         //than normal arrays.
endglobals

globals
    private hashtable ht = InitHashtable()
endglobals

struct order[MAX_ORDERS]
    unit    u
    integer id
    integer typ
    boolean fin
    widget  tar
    real    x
    real    y
    static method create takes unit ordered, integer ordid, integer ordtyp, widget target, real ordx, real ordy returns order
        local order   o   = order.allocate()
        local integer i   = ORDERS_TO_HOLD
        local integer hid = GetHandleId(ordered)
      
        set o.u   = ordered
        set o.id  = ordid
        set o.typ = ordtyp
        set o.fin = false
        set o.tar = target
        set o.x   = ordx
        set o.y   = ordy
        //Handle stored orders in the hashtable
        loop
            //We hold up to the constant ORDERS_TO_HOLD in the table
            exitwhen i == 1
            //Moves the N-1th order to the Nth slot, etc. except storing new last order
            if HaveSavedInteger(ht, hid, i-1) then
                if i == ORDERS_TO_HOLD and HaveSavedInteger(ht, hid, i) then
                    //Destroy lastmost order struct
                    call order.destroy(order(LoadInteger(ht, hid, i)))
                endif
                //Can only do this if the N-1th order exists
                call SaveInteger(ht, hid, i, LoadInteger(ht, hid, i-1))
            endif
            set i = i - 1
        endloop
        //Store the new order to the hashtable as 1th last order
        call SaveInteger(ht, hid, 1, integer(o))
        return o
    endmethod
endstruct

//******************************************************************************

//! textmacro LastOrderDebug takes ORDER, RETURN
    debug if $ORDER$ > ORDERS_TO_HOLD then
    debug     call BJDebugMsg(SCOPE_PREFIX+"Error: Order out of range")
    debug     return $RETURN$
    debug endif
    debug if not HaveSavedInteger(ht, GetHandleId(u), $ORDER$) then
    debug     call BJDebugMsg(SCOPE_PREFIX+"Error: The "+I2S($ORDER$)+"th order doesn't exist")
    debug     return $RETURN$
    debug endif
//! endtextmacro

function GetPastOrder takes unit u, integer whichOrder returns order
    //! runtextmacro LastOrderDebug("whichOrder", "0")
    return order(LoadInteger(ht, GetHandleId(u), whichOrder))
endfunction
function GetPastOrderId takes unit u, integer whichOrder returns integer
    //! runtextmacro LastOrderDebug("whichOrder", "0")
    return order(LoadInteger(ht, GetHandleId(u), whichOrder)).id
endfunction
function GetPastOrderString takes unit u, integer whichOrder returns string
    //! runtextmacro LastOrderDebug("whichOrder", "\"\"")
    return OrderId2String(order(LoadInteger(ht, GetHandleId(u), whichOrder)).id)
endfunction
function GetPastOrderType takes unit u, integer whichOrder returns integer
    //! runtextmacro LastOrderDebug("whichOrder", "0")
    return order(LoadInteger(ht, GetHandleId(u), whichOrder)).typ
endfunction
function GetPastOrderX takes unit u, integer whichOrder returns real
    //! runtextmacro LastOrderDebug("whichOrder", "0.")
    return order(LoadInteger(ht, GetHandleId(u), whichOrder)).x
endfunction
function GetPastOrderY takes unit u, integer whichOrder returns real
    //! runtextmacro LastOrderDebug("whichOrder", "0.")
    return order(LoadInteger(ht, GetHandleId(u), whichOrder)).y
endfunction
function GetPastOrderTarget takes unit u, integer whichOrder returns widget
    //! runtextmacro LastOrderDebug("whichOrder", "null")
    return order(LoadInteger(ht, GetHandleId(u), whichOrder)).tar
endfunction

//******************************************************************************

function GetLastOrder takes unit u returns order
    return GetPastOrder(u, 1)
endfunction
function GetLastOrderId takes unit u returns integer
    return GetPastOrderId(u, 1)
endfunction
function GetLastOrderString takes unit u returns string
    return GetPastOrderString(u, 1)
endfunction
function GetLastOrderType takes unit u returns integer
    return GetPastOrderType(u, 1)
endfunction
function GetLastOrderX takes unit u returns real
    return GetPastOrderX(u, 1)
endfunction
function GetLastOrderY takes unit u returns real
    return GetPastOrderY(u, 1)
endfunction
function GetLastOrderTarget takes unit u returns widget
    return GetPastOrderTarget(u, 1)
endfunction
function IsLastOrderFinished takes unit u returns boolean
    //! runtextmacro LastOrderDebug("1", "false")
    return GetUnitCurrentOrder(u) == 0 or order(LoadInteger(ht, GetHandleId(u), 1)).fin
endfunction

//******************************************************************************

private function OrderFilter takes unit u, integer id returns boolean
    //* Excludes specific orders or unit types from registering with the system
    //*
    //* 851972: stop
    //*         Stop is excluded from the system because it is the order that
    //*         tells a unit to do nothing. It should be ignored by the system.
    //*
    //* 851971: smart
    //* 851986: move
    //* 851983: attack
    //* 851984: attackground
    //* 851990: patrol
    //* 851993: holdposition
    //*         These are the UI orders that are passed to the system.
    //*
    //* 851973: stunned
    //*         This order is issued when a unit is stunned onto the stunner
    //*         It's ignored by the system, since you'd never want to reissue it
    //*
    //* >= 852055, <= 852762
    //*         These are all spell IDs from defend to incineratearrowoff with
    //*         a bit of leeway at the ends for orders with no strings.
    //*
    return id == 851971 or id == 851986 or id == 851983 or id == 851984 or id == 851990 or id == 851993 or (id >= 852055 and id <= 852762)
endfunction

private function IssuePastOrderFilter takes unit u, integer whichOrder returns boolean
    //* Some criteria for whether or not a unit's last order should be given
    //*
    //* INSTANT type orders are excluded because generally, reissuing an instant
    //* order doesn't make sense. You can remove that check below if you'd like,
    //* though.
    //*
    //* The Type check is really just to ensure that no spell recursion can
    //* occur with IssueLastOrder. The problem with intercepting the spell cast
    //* event is that it happens after the order is 'caught' and registered to
    //* this system. Therefore, to just IssueLastOrder tells it to recast the
    //* spell! That's a problem, so we need a method to eliminate it.
    //*
    return GetUnitTypeId(u) != 0 and not IsUnitType(u, UNIT_TYPE_DEAD) and GetPastOrderType(u, whichOrder) != 0 and GetPastOrderType(u, whichOrder) != ORDER_TYPE_IMMEDIATE
endfunction

//******************************************************************************

function IssuePastOrder takes unit u, integer whichOrder returns boolean
    local order o = GetPastOrder(u, whichOrder)
    if IssuePastOrderFilter(u, whichOrder) and not o.fin then
        if o.typ == ORDER_TYPE_TARGET then
            return IssueTargetOrderById(u, o.id, o.tar)
        elseif o.typ == ORDER_TYPE_POINT then
            if o.id == 851971 then
                //This adjusts for a bug in the point order's boolean return
                //when issuing a smart order
                call IssuePointOrderById(u, o.id, o.x, o.y)
                return true
            else
                return IssuePointOrderById(u, o.id, o.x, o.y)
            endif
        elseif o.typ == ORDER_TYPE_IMMEDIATE then
            return IssueImmediateOrderById(u, o.id)
        endif
    endif
    return false
endfunction

function IssueLastOrder takes unit u returns boolean
    return IssuePastOrder(u, 1)
endfunction

function IssueSecondLastOrder takes unit u returns boolean
    return IssuePastOrder(u, 2)
endfunction

function IssueArbitraryOrder takes unit u, order o returns boolean
    if o.typ == ORDER_TYPE_TARGET then
        return IssueTargetOrderById(u, o.id, o.tar)
    elseif o.typ == ORDER_TYPE_POINT then
        if o.id == 851971 then
            //This adjusts for a bug in the point order's boolean return
            //when issuing a smart order
            call IssuePointOrderById(u, o.id, o.x, o.y)
            return true
        else
            return IssuePointOrderById(u, o.id, o.x, o.y)
        endif
    elseif o.typ == ORDER_TYPE_IMMEDIATE then
        return IssueImmediateOrderById(u, o.id)
    endif
    return false
endfunction

function AbortOrder takes unit u returns boolean
    if IsUnitPaused(u) then
        return false
    else
        call PauseUnit(u, true)
        call IssueImmediateOrder(u, "stop")
        call PauseUnit(u, false)
    endif
    return true
endfunction

//**********************************************************

private function Conditions takes nothing returns boolean
    return OrderFilter(GetTriggerUnit(), GetIssuedOrderId())
endfunction

private function Actions takes nothing returns nothing
    local unit    u   = GetTriggerUnit()
    local widget  t   = GetOrderTarget()
    local integer oid = GetIssuedOrderId()
    local integer oty = 0
  
    if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER then
        call order.create(u, oid, ORDER_TYPE_TARGET, t, GetWidgetX(t), GetWidgetY(t))
    elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER then
        call order.create(u, oid, ORDER_TYPE_POINT, null, GetOrderPointX(), GetOrderPointY())
    elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_ORDER then
        call order.create(u, oid, ORDER_TYPE_IMMEDIATE, null, GetUnitX(u), GetUnitY(u))
    debug else
        debug call BJDebugMsg(SCOPE_PREFIX+"Error: Invalid order type")
    endif
  
    set u = null
    set t = null
endfunction

//**********************************************************

private function SpellActions takes nothing returns nothing
    local order o = GetPastOrder(GetTriggerUnit(), 1)
    set o.fin = true
endfunction

//**********************************************************

private function OnAdd takes nothing returns boolean
    local integer hid = GetHandleId(GetFilterUnit())
    local integer i   = ORDERS_TO_HOLD
  
    //Handle stored orders in the hashtable
    loop
        //We hold up to the constant ORDERS_TO_HOLD in the table
        exitwhen i == 0
        //If any of the N orders exist for this handle id, kill them all
        if HaveSavedInteger(ht, hid, i) then
            call order.destroy(order(LoadInteger(ht, hid, i)))
            call RemoveSavedInteger(ht, hid, i)
        endif
        set i = i - 1
    endloop
  
    return false
endfunction

//**********************************************************

private function Init takes nothing returns nothing
    local trigger trg = CreateTrigger()
    local region  re  = CreateRegion()
    local rect    m   = GetWorldBounds()
  
    //Main order catching trigger
    call TriggerAddAction(trg, function Actions)
    call TriggerAddCondition(trg, Condition(function Conditions))
    call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
    call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
    call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_ISSUED_ORDER)
  
    //Spell trigger to set a flag that indicates a spell order's completion
    set trg = CreateTrigger()
    call TriggerAddAction(trg, function SpellActions)
    call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_EFFECT)
  
    //Entering world trigger that clears old data from handle ids
    set trg = CreateTrigger()
    call RegionAddRect(re, m)
    call TriggerRegisterEnterRegion(trg, re, Condition(function OnAdd))
    call RemoveRect(m)
  
    set trg = null
    set re  = null
    set m   = null
endfunction
endlibrary
 
Top