diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js index 54548f76c0..25449d0c1e 100644 --- a/binaries/data/mods/public/simulation/components/UnitAI.js +++ b/binaries/data/mods/public/simulation/components/UnitAI.js @@ -2918,7 +2918,7 @@ UnitAI.prototype.UnitFsmSpec = { // but first check what is our next order and, if needed, insert a returnResource order const cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); if (cmpResourceGatherer.IsCarrying(resourceType.generic) && - this.orderQueue.length > 1 && this.orderQueue[1] !== "ReturnResource" && + this.orderQueue.length > 1 && this.orderQueue[1].type !== "ReturnResource" && (this.orderQueue[1].type !== "Gather" || this.orderQueue[1].data.type.generic !== resourceType.generic)) { const nearestDropsite = this.FindNearestDropsite(resourceType.generic); diff --git a/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js b/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js index d381dc7850..2ae6eef78e 100644 --- a/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js +++ b/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js @@ -1259,4 +1259,73 @@ function TestAttackGroupBehavior() })(); } +function TestGatherFindingNewTargetRespectsQueuedReturnResource() +{ + ResetState(); + const ent = 10; + const gatherTarget = 20; + const queuedDropsite = 30; + + const resourceType = { "generic": "food", "specific": "grain" }; + + AddMock(SYSTEM_ENTITY, IID_Timer, { + "SetInterval": () => {}, + "SetTimeout": () => {} + }); + AddMock(ent, IID_Position, { + "IsInWorld": () => true + }); + AddMock(ent, IID_ResourceGatherer, { + "IsCarrying": (type) => type == resourceType.generic, + "AddToPlayerCounter": () => {}, + "RemoveFromPlayerCounter": () => {}, + "GetLastCarriedType": () => resourceType, + "StartGathering": () => true, + "StopGathering": () => {} + }); + + const unitAI = ConstructComponent(ent, "UnitAI", { + "FormationController": "false", + "DefaultStance": "aggressive", + "FleeDistance": 10 + }); + unitAI.OnCreate(); + + unitAI.order = { + "type": "Gather", + "data": { + "force": false, + "target": gatherTarget, + "type": resourceType, + "template": "gaia/fruit/grain" + } + }; + unitAI.orderQueue = [ + unitAI.order, + { "type": "ReturnResource", "data": { "target": queuedDropsite, "force": false } } + ]; + + let dropsiteLookups = 0; + unitAI.FindNearestDropsite = () => { ++dropsiteLookups; return 999; }; + + unitAI.CheckTargetRange = () => true; + unitAI.UnitFsm.Init(unitAI, "INDIVIDUAL.GATHER.GATHERING"); + TS_ASSERT_EQUALS(unitAI.GetCurrentState(), "INDIVIDUAL.GATHER.GATHERING"); + + // Simulates the target running out: matches ResourceGatherer.PerformGather, + // which sends this same message on the same condition. + unitAI.CheckTargetRange = () => false; + unitAI.AbleToMove = () => true; + unitAI.MoveTo = () => true; + unitAI.ProcessMessage("TargetInvalidated", null); + + TS_ASSERT_EQUALS(dropsiteLookups, 0); + TS_ASSERT_EQUALS(unitAI.orderQueue.length, 1); + TS_ASSERT_EQUALS(unitAI.order.type, "ReturnResource"); + TS_ASSERT_EQUALS(unitAI.order.data.target, queuedDropsite); + TS_ASSERT_EQUALS(unitAI.GetCurrentState(), "INDIVIDUAL.RETURNRESOURCE.APPROACHING"); +} + +TestGatherFindingNewTargetRespectsQueuedReturnResource(); + TestAttackGroupBehavior();