mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-09-26 05:41:06 +00:00
UnitAI: properly follow queued return resources orders
GATHER.FINDINGNEWTARGET inserts a ReturnResource order ahead of the
next queued order when the unit is still carrying resources, but
skips doing so if the next order already returns those resources.
Unfortunately, the check was incorrectly written, missing a `.type`.
This meant we always added a fresh order.
Units could then sometimes return resources to the closest dropsites,
then move to the actually queued dropsite, which could lead to
unexpected pathing.
Introduced in 0dda3b579c where the feature was added.
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user