From 04aba0e41a632fb770c050ad7d80ec0fa5971d3d Mon Sep 17 00:00:00 2001 From: sanderd17 Date: Wed, 22 Jan 2014 13:20:55 +0000 Subject: [PATCH] Let certain formations stay in formation while attacking This was SVN commit r14633. --- .../public/simulation/components/Formation.js | 2 + .../public/simulation/components/UnitAI.js | 186 +++++++++--------- .../templates/formations/phalanx.xml | 3 + .../templates/formations/skirmish.xml | 3 + .../templates/formations/syntagma.xml | 3 + .../templates/formations/testudo.xml | 3 + .../simulation/templates/formations/wedge.xml | 3 + 7 files changed, 107 insertions(+), 96 deletions(-) diff --git a/binaries/data/mods/public/simulation/components/Formation.js b/binaries/data/mods/public/simulation/components/Formation.js index b9f4ef792d..cd2d9c33c5 100644 --- a/binaries/data/mods/public/simulation/components/Formation.js +++ b/binaries/data/mods/public/simulation/components/Formation.js @@ -995,6 +995,8 @@ Formation.prototype.LoadFormation = function(newTemplate) cmpNewUnitAI.AddOrders(orders); else cmpNewUnitAI.MoveIntoFormation(); + + Engine.BroadcastMessage(MT_EntityRenamed, {"entity": this.entity, "newentity": newFormation}); }; Engine.RegisterComponentType(IID_Formation, "Formation", Formation); diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js index caf7d06c84..300ba321fd 100644 --- a/binaries/data/mods/public/simulation/components/UnitAI.js +++ b/binaries/data/mods/public/simulation/components/UnitAI.js @@ -790,6 +790,7 @@ var UnitFsmSpec = { "Order.Stop": function(msg) { this.CallMemberFunction("Stop", [false]); + this.FinishOrder(); }, "Order.Attack": function(msg) { @@ -812,7 +813,7 @@ var UnitFsmSpec = { return; } this.CallMemberFunction("Attack", [target, false]); - this.SetNextState("MEMBER"); + this.SetNextState("ATTACKING"); }, "Order.Garrison": function(msg) { @@ -972,6 +973,10 @@ var UnitFsmSpec = { }, "IDLE": { + "enter": function(msg) { + var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); + cmpFormation.SetRearrange(false); + }, }, "WALKING": { @@ -1080,6 +1085,53 @@ var UnitFsmSpec = { } }, + "ATTACKING": { + // Wait for individual members to finish + "enter": function(msg) { + var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); + cmpFormation.SetRearrange(true); + cmpFormation.MoveMembersIntoFormation(false, false); + this.StartTimer(1000, 1000); + + var target = this.order.data.target; + // Check if we are already in range, otherwise walk there + if (!this.CheckTargetAttackRange(target, target)) + { + if (this.TargetIsAlive(target) && this.CheckTargetVisible(target)) + { + var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); + var range = cmpAttack.GetRange(target); + this.PushOrderFront("WalkToTargetRange", { "target": target, "min": range.min, "max": range.max }); + return; + } + this.FinishOrder(); + return; + } + + }, + + "Timer": function(msg) { + var target = this.order.data.target; + // Check if we are already in range, otherwise walk there + if (!this.CheckTargetAttackRange(target, target)) + { + if (this.TargetIsAlive(target) && this.CheckTargetVisible(target)) + { + var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); + var range = cmpAttack.GetRange(target); + this.PushOrderFront("WalkToTargetRange", { "target": target, "min": range.min, "max": range.max }); + return; + } + this.FinishOrder(); + return; + } + }, + + "leave": function(msg) { + this.StopTimer(); + }, + }, + "MEMBER": { // Wait for individual members to finish "enter": function(msg) { @@ -1166,98 +1218,11 @@ var UnitFsmSpec = { "IDLE": { "enter": function() { - var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); - if (!cmpFormation) - { - warn("UnitAI: " + this.entity + " got into FORMATIONMEMBER.IDLE mode without a valid formationController"); - if (this.IsAnimal()) - this.SetNextState("ANIMAL.IDLE"); - else - this.SetNextState("INDIVIDUAL.IDLE"); - return true; - } - - // Switch back to idle animation to guarantee we won't - // get stuck with an incorrect animation - this.SelectAnimation(cmpFormation.GetFormationAnimation(this.entity, "idle")); - - // If the unit is guarding/escorting, go back to its duty - if (this.isGuardOf) - { - this.Guard(this.isGuardOf, false); - cmpFormation.RemoveMembers([this.entity]); - return true; - } - - // The GUI and AI want to know when a unit is idle, but we don't - // want to send frequent spurious messages if the unit's only - // idle for an instant and will quickly go off and do something else. - // So we'll set a timer here and only report the idle event if we - // remain idle - this.StartTimer(1000); - - // If a unit can heal and attack we first want to heal wounded units, - // so check if we are a healer and find whether there's anybody nearby to heal. - // (If anyone approaches later it'll be handled via LosHealRangeUpdate.) - // If anyone in sight gets hurt that will be handled via LosHealRangeUpdate. - if (this.IsHealer() && this.FindNewHealTargets()) - { - cmpFormation.RemoveMembers([this.entity]); - return true; // (abort the FSM transition since we may have already switched state) - } - - // If we entered the idle state we must have nothing better to do, - // so immediately check whether there's anybody nearby to attack. - // (If anyone approaches later, it'll be handled via LosRangeUpdate.) - if (this.FindNewTargets()) - { - cmpFormation.RemoveMembers([this.entity]); - return true; // (abort the FSM transition since we may have already switched state) - } - - // Nobody to attack - stay in idle - return false; - }, - - "leave": function() { - var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); - if (this.losRangeQuery) - rangeMan.DisableActiveQuery(this.losRangeQuery); - if (this.losHealRangeQuery) - rangeMan.DisableActiveQuery(this.losHealRangeQuery); - - this.StopTimer(); - - if (this.isIdle) - { - this.isIdle = false; - Engine.PostMessage(this.entity, MT_UnitIdleChanged, { "idle": this.isIdle }); - } - }, - - "LosRangeUpdate": function(msg) { - if (this.GetStance().targetVisibleEnemies) - { - // Start attacking one of the newly-seen enemy (if any) - this.AttackEntitiesByPreference(msg.data.added); - } - }, - - "LosHealRangeUpdate": function(msg) { - if (this.RespondToHealableEntities(msg.data.added)) - { - var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); - if (cmpFormation) - cmpFormation.RemoveMembers([this.entity]); - } - }, - - "Timer": function(msg) { - if (!this.isIdle) - { - this.isIdle = true; - Engine.PostMessage(this.entity, MT_UnitIdleChanged, { "idle": this.isIdle }); - } + if (this.IsAnimal()) + this.SetNextState("ANIMAL.IDLE"); + else + this.SetNextState("INDIVIDUAL.IDLE"); + return true; }, }, @@ -1380,7 +1345,14 @@ var UnitFsmSpec = { "enter": function() { // Switch back to idle animation to guarantee we won't // get stuck with an incorrect animation - this.SelectAnimation("idle"); + var animationName = "idle"; + if (this.IsFormationMember()) + { + var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); + if (cmpFormation) + animationName = cmpFormation.GetFormationAnimation(this.entity, animationName); + } + this.SelectAnimation(animationName); // If the unit is guarding/escorting, go back to its duty if (this.isGuardOf) @@ -1737,7 +1709,7 @@ var UnitFsmSpec = { // Can't reach it - try to chase after it if (this.ShouldChaseTargetedEntity(target, this.order.data.force)) { - if (this.MoveToTargetRange(target, IID_Attack, this.order.data.attackType)) + if (this.MoveToTargetAttackRange(target, this.order.data.attackType)) { this.SetNextState("COMBAT.CHASING"); return; @@ -1839,7 +1811,7 @@ var UnitFsmSpec = { if (cmpTargetFormation) { this.order.data.target = this.order.data.formationTarget; - this.TimerHandler(msg.data, msg.lateness); + //this.TimerHandler(msg.data, msg.lateness); return; } @@ -3744,8 +3716,12 @@ UnitAI.prototype.OnGlobalConstructionFinished = function(msg) UnitAI.prototype.OnGlobalEntityRenamed = function(msg) { for each (var order in this.orderQueue) + { if (order.data && order.data.target && order.data.target == msg.entity) order.data.target = msg.newentity; + if (order.data && order.data.formationTarget && order.data.formationTarget == msg.entity) + order.data.formationTarget = msg.newentity; + } if (this.isGuardOf && this.isGuardOf == msg.entity) this.isGuardOf = msg.newentity; @@ -4113,6 +4089,15 @@ UnitAI.prototype.MoveToTargetRange = function(target, iid, type) */ UnitAI.prototype.MoveToTargetAttackRange = function(target, type) { + // for formation members, the formation will take care of the range check + if (this.IsFormationMember()) + { + var cmpFormationAttack = Engine.QueryInterface(this.formationController, IID_Attack); + var cmpFormationUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI); + if (cmpFormationAttack && cmpFormationAttack.CanAttackAsFormation() && cmpFormationUnitAI && cmpFormationUnitAI.GetCurrentState == "FORMATIONCONTROLLER.ATTACKING") + return false; + } + var cmpFormation = Engine.QueryInterface(target, IID_Formation) if (cmpFormation) target = cmpFormation.GetClosestMember(this.entity); @@ -4204,6 +4189,15 @@ UnitAI.prototype.CheckTargetRange = function(target, iid, type) */ UnitAI.prototype.CheckTargetAttackRange = function(target, type) { + // for formation members, the formation will take care of the range check + if (this.IsFormationMember()) + { + var cmpFormationAttack = Engine.QueryInterface(this.formationController, IID_Attack); + var cmpFormationUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI); + if (cmpFormationAttack && cmpFormationAttack.CanAttackAsFormation() && cmpFormationUnitAI && cmpFormationUnitAI.GetCurrentState == "FORMATIONCONTROLLER.ATTACKING") + return true; + } + var cmpFormation = Engine.QueryInterface(target, IID_Formation) if (cmpFormation) target = cmpFormation.GetClosestMember(this.entity); diff --git a/binaries/data/mods/public/simulation/templates/formations/phalanx.xml b/binaries/data/mods/public/simulation/templates/formations/phalanx.xml index b894fadf78..b206926472 100644 --- a/binaries/data/mods/public/simulation/templates/formations/phalanx.xml +++ b/binaries/data/mods/public/simulation/templates/formations/phalanx.xml @@ -9,4 +9,7 @@ 6 2 + + true + diff --git a/binaries/data/mods/public/simulation/templates/formations/skirmish.xml b/binaries/data/mods/public/simulation/templates/formations/skirmish.xml index e89627bb01..bf6cf6769c 100644 --- a/binaries/data/mods/public/simulation/templates/formations/skirmish.xml +++ b/binaries/data/mods/public/simulation/templates/formations/skirmish.xml @@ -8,4 +8,7 @@ 2 3 + + true + diff --git a/binaries/data/mods/public/simulation/templates/formations/syntagma.xml b/binaries/data/mods/public/simulation/templates/formations/syntagma.xml index 7186923dc5..a4828f1d1c 100644 --- a/binaries/data/mods/public/simulation/templates/formations/syntagma.xml +++ b/binaries/data/mods/public/simulation/templates/formations/syntagma.xml @@ -5,4 +5,7 @@ 9 pike infantry units required Syntagma + + true + diff --git a/binaries/data/mods/public/simulation/templates/formations/testudo.xml b/binaries/data/mods/public/simulation/templates/formations/testudo.xml index 3673d9f65b..d61ea9b9a2 100644 --- a/binaries/data/mods/public/simulation/templates/formations/testudo.xml +++ b/binaries/data/mods/public/simulation/templates/formations/testudo.xml @@ -6,4 +6,7 @@ Testudo 0.7 + + true + diff --git a/binaries/data/mods/public/simulation/templates/formations/wedge.xml b/binaries/data/mods/public/simulation/templates/formations/wedge.xml index f70ed3e285..fc0d5003e4 100644 --- a/binaries/data/mods/public/simulation/templates/formations/wedge.xml +++ b/binaries/data/mods/public/simulation/templates/formations/wedge.xml @@ -6,4 +6,7 @@ Wedge triangle + + true +