Rising_Dusk |
01-19-2009 08:39 AM |
LastOrder
1 Attachment(s)
LastOrder Library
Background:
This library of code allows lots of cool interfacing with the last orders of a unit. The main point of the library is to maintain an organized list of up to N of any given unit's last issued orders. The value of this is that you can recall those orders at any time and reissue them if the need arises. (Such as if those orders were lost to the unit by a pause/stop/pause or something similar) Thanks to ToukoAozaki for giving me the motivation to package and release it.
Requirements:Code:
 Library: library LastOrder initializer Init
globals
constant integer ORDER_TYPE_TARGET = 1
constant integer ORDER_TYPE_POINT = 2
constant integer ORDER_TYPE_IMMEDIATE = 3
private constant integer ORDERS_TO_HOLD = 3
private constant integer MAX_ORDERS = 8191
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
loop
exitwhen i == 1
if HaveSavedInteger(ht, hid, i-1) then
if i == ORDERS_TO_HOLD and HaveSavedInteger(ht, hid, i) then
call order.destroy(order(LoadInteger(ht, hid, i)))
endif
call SaveInteger(ht, hid, i, LoadInteger(ht, hid, i-1))
endif
set i = i - 1
endloop
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
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
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
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
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
loop
exitwhen i == 0
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()
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)
set trg = CreateTrigger()
call TriggerAddAction(trg, function SpellActions)
call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_EFFECT)
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
Function List:
This library gives the following interfacing options with last orders. It contains the following functions for use with interfacing last orders given to a unit. - 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
- ----
- 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
- ----
- function IssuePastOrder takes unit u, integer whichOrder returns boolean
- function IssueLastOrder takes unit u returns boolean
- function IssueSecondLastOrder takes unit u returns boolean
- function IssueArbitraryOrder takes unit u, order o returns boolean
- function AbortOrder takes unit u returns boolean
In the documentation, I explain a lot about why a user wants to store the last 3 orders a unit has received. The second and third orders are particularly useful for use with spells that target either units or locations and need to be intercepted. These spells are 'interrupted' on the SPELL_CAST event to prevent cooldown/mana cost, but that fires after the order events. This means that in the case where you want to reissue the order before you cast storm bolt, you need to now issue your second to last order. If you issue the very last order, you run into spell recursion, which is a whole bunch of nastiness that no one wants to look at.
Attached to this post is a demo map showing you how the system works. You can hit escape to view the last order details for your blademaster at any given time. Additionally, you can issue orders around the map and then use the "Demo Spell" with the cancel icon. That spell does nothing, but it is intercepted by an example code in the demo map and takes advantage of the following AbortSpell library, which was designed to be used with the LastOrder library. This should be a sufficient example for anyone to figure out how to use it in their maps to its fullest extent.
_______________________________________________________________________
AbortSpell Supplementary Library
Requirements: Supplementary Library: library AbortSpell requires LastOrder, SimError
function AbortSpell takes unit u, string msg, string key returns boolean
local boolean b = false
local real a = 0.
if AbortOrder(u) then
call SimError(GetOwningPlayer(u), "\n"+msg)
if GetLocalPlayer() == GetOwningPlayer(u) and StringLength(key) == 1 and key != " " then
call ForceUIKey(key)
endif
set b = IssueSecondLastOrder(u)
if not b and GetPastOrder(u, 2) != 0 then
set a = GetUnitFacing(u)*bj_DEGTORAD
call IssuePointOrderById(u, 851971, GetUnitX(u)+0.01*Cos(a), GetUnitY(u)+0.01*Sin(a))
endif
endif
return b
endfunction
endlibrary
Function List:
This library gives the following function to the user. - function AbortSpell takes unit u, string msg, string key returns boolean
This expands upon SimError to fully simulate WC3 spellcasting errors. When you try to cast a spell in WC3 and you can't, you keep following your last order and the AOE indicator and so forth don't go away. If you used only the normal SimError, you couldn't fully simulate this special case, but with AbortSpell you can. AbortSpell also takes a 'key' argument, which corresponds to the hotkey associated with your spell.
Thanks guys, comments are welcome!
|