|
|
|
|
JASS Functions Approved JASS functions will be located here.
Remember to submit your own resources to the submission forum. |
 |
|
01-28-2010, 09:50 PM
|
#1 (permalink)
|
|
He has beautiful eyes...
Join Date: Aug 2008
Posts: 2,062
|
[vJass] PreventOrders
A simple script I wrote because I needed it in a spell, this simply provides a way of preventing specific orders. It also provides a CancelOrders function which can be used externally and simply clears any orders the unit has, and works in response to SpellCast or IssuedOrder events, unlike simply issuing a "stop" order. Without any further ado, here's the code:
Requires:
AutoIndex by grim001
Jass:
/***********************************************************
* PreventOrders
* by Element of Water
************************************************************
* Requirements:
* - AutoIndex by grim001
* - JassHelper by Vexorian (for vJass)
************************************************************
* What is it?
*
* PreventOrders is a simple library which provides a few
* useful functions to do with the preventing and
* cancelling of certain orders. Whenever an order is
* "prevented", the unit will carry on with its previous
* order as usual (but if it has multiple orders queued
* up, it will not continue with those).
*
* CancelOrders immediately cancels a unit's orders. It
* is different to simply issuing a "stop" order, because
* it works even in response to SpellCast or IssuedOrder
* events. It takes just a single unit parameter. Call
* it like this:
*
* call CancelOrders(myUnit)
*
* It's that simple!
*
* PreventCurrentOrder immediately stops the unit from
* performing its current order and returns it to its
* previous order. The syntax for calling it is as follows:
*
* call PreventCurrentOrder(myUnit)
*
* Simplez!
*
* PreventOrders permanently stops a unit from responding
* to immediate, point and/or target orders. Call it like
* this:
*
* call PreventOrders(myUnit, immediate, point, target)
*
* immediate, point and target are booleans. These should
* be true if you want to disable immediate, point or
* target orders respectively. If you want to re-enable
* a type of order, simply call the function again with
* the desired parameters as false, and the unit will be
* able to recieve orders again
*
* PreventSpecificOrder permanently stops a unit from
* responding to a specific order, such as "move", "smart",
* "channel", "acidbomb", etc. Call it like this:
*
* call PreventSpecificOrder(myUnit, order, prevent)
*
* order is the order string which determines which order
* is prevented, and prevent is a boolean. prevent should
* be true if you want to disable the order, and false if
* you want to re-enable it. Also, if it is false it will
* bypass the blocking of all orders just for this order.
* You can call ClearSpecificOrderPrevention(myUnit, order)
* to reset for one specific order, or call
* ClearAllSpecificOrderPrevention(myUnit) to reset all
* orders for that unit.
*
* There is a variation of this function,
* PreventSpecificOrderById, which takes an integer instead
* of a string for if you prefer to work with order ids.
* You may also use ClearSpecificOrderPreventionById.
*
* AllowNextOrder allows a unit to respond to the next order
* it is issued, whatever that may be. Even if the order
* would not be blocked anyway, this is cleared immediately
* after the next order issued to the unit. The syntax to
* call it is as follows:
*
* call AllowNextOrder(myUnit)
*
* This function is useful for when you want to control the
* unit through triggers, but make it unresponsive to
* player control. The PreventOrdersHooks add-on utilises
* this function to allow all triggered orders via hooks.
***********************************************************/
library PreventOrders initializer Init requires AutoIndex
private keyword Data
globals
private timer Tim = CreateTimer()
private Data array CancelStack
private integer CancelCount = 0
private hashtable Hash = InitHashtable()
private constant integer ORDER_STOP = 851972
// Purely for debugging purposes, this displays when an order is prevented
private constant boolean DISPLAY_PREVENTED_ORDERS = false
// Set this to true to make units keep their old orders rather than simply stopping when an order is prevented
private constant boolean RETAIN_PREVIOUS_ORDERS = false
endglobals
private function DebugMsg takes string unitName, string orderType, string orderString returns nothing
debug static if DISPLAY_PREVENTED_ORDERS then
debug call BJDebugMsg("PreventOrders: Prevented " + unitName + " from being issued a" + orderType + " " + orderString + " order")
debug endif
endfunction
private struct Data extends array
static constant integer IMMEDIATE = 1
static constant integer POINT = 2
static constant integer TARGET = 3
unit u
boolean i
boolean p
boolean t
boolean doNext
integer lastOrderType
integer lastOrderId
widget lastOrderTargetW
real lastOrderTargetX
real lastOrderTargetY
boolean cancel
method lastImmediateOrder takes integer id returns nothing
set lastOrderType = IMMEDIATE
set lastOrderId = id
endmethod
method lastPointOrder takes integer id, real x, real y returns nothing
set lastOrderType = POINT
set lastOrderId = id
set lastOrderTargetX = x
set lastOrderTargetY = y
endmethod
method lastTargetOrder takes integer id, widget target returns nothing
set lastOrderType = TARGET
set lastOrderId = id
set lastOrderTargetW = target
endmethod
method doLastOrder takes nothing returns nothing
if cancel then
set doNext = true
call IssueImmediateOrderById(u, ORDER_STOP)
else
if lastOrderType == IMMEDIATE then
set doNext = true
call IssueImmediateOrderById(u, lastOrderId)
elseif lastOrderType == POINT then
set doNext = true
call IssuePointOrderById(u, lastOrderId, lastOrderTargetX, lastOrderTargetY)
elseif lastOrderType == TARGET then
set doNext = true
call IssueTargetOrderById(u, lastOrderId, lastOrderTargetW)
else
set doNext = true
call IssueImmediateOrderById(u, ORDER_STOP)
endif
endif
endmethod
method assign takes unit whichUnit returns nothing
set u = whichUnit
endmethod
method destroy takes nothing returns nothing
call FlushChildHashtable(Hash, this)
set u = null
set i = false
set p = false
set t = false
set doNext = false
set lastOrderType = 0
set cancel = false
endmethod
endstruct
private function Cancel takes nothing returns nothing
loop
set CancelCount = CancelCount - 1
static if RETAIN_PREVIOUS_ORDERS then
call CancelStack[CancelCount].doLastOrder()
else
set CancelStack[CancelCount].doNext = true
call IssueImmediateOrderById(CancelStack[CancelCount].u, ORDER_STOP)
endif
exitwhen CancelCount == 0
endloop
endfunction
private function PreventCurrentOrderFromData takes Data d returns nothing
if CancelCount == 0 then
call TimerStart(Tim, 0., false, function Cancel)
endif
set CancelStack[CancelCount] = d
set CancelCount = CancelCount + 1
endfunction
function PreventCurrentOrder takes unit u returns nothing
call PreventCurrentOrderFromData(Data[GetUnitId(u)])
endfunction
function CancelOrders takes unit u returns nothing
local Data d = Data[GetUnitId(u)]
static if RETAIN_PREVIOUS_ORDERS then
set d.cancel = true
endif
call PreventCurrentOrderFromData(d)
endfunction
function PreventOrders takes unit u, boolean i, boolean p, boolean t returns nothing
local Data d = Data[GetUnitId(u)]
if i or p or t then
set d.i = i
set d.p = p
set d.t = t
endif
endfunction
function PreventSpecificOrderById takes unit u, integer orderId, boolean prevent returns nothing
local Data d = Data[GetUnitId(u)]
if orderId != 0 then
if prevent then
call SaveBoolean(Hash, d, orderId, true)
elseif d != 0 then
call SaveBoolean(Hash, d, orderId, false)
endif
endif
endfunction
function PreventSpecificOrder takes unit u, string order, boolean prevent returns nothing
call PreventSpecificOrderById(u, OrderId(order), prevent)
endfunction
function ClearSpecificOrderPreventionById takes unit u, integer orderId returns nothing
local Data d = Data[GetUnitId(u)]
if orderId != 0 and orderId != ORDER_STOP then
call RemoveSavedBoolean(Hash, d, orderId)
endif
endfunction
function ClearSpecificOrderPrevention takes unit u, string order returns nothing
call ClearSpecificOrderPreventionById(u, OrderId(order))
endfunction
function ClearAllSpecificOrderPrevention takes unit u returns nothing
local Data d = Data[GetUnitId(u)]
call FlushChildHashtable(Hash, d)
endfunction
function AllowNextOrder takes unit u returns nothing
local Data d = Data[GetUnitId(u)]
set d.doNext = true
endfunction
private function ImmediateOrder takes nothing returns boolean
local unit u = GetTriggerUnit()
local Data d = Data[GetUnitId(u)]
local integer i = GetIssuedOrderId()
if d.doNext then
set d.doNext = false
call d.lastImmediateOrder(i)
else
if d.i then
if (not HaveSavedBoolean(Hash, d, i)) or (LoadBoolean(Hash, d, i)) then
call PreventCurrentOrderFromData(d)
debug call DebugMsg(GetUnitName(u), "n immediate", OrderId2String(i))
else
static if RETAIN_PREVIOUS_ORDERS then
call d.lastImmediateOrder(i)
endif
endif
elseif LoadBoolean(Hash, d, i) then
call PreventCurrentOrderFromData(d)
debug call DebugMsg(GetUnitName(u), "n immediate", OrderId2String(i))
else
static if RETAIN_PREVIOUS_ORDERS then
call d.lastImmediateOrder(i)
endif
endif
endif
set u = null
return false
endfunction
private function PointOrder takes nothing returns boolean
local unit u = GetTriggerUnit()
local Data d = Data[GetUnitId(u)]
local integer i = GetIssuedOrderId()
if d.doNext then
set d.doNext = false
call d.lastPointOrder(i, GetOrderPointX(), GetOrderPointY())
else
if d.p then
if (not HaveSavedBoolean(Hash, d, i)) or (LoadBoolean(Hash, d, i)) then
call PreventCurrentOrderFromData(d)
debug call DebugMsg(GetUnitName(u), " point", OrderId2String(i))
else
static if RETAIN_PREVIOUS_ORDERS then
call d.lastPointOrder(i, GetOrderPointX(), GetOrderPointY())
endif
endif
elseif LoadBoolean(Hash, d, i) then
call PreventCurrentOrderFromData(d)
debug call DebugMsg(GetUnitName(u), " point", OrderId2String(i))
else
static if RETAIN_PREVIOUS_ORDERS then
call d.lastPointOrder(i, GetOrderPointX(), GetOrderPointY())
endif
endif
endif
set u = null
return false
endfunction
private function TargetOrder takes nothing returns boolean
local unit u = GetTriggerUnit()
local Data d = Data[GetUnitId(u)]
local integer i = GetIssuedOrderId()
if d.doNext then
set d.doNext = false
call d.lastTargetOrder(i, GetOrderTarget())
else
if d.t then
if (not HaveSavedBoolean(Hash, d, i)) or (LoadBoolean(Hash, d, i)) then
call PreventCurrentOrderFromData(d)
debug call DebugMsg(GetUnitName(u), " target", OrderId2String(i))
else
static if RETAIN_PREVIOUS_ORDERS then
call d.lastTargetOrder(i, GetOrderTarget())
endif
endif
elseif LoadBoolean(Hash, d, i) then
call PreventCurrentOrderFromData(d)
debug call DebugMsg(GetUnitName(u), " target", OrderId2String(i))
else
static if RETAIN_PREVIOUS_ORDERS then
call d.lastTargetOrder(i, GetOrderTarget())
endif
endif
endif
set u = null
return false
endfunction
private function UnitIndexed takes unit u returns nothing
local Data d = Data[GetUnitId(u)]
call d.assign(u)
endfunction
private function UnitDeindexed takes unit u returns nothing
local Data d = Data[GetUnitId(u)]
call d.destroy()
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_ORDER)
call TriggerAddCondition(t, Condition(function ImmediateOrder))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerAddCondition(t, Condition(function PointOrder))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
call TriggerAddCondition(t, Condition(function TargetOrder))
call OnUnitIndexed(UnitIndexed)
call OnUnitDeindexed(UnitDeindexed)
endfunction
endlibrary
The following is an add-on for PreventOrders which uses hooks to make the system ignore any triggered issued orders.
Jass:
/*********************************************************
* PreventOrdersHooks *
* by Element of Water *
**********************************************************
* Requirements: *
* - PreventOrders by Element of Water *
* - JassHelper by Vexorian (for Zinc) *
**********************************************************
* What is it? *
* *
* PreventOrdersHooks is a simple add-on library for *
* PreventOrders which makes every order issued by *
* triggers bypass the system. It does this at the cost *
* a minor efficiency hit for EVERY call in GUI, JASS, *
* whatever, to ANY function which issues an order to a *
* unit. Don't use it if you issue lots of orders to *
* units and notice lag. This library is completely *
* automatic, so you don't have to worry about calling *
* any functions. *
*********************************************************/
library PreventOrdersHooks requires PreventOrders
globals
private constant integer ORDER_STOP = 851972
endglobals
function IssueBuildOrder_hook takes unit u, string s, real x, real y returns nothing
call AllowNextOrder(u)
endfunction
function IssueBuildOrderById_hook takes unit u, integer i, real x, real y returns nothing
call AllowNextOrder(u)
endfunction
function IssueBuildOrderByIdLocBJ_hook takes unit u, integer i, location l returns nothing
call AllowNextOrder(u)
endfunction
function IssueHauntOrderAtLocBJ_hook takes unit u, location l returns nothing
call AllowNextOrder(u)
endfunction
function IssueImmediateOrder_hook takes unit u, string s returns nothing
call AllowNextOrder(u)
endfunction
function IssueImmediateOrderById_hook takes unit u, integer i returns nothing
if i != ORDER_STOP then
call AllowNextOrder(u)
endif
endfunction
function IssueInstantPointOrder_hook takes unit u, string s, real x, real y, widget w returns nothing
call AllowNextOrder(u)
endfunction
function IssueInstantPointOrderById_hook takes unit u, integer i, real x, real y, widget w returns nothing
call AllowNextOrder(u)
endfunction
function IssueInstantTargetOrder_hook takes unit u, string s, widget w, widget w2 returns nothing
call AllowNextOrder(u)
endfunction
function IssueInstantTargetOrderById_hook takes unit u, integer i, widget w, widget w2 returns nothing
call AllowNextOrder(u)
endfunction
function IssueNeutralImmediateOrder_hook takes player p, unit u, string s returns nothing
call AllowNextOrder(u)
endfunction
function IssueNeutralImmediateOrderById_hook takes player p, unit u, integer i returns nothing
call AllowNextOrder(u)
endfunction
function IssueNeutralPointOrder_hook takes player p, unit u, string s, real x, real y returns nothing
call AllowNextOrder(u)
endfunction
function IssueNeutralPointOrderById_hook takes player p, unit u, integer i, real x, real y returns nothing
call AllowNextOrder(u)
endfunction
function IssueNeutralTargetOrder_hook takes player p, unit u, string s, widget w returns nothing
call AllowNextOrder(u)
endfunction
function IssueNeutralTargetOrderById_hook takes player p, unit u, integer i, widget w returns nothing
call AllowNextOrder(u)
endfunction
function IssuePointOrderLoc_hook takes unit u, string s, location l returns nothing
call AllowNextOrder(u)
endfunction
function IssueTargetOrder_hook takes unit u, string s, widget w returns nothing
call AllowNextOrder(u)
endfunction
function IssueTargetOrderById_hook takes unit u, integer i, widget w returns nothing
call AllowNextOrder(u)
endfunction
hook IssueBuildOrder IssueBuildOrder_hook
hook IssueBuildOrderById IssueBuildOrderById_hook
hook IssueBuildOrderByIdLocBJ IssueBuildOrderByIdLocBJ_hook
hook IssueHauntOrderAtLocBJ IssueHauntOrderAtLocBJ_hook
hook IssueImmediateOrder IssueImmediateOrder_hook
hook IssueImmediateOrderBJ IssueImmediateOrder_hook
hook IssueImmediateOrderById IssueImmediateOrderById_hook
hook IssueInstantPointOrder IssueInstantPointOrder_hook
hook IssueInstantPointOrderById IssueInstantPointOrderById_hook
hook IssueInstantTargetOrder IssueInstantTargetOrder_hook
hook IssueInstantTargetOrderById IssueInstantTargetOrderById_hook
hook IssueNeutralImmediateOrder IssueNeutralImmediateOrder_hook
hook IssueNeutralImmediateOrderById IssueNeutralImmediateOrderById_hook
hook IssueNeutralPointOrder IssueNeutralPointOrder_hook
hook IssueNeutralPointOrderById IssueNeutralPointOrderById_hook
hook IssueNeutralTargetOrder IssueNeutralTargetOrder_hook
hook IssueNeutralTargetOrderById IssueNeutralTargetOrderById_hook
hook IssuePointOrder IssueBuildOrder_hook
hook IssuePointOrderById IssueBuildOrderById_hook
hook IssuePointOrderByIdLoc IssueBuildOrderByIdLocBJ_hook
hook IssuePointOrderLoc IssuePointOrderLoc_hook
hook IssuePointOrderLocBJ IssuePointOrderLoc_hook
hook IssueTargetDestructableOrder IssueTargetOrder_hook
hook IssueTargetItemOrder IssueTargetOrder_hook
hook IssueTargetOrder IssueTargetOrder_hook
hook IssueTargetOrderBJ IssueTargetOrder_hook
hook IssueTargetOrderById IssueTargetOrderById_hook
hook IssueTrainOrderByIdBJ IssueImmediateOrderById_hook
hook IssueUpgradeOrderByIdBJ IssueImmediateOrderById_hook
endlibrary
Last edited by Element of Water; 02-11-2010 at 06:43 PM.
|
|
|
01-29-2010, 04:41 AM
|
#2 (permalink)
|
|
The Untitled... wait...
Join Date: Nov 2008
Posts: 447
|
"It is different to simply issuing a "stop" order" is incorrect grammar.
I would recommend making a wrapper error function so as to increase readability of errors, the color tags are mildly annoying and add line length. JassHelper will inline it, so no need for the eyesore.
private function Cancel takes nothing returns nothing call IssueImmediateOrderById (U, ORDER_STOP ) endfunction function CancelOrders takes unit u returns nothing set U = u call TimerStart (Tim, 0., false, function Cancel ) endfunction
Just a curiosity, why? I never noticed that you needed to wait a split second before issuing a stop order for it to work, it's always worked for me.
Under "PointOrder", I notice random spaces between the variables names for no particular reason, unless you like the syntax that way, but under "Immediate order", it's not like that. Your globals block is also out of whack with spaces. Nitpicking though, sorry.
You are using conditions intentionally instead of actions on triggers. Is there some sort of speed boost I'm unaware of? Seems to me like it would be more inefficient than actions since it has to return something and this forces the engine to read a return value from an interpreted language into C++, as opposed to an action which returns nothing.
You are doing geneirc unit events rather than dynamically adding units into the trigger as the function to prevent their orders is called. Minor inefficiency, although I've heard people yell about dynamic triggers corrupting things and whatnot. Never happened to me though so I wouldn't worry.
I'm sure it could be useful for many things, such as an alternative to pausing a unit and forcing a players unit to do things while still selected by the player. Oh, wait, nevermind, just looked at it again and it can't do that. It would work until the user tried to do something. >.>.
Maybe you could add that functionality?
|
|
|
01-29-2010, 04:12 PM
|
#3 (permalink)
|
|
He has beautiful eyes...
Join Date: Aug 2008
Posts: 2,062
|
1. I'm a Brit. I know more than anyone from any other country what is correct grammar and what is not.
2. Will do
3. So it works directly in response to orders or cast event. If you just issue a stop order in response to an order event directly, it won't work, but my method will.
4. Meh, makes it more readable IMO
5. Actions start a new thread, conditions don't. Conditions are faster.
6. Generic unit events are completely safe and the efficiency hit is only incredibly minor.
7. I'll try. Not sure how though.
|
|
|
01-29-2010, 08:22 PM
|
#4 (permalink)
|
|
He has beautiful eyes...
Join Date: Aug 2008
Posts: 2,062
|
HUGE update...
1. Made wrapper error function
2. Made it able to block specific orders
3. Made it possible to order a unit with triggers even when orders have been blocked, via AllowNextOrder, or PreventOrdersHooks
4. Added clean-up to prevent leaks when units affected by the system die or are removed.
|
|
|
01-29-2010, 10:31 PM
|
#5 (permalink)
|
|
The Untitled... wait...
Join Date: Nov 2008
Posts: 447
|
One thing I completely forgot to mention: if units in a loop are mass issued orders and they are all blocked for whatever reason, or StopOrder is used multiple times with no delays, your single global timer will likely fail, as 0.0 second timers do in fact create threads and dont run instantly in the current function - which is why it works to cancel orders, apparently.
Could be wrong on that, but as far as I know, that's how it works.
Great job regardless, I can definitely see some uses for this.
|
|
|
01-29-2010, 10:48 PM
|
#6 (permalink)
|
|
He has beautiful eyes...
Join Date: Aug 2008
Posts: 2,062
|
As im currently posting from mu iPod touch I cant test that but ill run some tests as soon as i get a chance
if it is a problem the only solution would be to use timerutils and sacrifice a tiny bit of efficiency
|
|
|
01-31-2010, 02:39 AM
|
#7 (permalink)
|
|
Blasphemy!
Join Date: Oct 2008
Posts: 174
|
You don't need to issue a stop after 0 seconds. Merely pause, issue the stop order, and unpause.
|
|
|
01-31-2010, 09:05 AM
|
#8 (permalink)
|
|
He has beautiful eyes...
Join Date: Aug 2008
Posts: 2,062
|
I tried that - it doesn't work. I think that and SetUnitPosition stopped working in terms of instantly cancelling orders after 1.24d.
|
|
|
01-31-2010, 10:08 PM
|
#9 (permalink)
|
|
Blasphemy!
Join Date: Oct 2008
Posts: 174
|
So it does. Blizzard seems to be breaking things left and right.
Oh, and you could use a stack instead of multiple timers or an unsafe global.
EDIT:
Now for deeper code reading.
The PointOrder function leaks a unit variable. Same with TargetOrder and UnitDeath.
You should probably put 'debug' before debug messages. More efficient that way.
Why not make Data an array and use unit ids as indices instead of the array you're using now?
Last edited by azlier; 01-31-2010 at 11:20 PM.
|
|
|
02-01-2010, 06:51 AM
|
#10 (permalink)
|
|
He has beautiful eyes...
Join Date: Aug 2008
Posts: 2,062
|
1. I'll try to come up with something for the timers...
2. I'll fix the unit leaks
3. Yeah, I just forgot for some of them.
4. You know, that's a very good point. Why didn't I think of that?
|
|
|
02-01-2010, 07:43 AM
|
#11 (permalink)
|
|
He has beautiful eyes...
Join Date: Aug 2008
Posts: 2,062
|
*UPDATE*
- Added a way of allowing a specific order while blocking all other orders
- Fixed all the stuff Azlier said
- Added a few new functions (see documentation)
|
|
|
02-01-2010, 05:56 PM
|
#12 (permalink)
|
|
Blasphemy!
Join Date: Oct 2008
Posts: 174
|
The only thing wrong I see at this point is the destroy method.
method destroy takes nothing returns nothing local integer id = GetUnitId(u)
set u = null set i = false set p = false set t = false
set doNext = false
call FlushChildHashtable(Hash, id) endmethod
This would be better:
method destroy takes nothing returns nothing call FlushChildHashtable(Hash, this)
set u = null set i = false set p = false set t = false
set doNext = false
endmethod
|
|
|
02-01-2010, 05:58 PM
|
#13 (permalink)
|
|
He has beautiful eyes...
Join Date: Aug 2008
Posts: 2,062
|
Fixed.
|
|
|
02-01-2010, 06:04 PM
|
#14 (permalink)
|
|
Blasphemy!
Join Date: Oct 2008
Posts: 174
|
Bah, I didn't see this before. In the PreventOrders method, the local integer j is only used once and should be inlined.
Also, what happens if I try to register Stop as an order that should be prevented?
|
|
|
02-01-2010, 06:07 PM
|
#15 (permalink)
|
|
He has beautiful eyes...
Join Date: Aug 2008
Posts: 2,062
|
1. Will fix
2. if j != ORDER_STOP then -- It wouldn't attempt to be prevented due to this line... but I guess I can give a debug error if you do try to prevent it for any reason.
|
|
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
|
|
|
|
|
|
|
|
|