1
0
forked from 0ad/0ad

Partial revert of d038b3c4f2 / REJECT_ORDER with FinishOrder()

Fixes d038b3c4f2, by partially reverting it.

If the new order is rejected, PushOrder() will call FinishOrder. If
there are no more orders on the queue, this calls SetNextState("idle"),
with the intention os switching the unit to IDLE. However, this only
works from within an FSM call, and thus does nothing if a new command
gets rejected.

The problem is that this.order/this.orderQueue is already replaced by
the point the order is rejected when called via ReplaceOrder. Ideally,
this would not happen (but doing so isn't trivial).

The current code avoids having 2 different ways to reject an order, thus
isn't a complete revert of d038b3c4f2. It triggers an IDLE re-entry that
wasn't there before if the unit is IDLE when it receives the rejected
order, which at the moment basically never happens.

Refs #5771 (reopened)

Reported by: gameboy
Comments by: Freagarach, Angen
Differential Revision: https://code.wildfiregames.com/D3618
This was SVN commit r24978.
This commit is contained in:
wraitii 2021-03-02 15:18:39 +00:00
parent e18001e897
commit b7ff2107ea

View File

@ -177,8 +177,8 @@ var g_LeaveFoundationRange = 4;
UnitAI.prototype.notifyToCheerInRange = 30;
// To reject an order, use 'return this.FinishOrder();'
const ACCEPT_ORDER = true;
const REJECT_ORDER = false;
// See ../helpers/FSM.js for some documentation of this FSM specification syntax
UnitAI.prototype.UnitFsmSpec = {
@ -248,7 +248,7 @@ UnitAI.prototype.UnitFsmSpec = {
// Called when being told to walk as part of a formation
"Order.FormationWalk": function(msg) {
if (!this.AbleToMove())
return REJECT_ORDER;
return this.FinishOrder();
if (this.CanPack())
{
@ -256,7 +256,7 @@ UnitAI.prototype.UnitFsmSpec = {
// In that case we don't actually want to move, as that would unpack us.
let cmpControllerAI = Engine.QueryInterface(this.GetFormationController(), IID_UnitAI);
if (cmpControllerAI.IsIdle())
return REJECT_ORDER;
return this.FinishOrder();
this.PushOrderFront("Pack", { "force": true });
}
else
@ -269,7 +269,7 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.LeaveFoundation": function(msg) {
if (!this.WillMoveFromFoundation(msg.data.target))
return REJECT_ORDER;
return this.FinishOrder();
this.order.data.min = g_LeaveFoundationRange;
this.SetNextState("INDIVIDUAL.WALKING");
return ACCEPT_ORDER;
@ -279,7 +279,7 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.LeaveFormation": function() {
if (!this.IsFormationMember())
return REJECT_ORDER;
return this.FinishOrder();
let cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
if (cmpFormation)
@ -300,7 +300,7 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.Walk": function(msg) {
if (!this.AbleToMove())
return REJECT_ORDER;
return this.FinishOrder();
if (this.CanPack())
{
@ -316,7 +316,7 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.WalkAndFight": function(msg) {
if (!this.AbleToMove())
return REJECT_ORDER;
return this.FinishOrder();
if (this.CanPack())
{
@ -333,7 +333,7 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.WalkToTarget": function(msg) {
if (!this.AbleToMove())
return REJECT_ORDER;
return this.FinishOrder();
if (this.CanPack())
{
@ -343,7 +343,7 @@ UnitAI.prototype.UnitFsmSpec = {
if (this.CheckRange(this.order.data))
return REJECT_ORDER;
return this.FinishOrder();
this.order.data.relaxed = true;
this.SetNextState("INDIVIDUAL.WALKING");
@ -353,13 +353,13 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.PickupUnit": function(msg) {
let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
if (!cmpGarrisonHolder || cmpGarrisonHolder.IsFull())
return REJECT_ORDER;
return this.FinishOrder();
let range = cmpGarrisonHolder.GetLoadingRange();
this.order.data.min = range.min;
this.order.data.max = range.max;
if (this.CheckRange(this.order.data))
return REJECT_ORDER;
return this.FinishOrder();
// Check if we need to move
// If the target can reach us and we are reasonably close, don't move.
@ -376,7 +376,7 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.Guard": function(msg) {
if (!this.AddGuard(this.order.data.target))
return REJECT_ORDER;
return this.FinishOrder();
if (!this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange))
this.SetNextState("INDIVIDUAL.GUARD.ESCORTING");
@ -393,7 +393,7 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.Attack": function(msg) {
let type = this.GetBestAttackAgainst(this.order.data.target, this.order.data.allowCapture);
if (!type)
return REJECT_ORDER;
return this.FinishOrder();
this.order.data.attackType = type;
@ -418,7 +418,7 @@ UnitAI.prototype.UnitFsmSpec = {
// If we're hunting, that's a special case where we should continue attacking our target.
if (this.GetStance().respondStandGround && !this.order.data.force && !this.order.data.hunting || !this.AbleToMove())
return REJECT_ORDER;
return this.FinishOrder();
if (this.CanPack())
{
@ -434,7 +434,7 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.Patrol": function(msg) {
if (!this.AbleToMove())
return REJECT_ORDER;
return this.FinishOrder();
if (this.CanPack())
{
@ -450,11 +450,11 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.Heal": function(msg) {
if (!this.TargetIsAlive(this.order.data.target))
return REJECT_ORDER;
return this.FinishOrder();
// Healers can't heal themselves.
if (this.order.data.target == this.entity)
return REJECT_ORDER;
return this.FinishOrder();
if (this.CheckTargetRange(this.order.data.target, IID_Heal))
{
@ -463,7 +463,7 @@ UnitAI.prototype.UnitFsmSpec = {
}
if (this.GetStance().respondStandGround && !this.order.data.force)
return REJECT_ORDER;
return this.FinishOrder();
this.SetNextState("INDIVIDUAL.HEAL.APPROACHING");
return ACCEPT_ORDER;
@ -502,7 +502,7 @@ UnitAI.prototype.UnitFsmSpec = {
{
// Oops, we can't attack at all - give up
// TODO: should do something so the player knows why this failed
return REJECT_ORDER;
return this.FinishOrder();
}
// The target was visible when this order was issued,
// but could now be invisible again.
@ -571,7 +571,7 @@ UnitAI.prototype.UnitFsmSpec = {
// We must check if this trader has both markets in case it was a back-to-work order.
let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader);
if (!cmpTrader || !cmpTrader.HasBothMarkets())
return REJECT_ORDER;
return this.FinishOrder();
this.waypoints = [];
this.SetNextState("TRADE.APPROACHINGMARKET");
@ -621,19 +621,19 @@ UnitAI.prototype.UnitFsmSpec = {
},
"Order.Cheer": function(msg) {
return REJECT_ORDER;
return this.FinishOrder();
},
"Order.Pack": function(msg) {
if (!this.CanPack())
return REJECT_ORDER;
return this.FinishOrder();
this.SetNextState("INDIVIDUAL.PACKING");
return ACCEPT_ORDER;
},
"Order.Unpack": function(msg) {
if (!this.CanUnpack())
return REJECT_ORDER;
return this.FinishOrder();
this.SetNextState("INDIVIDUAL.UNPACKING");
return ACCEPT_ORDER;
},
@ -642,7 +642,7 @@ UnitAI.prototype.UnitFsmSpec = {
// Overriden by the CHASING state.
// Can however happen outside of it when renaming...
// TODO: don't use an order for that behaviour.
return REJECT_ORDER;
return this.FinishOrder();
},
// States for the special entity representing a group of units moving in formation:
@ -669,21 +669,21 @@ UnitAI.prototype.UnitFsmSpec = {
// Only used by other orders to walk there in formation.
"Order.WalkToTargetRange": function(msg) {
if (this.CheckRange(this.order.data))
return REJECT_ORDER;
return this.FinishOrder();
this.SetNextState("WALKING");
return ACCEPT_ORDER;
},
"Order.WalkToTarget": function(msg) {
if (this.CheckRange(this.order.data))
return REJECT_ORDER;
return this.FinishOrder();
this.SetNextState("WALKING");
return ACCEPT_ORDER;
},
"Order.WalkToPointRange": function(msg) {
if (this.CheckRange(this.order.data))
return REJECT_ORDER;
return this.FinishOrder();
this.SetNextState("WALKING");
return ACCEPT_ORDER;
},
@ -726,7 +726,7 @@ UnitAI.prototype.UnitFsmSpec = {
this.SetNextState("COMBAT.APPROACHING");
return ACCEPT_ORDER;
}
return REJECT_ORDER;
return this.FinishOrder();
}
this.CallMemberFunction("Attack", [target, allowCapture, false]);
let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
@ -739,11 +739,11 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.Garrison": function(msg) {
if (!Engine.QueryInterface(msg.data.target, IID_GarrisonHolder))
return REJECT_ORDER;
return this.FinishOrder();
if (!this.CheckGarrisonRange(msg.data.target))
{
if (!this.CheckTargetVisible(msg.data.target))
return REJECT_ORDER;
return this.FinishOrder();
this.SetNextState("GARRISON.APPROACHING");
}
@ -786,7 +786,7 @@ UnitAI.prototype.UnitFsmSpec = {
if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10))
{
if (!this.CanGather(msg.data.target) || !this.CheckTargetVisible(msg.data.target))
return REJECT_ORDER;
return this.FinishOrder();
// TODO: Should we issue a gather-near-position order
// if the target isn't gatherable/doesn't exist anymore?
if (!msg.data.secondTry)
@ -795,7 +795,7 @@ UnitAI.prototype.UnitFsmSpec = {
this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 });
return ACCEPT_ORDER;
}
return REJECT_ORDER;
return this.FinishOrder();
}
this.CallMemberFunction("Gather", [msg.data.target, false]);
@ -824,7 +824,7 @@ UnitAI.prototype.UnitFsmSpec = {
if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10))
{
if (!this.TargetIsAlive(msg.data.target) || !this.CheckTargetVisible(msg.data.target))
return REJECT_ORDER;
return this.FinishOrder();
if (!msg.data.secondTry)
{
@ -832,7 +832,7 @@ UnitAI.prototype.UnitFsmSpec = {
this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 });
return ACCEPT_ORDER;
}
return REJECT_ORDER;
return this.FinishOrder();
}
this.CallMemberFunction("Heal", [msg.data.target, false]);
@ -846,7 +846,7 @@ UnitAI.prototype.UnitFsmSpec = {
if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10))
{
if (!this.TargetIsAlive(msg.data.target) || !this.CheckTargetVisible(msg.data.target))
return REJECT_ORDER;
return this.FinishOrder();
if (!msg.data.secondTry)
{
@ -854,7 +854,7 @@ UnitAI.prototype.UnitFsmSpec = {
this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 });
return ACCEPT_ORDER;
}
return REJECT_ORDER;
return this.FinishOrder();
}
this.CallMemberFunction("Repair", [msg.data.target, msg.data.autocontinue, false]);
@ -868,7 +868,7 @@ UnitAI.prototype.UnitFsmSpec = {
if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10))
{
if (!this.CheckTargetVisible(msg.data.target))
return REJECT_ORDER;
return this.FinishOrder();
if (!msg.data.secondTry)
{
@ -876,7 +876,7 @@ UnitAI.prototype.UnitFsmSpec = {
this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 });
return ACCEPT_ORDER;
}
return REJECT_ORDER;
return this.FinishOrder();
}
this.CallMemberFunction("ReturnResource", [msg.data.target, false]);
@ -1336,7 +1336,7 @@ UnitAI.prototype.UnitFsmSpec = {
// state forever and need to get out of foundations in that case)
"Order.LeaveFoundation": function(msg) {
if (!this.WillMoveFromFoundation(msg.data.target))
return REJECT_ORDER;
return this.FinishOrder();
this.order.data.min = g_LeaveFoundationRange;
this.SetNextState("WALKINGTOPOINT");
return ACCEPT_ORDER;
@ -1492,7 +1492,7 @@ UnitAI.prototype.UnitFsmSpec = {
"Order.Cheer": function() {
// Do not cheer if there is no cheering time and we are not idle yet.
if (!this.cheeringTime || !this.isIdle)
return REJECT_ORDER;
return this.FinishOrder();
this.SetNextState("CHEERING");
return ACCEPT_ORDER;
@ -1938,7 +1938,7 @@ UnitAI.prototype.UnitFsmSpec = {
"COMBAT": {
"Order.LeaveFoundation": function(msg) {
// Ignore the order as we're busy.
return REJECT_ORDER;
return this.FinishOrder();
},
"Attacked": function(msg) {
@ -2187,7 +2187,7 @@ UnitAI.prototype.UnitFsmSpec = {
"FINDINGNEWTARGET": {
"Order.Cheer": function() {
if (!this.cheeringTime)
return REJECT_ORDER;
return this.FinishOrder();
this.SetNextState("CHEERING");
return ACCEPT_ORDER;
@ -2238,7 +2238,7 @@ UnitAI.prototype.UnitFsmSpec = {
"CHASING": {
"Order.MoveToChasingPoint": function(msg) {
if (this.CheckPointRangeExplicit(msg.data.x, msg.data.z, 0, msg.data.max))
return REJECT_ORDER;
return this.FinishOrder();
this.order.data.relaxed = true;
this.StopTimer();
this.SetNextState("MOVINGTOPOINT");
@ -3751,11 +3751,13 @@ UnitAI.prototype.SetupAttackRangeQuery = function(enable = true)
//// FSM linkage functions ////
// Setting the next state to the current state will leave/re-enter the top-most substate.
// Must be called from inside the FSM.
UnitAI.prototype.SetNextState = function(state)
{
this.UnitFsm.SetNextState(this, state);
};
// Must be called from inside the FSM.
UnitAI.prototype.DeferMessage = function(msg)
{
this.UnitFsm.DeferMessage(this, msg);
@ -3777,6 +3779,7 @@ UnitAI.prototype.FsmStateNameChanged = function(state)
* next one (if any). Returns false and defaults to IDLE
* if there are no remaining orders or if the unit is not
* inWorld and not garrisoned (thus usually waiting to be destroyed).
* Must be called from inside the FSM.
*/
UnitAI.prototype.FinishOrder = function()
{
@ -3801,13 +3804,7 @@ UnitAI.prototype.FinishOrder = function()
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() });
// If the order was rejected then immediately take it off
// and process the remaining queue
if (ret === REJECT_ORDER)
return this.FinishOrder();
// Otherwise we've successfully processed a new order
return true;
return ret;
}
this.orderQueue = [];
@ -3849,14 +3846,9 @@ UnitAI.prototype.PushOrder = function(type, data)
if (this.orderQueue.length == 1)
{
this.order = order;
let ret = this.UnitFsm.ProcessMessage(this,
this.UnitFsm.ProcessMessage(this,
{ "type": "Order."+this.order.type, "data": this.order.data }
);
// If the order was rejected then immediately take it off
// and process the remaining queue
if (ret === REJECT_ORDER)
this.FinishOrder();
}
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() });
@ -3879,19 +3871,9 @@ UnitAI.prototype.PushOrderFront = function(type, data, ignorePacking = false)
{
this.orderQueue.unshift(order);
this.order = order;
let ret = this.UnitFsm.ProcessMessage(this,
this.UnitFsm.ProcessMessage(this,
{ "type": "Order."+this.order.type, "data": this.order.data }
);
// If the order was rejected then immediately take it off again;
// assume the previous active order is still valid (the short-lived
// new order hasn't changed state or anything) so we can carry on
// as if nothing had happened
if (ret === REJECT_ORDER)
{
this.orderQueue.shift();
this.order = this.orderQueue[0];
}
}
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() });