Index: ps/trunk/binaries/data/mods/public/simulation/components/Formation.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/Formation.js +++ ps/trunk/binaries/data/mods/public/simulation/components/Formation.js @@ -79,7 +79,8 @@ "memberPositions", "maxRowsUsed", "maxColumnsUsed", - "waitingOnController", + "finishedEntities", + "idleEntities", "columnar", "rearrange", "formationMembersWithAura", @@ -135,8 +136,9 @@ 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 @@ 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.UnsetFinishedEntity = function(ent) +{ + this.finishedEntities.delete(ent); +}; + +Formation.prototype.ResetFinishedEntities = function() +{ + this.finishedEntities.clear(); +}; + +Formation.prototype.AreAllMembersFinished = function() +{ + return this.finishedEntities.size === this.members.length; +}; + +Formation.prototype.SetIdleEntity = function(ent) +{ + this.idleEntities.add(ent); }; -Formation.prototype.UnsetWaitingOnController = function(ent) +Formation.prototype.UnsetIdleEntity = function(ent) { - let ind = this.waitingOnController.indexOf(ent); - if (ind !== -1) - this.waitingOnController.splice(ind, 1); + this.idleEntities.delete(ent); }; -Formation.prototype.ResetWaitingEntities = function() +Formation.prototype.ResetIdleEntities = function() { - this.waitingOnController = []; + this.idleEntities.clear(); }; -Formation.prototype.AreAllMembersWaiting = function() +Formation.prototype.AreAllMembersIdle = function() { - return this.waitingOnController.length === this.members.length; + return this.idleEntities.size === this.members.length; }; /** @@ -362,10 +380,10 @@ { 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 @@ } this.members = []; - this.waitingOnController = []; + this.finishedEntities.clear(); this.formationMembersWithAura = []; this.offsets = undefined; @@ -525,8 +543,8 @@ 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 @@ 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; Index: ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js +++ ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js @@ -1358,7 +1358,7 @@ "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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /** * 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); Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js @@ -439,7 +439,7 @@ // 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");