Index: binaries/data/mods/public/gui/session/selection_panels.js =================================================================== --- binaries/data/mods/public/gui/session/selection_panels.js +++ binaries/data/mods/public/gui/session/selection_panels.js @@ -242,13 +242,13 @@ if (unitEntStates.some(state => !hasClass(state, "Unit"))) return []; - if (unitEntStates.every(state => !state.identity || !state.identity.hasSomeFormation)) + if (unitEntStates.every(state => !state.unitAI || !state.unitAI.formations.length)) return []; if (!g_AvailableFormations.has(unitEntStates[0].player)) g_AvailableFormations.set(unitEntStates[0].player, Engine.GuiInterfaceCall("GetAvailableFormations", unitEntStates[0].player)); - return g_AvailableFormations.get(unitEntStates[0].player).filter(formation => unitEntStates.some(state => !!state.identity && state.identity.formations.indexOf(formation) != -1)); + return g_AvailableFormations.get(unitEntStates[0].player).filter(formation => unitEntStates.some(state => !!state.unitAI && state.unitAI.formations.includes(formation))); }, "setupButton": function(data) { Index: binaries/data/mods/public/simulation/components/GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/GuiInterface.js +++ binaries/data/mods/public/simulation/components/GuiInterface.js @@ -276,8 +276,6 @@ "classes": cmpIdentity.GetClassesList(), "selectionGroupName": cmpIdentity.GetSelectionGroupName(), "canDelete": !cmpIdentity.IsUndeletable(), - "hasSomeFormation": cmpIdentity.HasSomeFormation(), - "formations": cmpIdentity.GetFormationsList(), "controllable": cmpIdentity.IsControllable() }; @@ -433,6 +431,7 @@ "canPatrol": cmpUnitAI.CanPatrol(), "selectableStances": cmpUnitAI.GetSelectableStances(), "isIdle": cmpUnitAI.IsIdle(), + "formations": cmpUnitAI.GetFormationsList(), "formation": cmpUnitAI.GetFormationController() }; Index: binaries/data/mods/public/simulation/components/Identity.js =================================================================== --- binaries/data/mods/public/simulation/components/Identity.js +++ binaries/data/mods/public/simulation/components/Identity.js @@ -73,14 +73,6 @@ "" + "" + "" + - "" + - "" + - "tokens" + - "" + - "" + - "" + - "" + - "" + "" + "" + "" + @@ -111,11 +103,6 @@ this.controllable = this.template.Controllable ? this.template.Controllable == "true" : true; }; -Identity.prototype.HasSomeFormation = function() -{ - return this.GetFormationsList().length > 0; -}; - Identity.prototype.GetCiv = function() { return this.template.Civ; @@ -164,18 +151,6 @@ return this.GetClassesList().indexOf(name) != -1; }; -Identity.prototype.GetFormationsList = function() -{ - if (this.template.Formations && this.template.Formations._string) - return this.template.Formations._string.split(/\s+/); - return []; -}; - -Identity.prototype.CanUseFormation = function(template) -{ - return this.GetFormationsList().indexOf(template) != -1; -}; - Identity.prototype.GetSelectionGroupName = function() { return this.template.SelectionGroupName || ""; Index: binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- binaries/data/mods/public/simulation/components/UnitAI.js +++ binaries/data/mods/public/simulation/components/UnitAI.js @@ -20,6 +20,14 @@ "" + "" + "" + + "" + + "" + + "" + + "tokens" + + "" + + "" + + "" + + "" + "" + "" + "" + @@ -3474,6 +3482,16 @@ return (this.formationController != INVALID_ENTITY); }; +UnitAI.prototype.GetFormationsList = function() +{ + return this.template.Formations?._string?.split(/\s+/) || []; +}; + +UnitAI.prototype.CanUseFormation = function(formation) +{ + return this.GetFormationsList().includes(formation); +}; + /** * For now, entities with a RoamDistance are animals. */ Index: binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js +++ binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js @@ -569,8 +569,6 @@ "HasClass": function() { return true; }, "IsUndeletable": function() { return false; }, "IsControllable": function() { return true; }, - "HasSomeFormation": function() { return false; }, - "GetFormationsList": function() { return []; }, }); AddMock(10, IID_Position, { @@ -600,8 +598,6 @@ "classes": ["class1", "class2"], "selectionGroupName": "Selection Group Name", "canDelete": true, - "hasSomeFormation": false, - "formations": [], "controllable": true, }, "position": { "x": 1, "y": 2, "z": 3 }, Index: binaries/data/mods/public/simulation/components/tests/test_Identity.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Identity.js +++ binaries/data/mods/public/simulation/components/tests/test_Identity.js @@ -13,8 +13,6 @@ TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetClassesList(), []); TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetVisibleClassesList(), []); TS_ASSERT_EQUALS(cmpIdentity.HasClass("CitizenSoldier"), false); -TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetFormationsList(), []); -TS_ASSERT_EQUALS(cmpIdentity.CanUseFormation("special/formations/skirmish"), false); TS_ASSERT_EQUALS(cmpIdentity.GetSelectionGroupName(), ""); TS_ASSERT_EQUALS(cmpIdentity.GetGenericName(), "Iberian Skirmisher"); @@ -33,7 +31,6 @@ "Rank": "Basic", "Classes": { "_string": "CitizenSoldier Human Organic" }, "VisibleClasses": { "_string": "Javelineer" }, - "Formations": { "_string": "special/formations/skirmish" }, "Icon": "units/iber_infantry_javelineer.png", "RequiredTechnology": "phase_town" }); @@ -46,9 +43,6 @@ TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetVisibleClassesList(), ["Javelineer"]); TS_ASSERT_EQUALS(cmpIdentity.HasClass("CitizenSoldier"), true); TS_ASSERT_EQUALS(cmpIdentity.HasClass("Female"), false); -TS_ASSERT_UNEVAL_EQUALS(cmpIdentity.GetFormationsList(), ["special/formations/skirmish"]); -TS_ASSERT_EQUALS(cmpIdentity.CanUseFormation("special/formations/skirmish"), true); -TS_ASSERT_EQUALS(cmpIdentity.CanUseFormation("special/formations/line"), false); TS_ASSERT_EQUALS(cmpIdentity.GetSelectionGroupName(), "units/iber/infantry_javelineer_b"); TS_ASSERT_EQUALS(cmpIdentity.GetGenericName(), "Iberian Skirmisher"); Index: binaries/data/mods/public/simulation/helpers/Commands.js =================================================================== --- binaries/data/mods/public/simulation/helpers/Commands.js +++ binaries/data/mods/public/simulation/helpers/Commands.js @@ -1597,12 +1597,11 @@ if (!cmpUnitAI || !cmpPosition || !cmpPosition.IsInWorld()) continue; - let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); // TODO: We only check if the formation is usable by some units // if we move them to it. We should check if we can use formations // for the other cases. let nullFormation = (formationTemplate || cmpUnitAI.GetFormationTemplate()) == NULL_FORMATION; - if (nullFormation || !cmpIdentity || !cmpIdentity.CanUseFormation(formationTemplate || NULL_FORMATION)) + if (nullFormation || !cmpUnitAI.CanUseFormation(formationTemplate || NULL_FORMATION)) { if (nullFormation && cmpUnitAI.GetFormationController()) cmpUnitAI.LeaveFormation(cmd.queued || false); @@ -1744,19 +1743,14 @@ // TODO: should check the player's civ is allowed to use this formation // See simulation/components/Player.js GetFormations() for a list of all allowed formations - var requirements = GetFormationRequirements(formationTemplate); + const requirements = GetFormationRequirements(formationTemplate); if (!requirements) return false; - var count = 0; - for (let ent of ents) - { - var cmpIdentity = Engine.QueryInterface(ent, IID_Identity); - if (!cmpIdentity || !cmpIdentity.CanUseFormation(formationTemplate)) - continue; - - ++count; - } + let count = 0; + for (const ent of ents) + if (Engine.QueryInterface(ent, IID_UnitAI)?.CanUseFormation(formationTemplate)) + ++count; return count >= requirements.minCount; }