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 @@ -177,7 +177,7 @@ UnitAI.prototype.notifyToCheerInRange = 30; -// To reject an order, use 'return this.FinishOrder();' +const REJECT_ORDER = false; const ACCEPT_ORDER = true; // See ../helpers/FSM.js for some documentation of this FSM specification syntax @@ -248,7 +248,7 @@ // Called when being told to walk as part of a formation "Order.FormationWalk": function(msg) { if (!this.AbleToMove()) - return this.FinishOrder(); + return REJECT_ORDER; if (this.CanPack()) { @@ -256,12 +256,12 @@ // 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 this.FinishOrder(); + return REJECT_ORDER; this.PushOrderFront("Pack", { "force": true }); + return ACCEPT_ORDER; } - else - this.SetNextState("FORMATIONMEMBER.WALKING"); - return ACCEPT_ORDER; + this.SetNextState("FORMATIONMEMBER.WALKING"); + return this.AcceptOrder(msg); }, // Special orders: @@ -269,17 +269,17 @@ "Order.LeaveFoundation": function(msg) { if (!this.WillMoveFromFoundation(msg.data.target)) - return this.FinishOrder(); + return REJECT_ORDER; msg.data.min = g_LeaveFoundationRange; this.SetNextState("INDIVIDUAL.WALKING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, // Individual orders: - "Order.LeaveFormation": function() { + "Order.LeaveFormation": function(msg) { if (!this.IsFormationMember()) - return this.FinishOrder(); + return REJECT_ORDER; let cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); if (cmpFormation) @@ -290,7 +290,7 @@ cmpFormation.RemoveMembers([this.entity]); cmpFormation.SetRearrange(true); } - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Stop": function(msg) { @@ -300,7 +300,7 @@ "Order.Walk": function(msg) { if (!this.AbleToMove()) - return this.FinishOrder(); + return REJECT_ORDER; if (this.CanPack()) { @@ -311,12 +311,12 @@ this.SetHeldPosition(msg.data.x, msg.data.z); msg.data.relaxed = true; this.SetNextState("INDIVIDUAL.WALKING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.WalkAndFight": function(msg) { if (!this.AbleToMove()) - return this.FinishOrder(); + return REJECT_ORDER; if (this.CanPack()) { @@ -327,13 +327,13 @@ this.SetHeldPosition(msg.data.x, msg.data.z); msg.data.relaxed = true; this.SetNextState("INDIVIDUAL.WALKINGANDFIGHTING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.WalkToTarget": function(msg) { if (!this.AbleToMove()) - return this.FinishOrder(); + return REJECT_ORDER; if (this.CanPack()) { @@ -341,25 +341,24 @@ return ACCEPT_ORDER; } - if (this.CheckRange(msg.data)) - return this.FinishOrder(); + return REJECT_ORDER; msg.data.relaxed = true; this.SetNextState("INDIVIDUAL.WALKING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.PickupUnit": function(msg) { let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); if (!cmpGarrisonHolder || cmpGarrisonHolder.IsFull()) - return this.FinishOrder(); + return REJECT_ORDER; let range = cmpGarrisonHolder.GetLoadingRange(); msg.data.min = range.min; msg.data.max = range.max; if (this.CheckRange(msg.data)) - return this.FinishOrder(); + return REJECT_ORDER; // Check if we need to move // If the target can reach us and we are reasonably close, don't move. @@ -371,33 +370,33 @@ this.SetNextState("INDIVIDUAL.PICKUP.LOADING"); else this.SetNextState("INDIVIDUAL.PICKUP.APPROACHING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Guard": function(msg) { if (!this.AddGuard(msg.data.target)) - return this.FinishOrder(); + return REJECT_ORDER; if (!this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) this.SetNextState("INDIVIDUAL.GUARD.ESCORTING"); else this.SetNextState("INDIVIDUAL.GUARD.GUARDING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Flee": function(msg) { this.SetNextState("INDIVIDUAL.FLEEING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Attack": function(msg) { let type = this.GetBestAttackAgainst(msg.data.target, msg.data.allowCapture); if (!type) - return this.FinishOrder(); + return REJECT_ORDER; msg.data.attackType = type; - this.RememberTargetPosition(); + this.RememberTargetPosition(msg); if (msg.data.hunting && this.orderQueue.length > 1 && this.orderQueue[1].type === "Gather") this.RememberTargetPosition(this.orderQueue[1].data); @@ -413,12 +412,12 @@ if (this.EnsureCorrectPackStateForAttack(false)) this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); } // If we're hunting, that's a special case where we should continue attacking our target. if (this.GetStance().respondStandGround && !msg.data.force && !msg.data.hunting || !this.AbleToMove()) - return this.FinishOrder(); + return REJECT_ORDER; if (this.CanPack()) { @@ -429,12 +428,12 @@ // If we're currently packing/unpacking, make sure we are packed, so we can move. if (this.EnsureCorrectPackStateForAttack(true)) this.SetNextState("INDIVIDUAL.COMBAT.APPROACHING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Patrol": function(msg) { if (!this.AbleToMove()) - return this.FinishOrder(); + return REJECT_ORDER; if (this.CanPack()) { @@ -445,16 +444,16 @@ msg.data.relaxed = true; this.SetNextState("INDIVIDUAL.PATROL.PATROLLING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Heal": function(msg) { if (!this.TargetIsAlive(msg.data.target)) - return this.FinishOrder(); + return REJECT_ORDER; // Healers can't heal themselves. if (msg.data.target == this.entity) - return this.FinishOrder(); + return REJECT_ORDER; if (this.CheckTargetRange(msg.data.target, IID_Heal)) { @@ -463,17 +462,17 @@ } if (this.GetStance().respondStandGround && !msg.data.force) - return this.FinishOrder(); + return REJECT_ORDER; this.SetNextState("INDIVIDUAL.HEAL.APPROACHING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Gather": function(msg) { if (!this.CanGather(msg.data.target)) { this.SetNextState("INDIVIDUAL.GATHER.FINDINGNEWTARGET"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); } // If the unit is full go to the nearest dropsite instead of trying to gather. @@ -488,7 +487,7 @@ "type": msg.data.type }); // Players expect the unit to move, so walk to the target instead of trying to gather. - else if (!this.FinishOrder()) + else if (!this.OrderQueue.length) this.WalkToTarget(msg.data.target, false); return ACCEPT_ORDER; @@ -501,7 +500,7 @@ { // Oops, we can't attack at all - give up // TODO: should do something so the player knows why this failed - return this.FinishOrder(); + return REJECT_ORDER; } // The target was visible when this order was issued, // but could now be invisible again. @@ -513,7 +512,7 @@ this.PushOrderFront("Walk", msg.data.lastPos); } // We couldn't move there, or the target moved away - else if (!this.FinishOrder()) + else if (!this.OrderQueue.length) this.PushOrderFront("GatherNearPosition", { "x": msg.data.lastPos.x, "z": msg.data.lastPos.z, @@ -527,7 +526,7 @@ return ACCEPT_ORDER; } - this.RememberTargetPosition(); + this.RememberTargetPosition(msg); if (!msg.data.initPos) msg.data.initPos = msg.data.lastPos; @@ -535,14 +534,14 @@ this.SetNextState("INDIVIDUAL.GATHER.GATHERING"); else this.SetNextState("INDIVIDUAL.GATHER.APPROACHING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.GatherNearPosition": function(msg) { this.SetNextState("INDIVIDUAL.GATHER.WALKING"); msg.data.initPos = { 'x': msg.data.x, 'z': msg.data.z }; msg.data.relaxed = true; - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.ReturnResource": function(msg) { @@ -555,22 +554,21 @@ // Our next order should always be a Gather, // so just switch back to that order. - this.FinishOrder(); + return ACCEPT_ORDER; } - else - this.SetNextState("INDIVIDUAL.RETURNRESOURCE.APPROACHING"); - return ACCEPT_ORDER; + this.SetNextState("INDIVIDUAL.RETURNRESOURCE.APPROACHING"); + return this.AcceptOrder(msg); }, "Order.Trade": function(msg) { // 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 this.FinishOrder(); + return REJECT_ORDER; this.waypoints = []; this.SetNextState("TRADE.APPROACHINGMARKET"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Repair": function(msg) { @@ -578,12 +576,12 @@ this.SetNextState("INDIVIDUAL.REPAIR.REPAIRING"); else this.SetNextState("INDIVIDUAL.REPAIR.APPROACHING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Garrison": function(msg) { if (!this.AbleToMove()) - return this.FinishOrder(); + return REJECT_ORDER; // Also pack when we are in range. if (this.CanPack()) @@ -596,7 +594,7 @@ this.SetNextState("INDIVIDUAL.GARRISON.GARRISONING"); else this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Ungarrison": function(msg) { @@ -609,37 +607,37 @@ }, "Order.Cheer": function(msg) { - return this.FinishOrder(); + return REJECT_ORDER; }, "Order.Pack": function(msg) { if (!this.CanPack()) - return this.FinishOrder(); + return REJECT_ORDER; this.SetNextState("INDIVIDUAL.PACKING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Unpack": function(msg) { if (!this.CanUnpack()) - return this.FinishOrder(); + return REJECT_ORDER; this.SetNextState("INDIVIDUAL.UNPACKING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.MoveToChasingPoint": function(msg) { // Overriden by the CHASING state. // Can however happen outside of it when renaming... // TODO: don't use an order for that behaviour. - return this.FinishOrder(); + return REJECT_ORDER; }, "Order.CollectTreasure": function(msg) { let cmpTreasureCollecter = Engine.QueryInterface(this.entity, IID_TreasureCollecter); if (!cmpTreasureCollecter || !cmpTreasureCollecter.CanCollect(msg.data.target)) - return this.FinishOrder(); + return REJECT_ORDER; this.SetNextState("COLLECTTREASURE"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.CollectTreasureNearPosition": function(msg) { @@ -648,7 +646,7 @@ this.CollectTreasure(nearbyTreasure, oldData.autocontinue, true); else this.SetNextState("COLLECTTREASURE"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, // States for the special entity representing a group of units moving in formation: @@ -657,53 +655,53 @@ "Order.Walk": function(msg) { this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]); this.SetNextState("WALKING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.WalkAndFight": function(msg) { this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]); this.SetNextState("WALKINGANDFIGHTING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.MoveIntoFormation": function(msg) { this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]); this.SetNextState("FORMING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, // Only used by other orders to walk there in formation. "Order.WalkToTargetRange": function(msg) { if (this.CheckRange(msg.data)) - return this.FinishOrder(); + return REJECT_ORDER; this.SetNextState("WALKING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.WalkToTarget": function(msg) { if (this.CheckRange(msg.data)) - return this.FinishOrder(); + return REJECT_ORDER; this.SetNextState("WALKING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.WalkToPointRange": function(msg) { if (this.CheckRange(msg.data)) - return this.FinishOrder(); + return REJECT_ORDER; this.SetNextState("WALKING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Patrol": function(msg) { this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]); this.SetNextState("PATROL.PATROLLING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Guard": function(msg) { this.CallMemberFunction("Guard", [msg.data.target, false]); Engine.QueryInterface(this.entity, IID_Formation).Disband(); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Stop": function(msg) { @@ -730,9 +728,9 @@ if (this.CanAttack(target) && this.CheckTargetVisible(target)) { this.SetNextState("COMBAT.APPROACHING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); } - return this.FinishOrder(); + return REJECT_ORDER; } this.CallMemberFunction("Attack", [target, allowCapture, false]); let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); @@ -740,22 +738,22 @@ this.SetNextState("COMBAT.ATTACKING"); else this.SetNextState("MEMBER"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Garrison": function(msg) { if (!Engine.QueryInterface(msg.data.target, IID_GarrisonHolder)) - return this.FinishOrder(); + return REJECT_ORDER; if (!this.CheckGarrisonRange(msg.data.target)) { if (!this.CheckTargetVisible(msg.data.target)) - return this.FinishOrder(); + return REJECT_ORDER; this.SetNextState("GARRISON.APPROACHING"); } else this.SetNextState("GARRISON.GARRISONING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Gather": function(msg) { @@ -771,17 +769,13 @@ this.PushOrderFront("Walk", msg.data.lastPos); } // We couldn't move there, or the target moved away - else - { - let data = msg.data; - if (!this.FinishOrder()) - this.PushOrderFront("GatherNearPosition", { - "x": data.lastPos.x, - "z": data.lastPos.z, - "type": data.type, - "template": data.template - }); - } + else if (!this.OrderQueue.length) + this.PushOrderFront("GatherNearPosition", { + "x": msg.data.lastPos.x, + "z": msg.data.lastPos.z, + "type": msg.data.type, + "template": msg.data.template + }); return ACCEPT_ORDER; } this.PushOrderFront("Attack", { "target": msg.data.target, "force": !!msg.data.force, "hunting": true, "allowCapture": false, "min": 0, "max": 10 }); @@ -792,7 +786,7 @@ if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10)) { if (!this.CanGather(msg.data.target) || !this.CheckTargetVisible(msg.data.target)) - return this.FinishOrder(); + return REJECT_ORDER; // TODO: Should we issue a gather-near-position order // if the target isn't gatherable/doesn't exist anymore? if (!msg.data.secondTry) @@ -801,13 +795,13 @@ this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 }); return ACCEPT_ORDER; } - return this.FinishOrder(); + return REJECT_ORDER; } this.CallMemberFunction("Gather", [msg.data.target, false]); this.SetNextState("MEMBER"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.GatherNearPosition": function(msg) { @@ -822,7 +816,7 @@ this.CallMemberFunction("GatherNearPosition", [msg.data.x, msg.data.z, msg.data.type, msg.data.template, false]); this.SetNextState("MEMBER"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Heal": function(msg) { @@ -830,7 +824,7 @@ if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10)) { if (!this.TargetIsAlive(msg.data.target) || !this.CheckTargetVisible(msg.data.target)) - return this.FinishOrder(); + return REJECT_ORDER; if (!msg.data.secondTry) { @@ -838,13 +832,13 @@ this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 }); return ACCEPT_ORDER; } - return this.FinishOrder(); + return REJECT_ORDER; } this.CallMemberFunction("Heal", [msg.data.target, false]); this.SetNextState("MEMBER"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Repair": function(msg) { @@ -852,7 +846,7 @@ if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10)) { if (!this.TargetIsAlive(msg.data.target) || !this.CheckTargetVisible(msg.data.target)) - return this.FinishOrder(); + return REJECT_ORDER; if (!msg.data.secondTry) { @@ -860,13 +854,13 @@ this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 }); return ACCEPT_ORDER; } - return this.FinishOrder(); + return REJECT_ORDER; } this.CallMemberFunction("Repair", [msg.data.target, msg.data.autocontinue, false]); this.SetNextState("MEMBER"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.ReturnResource": function(msg) { @@ -874,7 +868,7 @@ if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10)) { if (!this.CheckTargetVisible(msg.data.target)) - return this.FinishOrder(); + return REJECT_ORDER; if (!msg.data.secondTry) { @@ -882,27 +876,27 @@ this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 }); return ACCEPT_ORDER; } - return this.FinishOrder(); + return REJECT_ORDER; } this.CallMemberFunction("ReturnResource", [msg.data.target, false]); this.SetNextState("MEMBER"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Pack": function(msg) { this.CallMemberFunction("Pack", [false]); this.SetNextState("MEMBER"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "Order.Unpack": function(msg) { this.CallMemberFunction("Unpack", [false]); this.SetNextState("MEMBER"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "IDLE": { @@ -1342,10 +1336,10 @@ // state forever and need to get out of foundations in that case) "Order.LeaveFoundation": function(msg) { if (!this.WillMoveFromFoundation(msg.data.target)) - return this.FinishOrder(); + return REJECT_ORDER; msg.data.min = g_LeaveFoundationRange; this.SetNextState("WALKINGTOPOINT"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "enter": function() { @@ -1495,13 +1489,13 @@ }, "IDLE": { - "Order.Cheer": function() { + "Order.Cheer": function(msg) { // Do not cheer if there is no cheering time and we are not idle yet. if (!this.cheeringTime || !this.isIdle) - return this.FinishOrder(); + return REJECT_ORDER; this.SetNextState("CHEERING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "enter": function() { @@ -1944,7 +1938,7 @@ "COMBAT": { "Order.LeaveFoundation": function(msg) { // Ignore the order as we're busy. - return this.FinishOrder(); + return REJECT_ORDER; }, "Attacked": function(msg) { @@ -2191,12 +2185,12 @@ }, "FINDINGNEWTARGET": { - "Order.Cheer": function() { + "Order.Cheer": function(msg) { if (!this.cheeringTime) - return this.FinishOrder(); + return REJECT_ORDER; this.SetNextState("CHEERING"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "enter": function() { @@ -2244,11 +2238,11 @@ "CHASING": { "Order.MoveToChasingPoint": function(msg) { if (this.CheckPointRangeExplicit(msg.data.x, msg.data.z, 0, msg.data.max)) - return this.FinishOrder(); + return REJECT_ORDER; msg.data.relaxed = true; this.StopTimer(); this.SetNextState("MOVINGTOPOINT"); - return ACCEPT_ORDER; + return this.AcceptOrder(msg); }, "enter": function() { if (!this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType)) @@ -3858,7 +3852,6 @@ * 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() { @@ -3871,16 +3864,19 @@ } this.orderQueue.shift(); - this.order = this.orderQueue[0]; let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); if (this.orderQueue.length && (this.isGarrisoned || this.IsFormationController() || cmpPosition && cmpPosition.IsInWorld())) { + let order = this.orderQueue[0]; let ret = this.UnitFsm.ProcessMessage(this, - { "type": "Order."+this.order.type, "data": this.order.data } + { "type": "Order." + order.type, "data": order.data } ); + if (ret === REJECT_ORDER) + return this.FinishOrder(); + Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); return ret; @@ -3914,20 +3910,35 @@ }; /** + * @param {Object} order - The order to accept. + * @return {boolean} - Whether we accepted the order. + */ +UnitAI.prototype.AcceptOrder = function(order) +{ + this.order = order; + return ACCEPT_ORDER; +}; + +/** * Add an order onto the back of the queue, * and execute it if we didn't already have an order. */ UnitAI.prototype.PushOrder = function(type, data) { - var order = { "type": type, "data": data }; - this.orderQueue.push(order); + let order = { "type": type, "data": data }; + this.orderQueue.push(order); if (this.orderQueue.length == 1) { - this.order = order; - this.UnitFsm.ProcessMessage(this, - { "type": "Order."+this.order.type, "data": this.order.data } + let ret = this.UnitFsm.ProcessMessage(this, + { "type": "Order." + order.type, "data": order.data } ); + if (ret === REJECT_ORDER) + { + // A player might expect this. + this.Stop(); + return; + } } Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); @@ -3939,24 +3950,29 @@ */ UnitAI.prototype.PushOrderFront = function(type, data, ignorePacking = false) { - var order = { "type": type, "data": data }; + let order = { "type": type, "data": data }; // If current order is packing/unpacking then add new order after it. if (!ignorePacking && this.order && this.IsPacking()) { - var packingOrder = this.orderQueue.shift(); + let packingOrder = this.orderQueue.shift(); this.orderQueue.unshift(packingOrder, order); } else { this.orderQueue.unshift(order); - this.order = order; - this.UnitFsm.ProcessMessage(this, - { "type": "Order."+this.order.type, "data": this.order.data } + let ret = this.UnitFsm.ProcessMessage(this, + { "type": "Order." + order.type, "data": order.data } ); + if (ret === REJECT_ORDER) + { + this.orderQueue.shift(); + // A player might expect this. + this.Stop(); + return; + } } Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); - }; /** Index: binaries/data/mods/public/simulation/components/tests/test_UnitAI.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_UnitAI.js +++ binaries/data/mods/public/simulation/components/tests/test_UnitAI.js @@ -328,7 +328,7 @@ units.push(unit + i); - var unitAI = ConstructComponent(unit + i, "UnitAI", { "FormationController": "false", "DefaultStance": "aggressive" }); + let unitAI = ConstructComponent(unit + i, "UnitAI", { "FormationController": "false", "DefaultStance": "aggressive" }); AddMock(unit + i, IID_Identity, { GetClassesList: function() { return []; }, @@ -344,6 +344,7 @@ GetPosition2D: function() { return new Vector2D(); }, GetRotation: function() { return { "y": 0 }; }, IsInWorld: function() { return true; }, + TurnTo: function() {} }); AddMock(unit + i, IID_UnitMotion, { @@ -428,16 +429,16 @@ controllerAI.Attack(enemy, []); - for (let ent of unitAIs) + for (let unitAI of unitAIs) TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.COMBAT.ATTACKING"); controllerAI.MoveIntoFormation({"name": "Circle"}); - // let all units be in position - for (let ent of unitAIs) - controllerFormation.SetWaitingOnController(ent); + // Let all units be in position. + for (let unitAI of unitAIs) + unitAI.OnMotionUpdate({}); - for (let ent of unitAIs) + for (let unitAI of unitAIs) TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.COMBAT.ATTACKING"); controllerFormation.Disband();