Index: binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- binaries/data/mods/public/simulation/components/UnitAI.js +++ binaries/data/mods/public/simulation/components/UnitAI.js @@ -132,6 +132,10 @@ } }; +// These orders always require a packed unit, so if a unit that is unpacking is given one of these orders, +// it can immediately cancel unpacking. +var g_OrdersCancelUnpacking = new Set(["FormationWalk", "Walk", "WalkAndFight", "WalkToTarget", "Patrol", "Garrison"]); + // See ../helpers/FSM.js for some documentation of this FSM specification syntax UnitAI.prototype.UnitFsmSpec = { @@ -424,6 +428,10 @@ return; } + // Cancel any current packing order + if (!this.EnsureCorrectPackStateForAttack(true)) + return; + if (this.IsAnimal()) this.SetNextState("ANIMAL.COMBAT.ATTACKING"); else @@ -448,6 +456,10 @@ return; } + // If we're currently packing/unpacking, make sure we are packed, so we can move. + if (!this.EnsureCorrectPackStateForAttack(false)) + return; + if (this.IsAnimal()) this.SetNextState("ANIMAL.COMBAT.APPROACHING"); else @@ -3649,7 +3661,7 @@ * Add an order onto the front of the queue, * and execute it immediately. */ -UnitAI.prototype.PushOrderFront = function(type, data) +UnitAI.prototype.PushOrderFront = function(type, data, ignorePacking = false) { var order = { "type": type, "data": data }; // If current order is cheering then add new order after it @@ -3659,7 +3671,7 @@ var cheeringOrder = this.orderQueue.shift(); this.orderQueue.unshift(cheeringOrder, order); } - else if (this.order && this.IsPacking()) + else if (!ignorePacking && this.order && this.IsPacking()) { var packingOrder = this.orderQueue.shift(); this.orderQueue.unshift(packingOrder, order); @@ -3713,6 +3725,43 @@ Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); }; +/** + * For a unit that is packing and trying to attack something, + * either cancel packing or continue with packing, as appropriate. + * Precondition: if the unit is packing/unpacking, then orderQueue + * should have the Attack order at index 0, + * and the Pack/Unpack order at index 1. + * + * @param cmpPack - packing component + * @param preferUnpacked - true if the unit needs to be unpacked to continue attacking, + * false if it needs to be packed. + * @return true if the unit can attack now, false if it must continue packing (or unpacking) first. + */ +UnitAI.prototype.EnsureCorrectPackStateForAttack = function(preferUnpacked) +{ + let cmpPack = Engine.QueryInterface(this.entity, IID_Pack); + if (!cmpPack || !cmpPack.IsPacking()) + return true; + if (this.orderQueue.length != 2 || this.orderQueue[0].type != "Attack" || (this.orderQueue[1].type != "Pack" && this.orderQueue[1].type != "Unpack")) + return true; // violated precondition; should never happen. + if(cmpPack.IsPacked() ^ preferUnpacked) + { + // The unit is already in the packed/unpacked state we want. + // Delete the packing order. + this.orderQueue.splice(1,1); + cmpPack.CancelPack(); + Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); + // Continue with the attack order. + return true; + } + // Move the attack order behind the unpacking order, to continue unpacking. + let tmp = this.orderQueue[0]; + this.orderQueue[0] = this.orderQueue[1]; + this.orderQueue[1] = tmp; + Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); + return false; +}; + UnitAI.prototype.ReplaceOrder = function(type, data) { // Remember the previous work orders to be able to go back to them later if required @@ -3740,7 +3789,22 @@ { var order = { "type": type, "data": data }; var packingOrder = this.orderQueue.shift(); - this.orderQueue = [packingOrder, order]; + if (type == "Attack") + { + // The Attack order is able to handle a packing unit, while other orders can't. + this.orderQueue = [packingOrder]; + this.PushOrderFront(type, data, true); + } + else if (packingOrder.type == "Unpack" && g_OrdersCancelUnpacking.has(type)) + { + // Immediately cancel unpacking before processing an order that demands a packed unit. + let cmpPack = Engine.QueryInterface(this.entity, IID_Pack); + cmpPack.CancelPack(); + this.orderQueue = []; + this.PushOrder(type, data); + } + else + this.orderQueue = [packingOrder, order]; } else {