1
0
forked from mirrors/0ad

Don't stop gathering after starting by autocontinue when in a formation.

FinishOrder called SetWaitingOnController, although the order
(constructing) had pushed another order (gather).
This is done now by telling we finished the order only when really idle.

This seems to boil down to the question: If we issued an order to a
formation, and its members have wandered off (imagine attacking an
entity and our members have finished the initial target but attack
nearby ones) do we want the whole formation to continue attacking or
execute the next order.

Also fixes reforming whilst attacking when an attack order was issued by
the player.

Reported by: @Langbart at https://code.wildfiregames.com/D2175#182343
Differential revision: https://code.wildfiregames.com/D4282
Tested by: @Langbart
This was SVN commit r25947.
This commit is contained in:
Freagarach
2021-10-03 05:59:54 +00:00
parent 9f556c8b5a
commit 65cd29696d
3 changed files with 54 additions and 34 deletions
@@ -79,7 +79,8 @@ Formation.prototype.variablesToSerialize = [
"memberPositions",
"maxRowsUsed",
"maxColumnsUsed",
"waitingOnController",
"finishedEntities",
"idleEntities",
"columnar",
"rearrange",
"formationMembersWithAura",
@@ -135,8 +136,9 @@ Formation.prototype.Init = function(deserialized = false)
this.memberPositions = {};
this.maxRowsUsed = 0;
this.maxColumnsUsed = [];
// Entities that are waiting on the controller.
this.waitingOnController = [];
// Entities that have finished the original task.
this.finishedEntities = new Set();
this.idleEntities = new Set();
// Whether we're travelling in column (vs box) formation.
this.columnar = false;
// Whether we should rearrange all formation members.
@@ -284,34 +286,50 @@ Formation.prototype.GetFormationAnimationVariant = function(entity)
return undefined;
};
Formation.prototype.SetWaitingOnController = function(ent)
Formation.prototype.SetFinishedEntity = function(ent)
{
// Rotate the entity to the right angle.
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
let cmpEntPosition = Engine.QueryInterface(ent, IID_Position);
// Rotate the entity to the correct angle.
const cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
const cmpEntPosition = Engine.QueryInterface(ent, IID_Position);
if (cmpEntPosition && cmpEntPosition.IsInWorld() && cmpPosition && cmpPosition.IsInWorld())
cmpEntPosition.TurnTo(cmpPosition.GetRotation().y);
if (this.waitingOnController.indexOf(ent) !== -1)
return;
this.waitingOnController.push(ent);
this.finishedEntities.add(ent);
};
Formation.prototype.UnsetWaitingOnController = function(ent)
Formation.prototype.UnsetFinishedEntity = function(ent)
{
let ind = this.waitingOnController.indexOf(ent);
if (ind !== -1)
this.waitingOnController.splice(ind, 1);
this.finishedEntities.delete(ent);
};
Formation.prototype.ResetWaitingEntities = function()
Formation.prototype.ResetFinishedEntities = function()
{
this.waitingOnController = [];
this.finishedEntities.clear();
};
Formation.prototype.AreAllMembersWaiting = function()
Formation.prototype.AreAllMembersFinished = function()
{
return this.waitingOnController.length === this.members.length;
return this.finishedEntities.size === this.members.length;
};
Formation.prototype.SetIdleEntity = function(ent)
{
this.idleEntities.add(ent);
};
Formation.prototype.UnsetIdleEntity = function(ent)
{
this.idleEntities.delete(ent);
};
Formation.prototype.ResetIdleEntities = function()
{
this.idleEntities.clear();
};
Formation.prototype.AreAllMembersIdle = function()
{
return this.idleEntities.size === this.members.length;
};
/**
@@ -362,10 +380,10 @@ Formation.prototype.RemoveMembers = function(ents, renamed = false)
{
this.offsets = undefined;
this.members = this.members.filter(ent => ents.indexOf(ent) === -1);
this.waitingOnController = this.waitingOnController.filter(ent => ents.indexOf(ent) === -1);
for (let ent of ents)
{
this.finishedEntities.delete(ent);
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
cmpUnitAI.UpdateWorkOrders();
cmpUnitAI.SetFormationController(INVALID_ENTITY);
@@ -453,7 +471,7 @@ Formation.prototype.Disband = function()
}
this.members = [];
this.waitingOnController = [];
this.finishedEntities.clear();
this.formationMembersWithAura = [];
this.offsets = undefined;
@@ -525,8 +543,8 @@ Formation.prototype.MoveMembersIntoFormation = function(moveCenter, force, varia
let yMin = 0;
if (force)
// Reset waitingOnController as FormationWalk is called.
this.ResetWaitingEntities();
// Reset finishedEntities as FormationWalk is called.
this.ResetFinishedEntities();
for (let i = 0; i < this.offsets.length; ++i)
{
@@ -965,9 +983,8 @@ Formation.prototype.OnGlobalEntityRenamed = function(msg)
if (this.members.indexOf(msg.entity) === -1)
return;
let waitingIndex = this.waitingOnController.indexOf(msg.entity);
if (waitingIndex !== -1)
this.waitingOnController.splice(waitingIndex, 1, msg.newentity);
if (this.finishedEntities.delete(msg.entity))
this.finishedEntities.add(msg.newentity);
// Save rearranging to temporarily set it to false.
let temp = this.rearrange;
@@ -1358,7 +1358,7 @@ UnitAI.prototype.UnitFsmSpec = {
"Timer": function(msg) {
let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
if (cmpFormation && !cmpFormation.AreAllMembersWaiting())
if (cmpFormation && !cmpFormation.AreAllMembersFinished())
return;
if (this.FinishOrder())
@@ -1374,7 +1374,7 @@ UnitAI.prototype.UnitFsmSpec = {
this.StopTimer();
// Reform entirely as members might be all over the place now.
let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
if (cmpFormation)
if (cmpFormation && (cmpFormation.AreAllMembersIdle() || this.orderQueue.length))
cmpFormation.MoveMembersIntoFormation(true);
// Update the held position so entities respond to orders.
@@ -1609,6 +1609,8 @@ UnitAI.prototype.UnitFsmSpec = {
if (this.isIdle)
{
if (this.IsFormationMember())
Engine.QueryInterface(this.formationController, IID_Formation).UnsetIdleEntity(this.entity);
this.isIdle = false;
Engine.PostMessage(this.entity, MT_UnitIdleChanged, { "idle": this.isIdle });
}
@@ -1677,6 +1679,7 @@ UnitAI.prototype.UnitFsmSpec = {
let cmpFormationAI = Engine.QueryInterface(this.formationController, IID_UnitAI);
if (!cmpFormationAI || !cmpFormationAI.IsIdle())
return;
Engine.QueryInterface(this.formationController, IID_Formation).SetIdleEntity(this.entity);
}
this.isIdle = true;
@@ -3895,8 +3898,8 @@ UnitAI.prototype.FinishOrder = function()
if (cmpUnitAI)
{
// Inform the formation controller that we finished this task
let cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
cmpFormation.SetWaitingOnController(this.entity);
Engine.QueryInterface(this.formationController, IID_Formation).
SetFinishedEntity(this.entity);
// We don't want to carry out the default order
// if there are still queued formation orders left
if (cmpUnitAI.GetOrders().length > 1)
@@ -6450,20 +6453,20 @@ UnitAI.prototype.AttackEntitiesByPreference = function(ents)
/**
* Call UnitAI.funcname(args) on all formation members.
* @param resetWaitingEntities - If true, call ResetWaitingEntities first.
* @param resetFinishedEntities - If true, call ResetFinishedEntities first.
* If the controller wants to wait on its members to finish their order,
* this needs to be reset before sending new orders (in case they instafail)
* so it makes sense to do it here.
* Only set this to false if you're sure it's safe.
*/
UnitAI.prototype.CallMemberFunction = function(funcname, args, resetWaitingEntities = true)
UnitAI.prototype.CallMemberFunction = function(funcname, args, resetFinishedEntities = true)
{
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
if (!cmpFormation)
return;
if (resetWaitingEntities)
cmpFormation.ResetWaitingEntities();
if (resetFinishedEntities)
cmpFormation.ResetFinishedEntities();
cmpFormation.GetMembers().forEach(ent => {
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
@@ -439,7 +439,7 @@ function TestMoveIntoFormationWhileAttacking()
// let all units be in position
for (let ent of unitAIs)
controllerFormation.SetWaitingOnController(ent);
controllerFormation.SetFinishedEntity(ent);
for (let ent of unitAIs)
TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.COMBAT.ATTACKING");