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:
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user