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 = { @@ -412,6 +416,8 @@ if (this.order.data.hunting && this.orderQueue.length > 1 && this.orderQueue[1].type === "Gather") this.RememberTargetPosition(this.orderQueue[1].data); + let cmpPack = Engine.QueryInterface(this.entity, IID_Pack); + // If we are already at the target, try attacking it from here if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType)) { @@ -424,6 +430,10 @@ return; } + // Cancel any current packing order + if (!this.packForAttack(cmpPack, true)) + return; + if (this.IsAnimal()) this.SetNextState("ANIMAL.COMBAT.ATTACKING"); else @@ -448,6 +458,10 @@ return; } + // If we're currently packing/unpacking, make sure we are packed, so we can move. + if (!this.packForAttack(cmpPack, false)) + return; + if (this.IsAnimal()) this.SetNextState("ANIMAL.COMBAT.APPROACHING"); else @@ -3649,7 +3663,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 +3673,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 +3727,42 @@ 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: 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.packForAttack = function(cmpPack, preferUnpacked) +{ + if (cmpPack && cmpPack.IsPacking()) + { + 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(); + // Continue with the attack order. + return true; + } + else + { + // 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; + return false; + } + } + return true; +}; + UnitAI.prototype.ReplaceOrder = function(type, data) { // Remember the previous work orders to be able to go back to them later if required @@ -3725,6 +3775,7 @@ } let garrisonHolder = this.IsGarrisoned() && type != "Ungarrison" ? this.GetGarrisonHolder() : null; + let cmpPack = Engine.QueryInterface(this.entity, IID_Pack); // Special cases of orders that shouldn't be replaced: // 1. Cheering - we're invulnerable, add order after we finish @@ -3736,11 +3787,25 @@ var cheeringOrder = this.orderQueue.shift(); this.orderQueue = [cheeringOrder, order]; } - else if (this.IsPacking() && type != "CancelPack" && type != "CancelUnpack") + else if (cmpPack && this.IsPacking() && type != "CancelPack" && type != "CancelUnpack") { var order = { "type": type, "data": data }; var packingOrder = this.orderQueue.shift(); - this.orderQueue = [packingOrder, order]; + if (packingOrder.type == "Unpack" && g_OrdersCancelUnpacking.has(type) && cmpPack) + { + // Immediately cancel unpacking before processing an order that demands a packed unit. + cmpPack.CancelPack(); + this.orderQueue = []; + this.PushOrder(type, data); + } + else 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 + this.orderQueue = [packingOrder, order]; } else {