1
0
forked from mirrors/0ad

Enforce formation required member count

Disband formation with less memebers than required.
Do not allow one member in formation.
Show only formations entities actually are allowed to form. (do not show
wedge for infantry)

Differential Revision: https://code.wildfiregames.com/D1462
Fixes: #5119
Comments by: elexis, bb, temple, Stan, Freagarach
This was SVN commit r23450.
This commit is contained in:
Angen
2020-01-27 17:49:06 +00:00
parent 17c9950cee
commit 9c2e9d7893
20 changed files with 47 additions and 34 deletions
@@ -292,24 +292,20 @@ g_SelectionPanels.Formation = {
if (unitEntStates.some(state => !hasClass(state, "Unit")))
return [];
if (unitEntStates.every(state => !state.identity || !state.identity.hasSomeFormation))
return [];
if (!g_AvailableFormations.has(unitEntStates[0].player))
g_AvailableFormations.set(unitEntStates[0].player, Engine.GuiInterfaceCall("GetAvailableFormations", unitEntStates[0].player));
let availableFormations = g_AvailableFormations.get(unitEntStates[0].player);
// Hide the panel if all formations are disabled
if (availableFormations.some(formation => canMoveSelectionIntoFormation(formation)))
return availableFormations;
return [];
return g_AvailableFormations.get(unitEntStates[0].player).filter(formation => unitEntStates.some(state => !!state.identity && state.identity.formations.indexOf(formation) != -1));
},
"setupButton": function(data)
{
if (!g_FormationsInfo.has(data.item))
g_FormationsInfo.set(data.item, Engine.GuiInterfaceCall("GetFormationInfoFromTemplate", { "templateName": data.item }));
let formationInfo = g_FormationsInfo.get(data.item);
let formationOk = canMoveSelectionIntoFormation(data.item);
let formationOk = data.item == "special/formations/null" || canMoveSelectionIntoFormation(data.item);
let unitIds = data.unitEntStates.map(state => state.id);
let formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", {
"ents": unitIds,
@@ -320,6 +316,7 @@ g_SelectionPanels.Formation = {
performFormation(unitIds, data.item);
};
let formationInfo = g_FormationsInfo.get(data.item);
let tooltip = translate(formationInfo.name);
if (!formationOk && formationInfo.tooltip)
tooltip += "\n" + coloredText(translate(formationInfo.tooltip), "red");
@@ -7,8 +7,12 @@ Formation.prototype.Schema =
"<element name='Icon'>" +
"<text/>" +
"</element>" +
"<element name='RequiredMemberCount' a:help='Minimum number of entities the formation should contain'>" +
"<data type='nonNegativeInteger'/>" +
"<element name='RequiredMemberCount' a:help='Minimum number of entities the formation should contain (at least 2)'>" +
"<data type='integer'>" +
"<param name='minInclusive'>"+
"2"+
"</param>"+
"</data>" +
"</element>" +
"<element name='DisabledTooltip' a:help='Tooltip shown when the formation is disabled'>" +
"<text/>" +
@@ -319,16 +323,16 @@ Formation.prototype.RemoveMembers = function(ents, renamed = false)
this.members = this.members.filter(function(e) { return ents.indexOf(e) == -1; });
this.inPosition = this.inPosition.filter(function(e) { return ents.indexOf(e) == -1; });
for (var ent of ents)
for (let ent of ents)
{
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
cmpUnitAI.UpdateWorkOrders();
cmpUnitAI.SetFormationController(INVALID_ENTITY);
}
for (var ent of this.formationMembersWithAura)
for (let ent of this.formationMembersWithAura)
{
var cmpAuras = Engine.QueryInterface(ent, IID_Auras);
let cmpAuras = Engine.QueryInterface(ent, IID_Auras);
cmpAuras.RemoveFormationAura(ents);
// the unit with the aura is also removed from the formation
@@ -340,7 +344,7 @@ Formation.prototype.RemoveMembers = function(ents, renamed = false)
// If there's nobody left, destroy the formation
// unless this is a rename where we can have 0 members temporarily.
if (this.members.length == 0 && !renamed)
if (this.members.length < +this.template.RequiredMemberCount && !renamed)
{
Engine.DestroyEntity(this.entity);
return;
@@ -264,7 +264,9 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
"classes": cmpIdentity.GetClassesList(),
"visibleClasses": cmpIdentity.GetVisibleClassesList(),
"selectionGroupName": cmpIdentity.GetSelectionGroupName(),
"canDelete": !cmpIdentity.IsUndeletable()
"canDelete": !cmpIdentity.IsUndeletable(),
"hasSomeFormation": cmpIdentity.HasSomeFormation(),
"formations": cmpIdentity.GetFormationsList(),
};
let cmpPosition = Engine.QueryInterface(ent, IID_Position);
@@ -104,6 +104,11 @@ Identity.prototype.Init = function()
this.phenotype = "default";
};
Identity.prototype.HasSomeFormation = function()
{
return this.GetFormationsList().length > 0;
};
Identity.prototype.GetCiv = function()
{
return this.template.Civ;
@@ -544,7 +544,9 @@ AddMock(10, IID_Identity, {
GetRank: function() { return "foo"; },
GetSelectionGroupName: function() { return "Selection Group Name"; },
HasClass: function() { return true; },
IsUndeletable: function() { return false; }
IsUndeletable: function() { return false; },
HasSomeFormation: function() { return false; },
GetFormationsList: function() { return []; },
});
AddMock(10, IID_Position, {
@@ -574,7 +576,9 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetEntityState(-1, 10), {
"classes": ["class1", "class2"],
"visibleClasses": ["class3", "class4"],
"selectionGroupName": "Selection Group Name",
"canDelete": true
"canDelete": true,
"hasSomeFormation": false,
"formations": [],
},
"position": {x:1, y:2, z:3},
"hitpoints": 50,
@@ -145,9 +145,9 @@
"special/formations/column_open",
"special/formations/line_open",
"special/formations/flank",
"special/formations/battle_line",
"special/formations/skirmish",
"special/formations/wedge",
"special/formations/battle_line",
"special/formations/phalanx"
],
"AINames":
@@ -149,9 +149,9 @@
"special/formations/column_open",
"special/formations/line_open",
"special/formations/flank",
"special/formations/battle_line",
"special/formations/skirmish",
"special/formations/wedge",
"special/formations/battle_line",
"special/formations/phalanx"
],
"AINames":
@@ -141,9 +141,9 @@
"special/formations/column_open",
"special/formations/line_open",
"special/formations/flank",
"special/formations/battle_line",
"special/formations/skirmish",
"special/formations/wedge",
"special/formations/battle_line",
"special/formations/syntagma"
],
"AINames":
@@ -141,9 +141,9 @@
"special/formations/column_open",
"special/formations/line_open",
"special/formations/flank",
"special/formations/battle_line",
"special/formations/skirmish",
"special/formations/wedge",
"special/formations/battle_line",
"special/formations/phalanx",
"special/formations/syntagma"
],
@@ -141,9 +141,9 @@
"special/formations/column_open",
"special/formations/line_open",
"special/formations/flank",
"special/formations/battle_line",
"special/formations/skirmish",
"special/formations/wedge",
"special/formations/battle_line",
"special/formations/phalanx"
],
"AINames":
@@ -150,9 +150,9 @@
"special/formations/column_open",
"special/formations/line_open",
"special/formations/flank",
"special/formations/battle_line",
"special/formations/skirmish",
"special/formations/wedge",
"special/formations/battle_line",
"special/formations/phalanx",
"special/formations/syntagma"
],
@@ -126,9 +126,9 @@
"special/formations/column_open",
"special/formations/line_open",
"special/formations/flank",
"special/formations/battle_line",
"special/formations/skirmish",
"special/formations/wedge",
"special/formations/battle_line",
"special/formations/testudo",
"special/formations/anti_cavalry"
],
@@ -149,9 +149,9 @@
"special/formations/column_open",
"special/formations/line_open",
"special/formations/flank",
"special/formations/battle_line",
"special/formations/skirmish",
"special/formations/wedge",
"special/formations/battle_line",
"special/formations/phalanx",
"special/formations/syntagma"
],
@@ -141,9 +141,9 @@
"special/formations/column_open",
"special/formations/line_open",
"special/formations/flank",
"special/formations/battle_line",
"special/formations/skirmish",
"special/formations/wedge",
"special/formations/battle_line",
"special/formations/phalanx"
],
"AINames":
@@ -3,5 +3,6 @@
<Formation>
<Icon>formations/null.png</Icon>
<FormationName>None</FormationName>
<DisabledTooltip/>
</Formation>
</Entity>
@@ -2,7 +2,7 @@
<Entity parent="template_formation">
<Formation>
<Icon>formations/skirmish.png</Icon>
<DisabledTooltip>Requires a Ranged Soldier.</DisabledTooltip>
<DisabledTooltip>At least 2 units are required (only ranged).</DisabledTooltip>
<FormationName>Skirmish</FormationName>
<ShiftRows>true</ShiftRows>
<UnitSeparationWidthMultiplier>2</UnitSeparationWidthMultiplier>
@@ -3,7 +3,7 @@
<Formation>
<Icon>formations/syntagma.png</Icon>
<RequiredMemberCount>16</RequiredMemberCount>
<DisabledTooltip>Requires at least 16 Infantry Pikemen.</DisabledTooltip>
<DisabledTooltip>At least 16 units are required (only pike infantry).</DisabledTooltip>
<SortingClasses>Hero Champion Elite Advanced Basic</SortingClasses>
<FormationName>Syntagma</FormationName>
<FormationShape>square</FormationShape>
@@ -3,7 +3,7 @@
<Formation>
<Icon>formations/testudo.png</Icon>
<RequiredMemberCount>16</RequiredMemberCount>
<DisabledTooltip>Requires at least 16 Melee Infantry.</DisabledTooltip>
<DisabledTooltip>At least 16 units are required (only melee infantry).</DisabledTooltip>
<SortingClasses>Hero Champion Elite Advanced Basic</SortingClasses>
<FormationName>Testudo</FormationName>
<FormationShape>square</FormationShape>
@@ -3,7 +3,7 @@
<Formation>
<Icon>formations/wedge.png</Icon>
<RequiredMemberCount>6</RequiredMemberCount>
<DisabledTooltip>Requires at least 6 Cavalry.</DisabledTooltip>
<DisabledTooltip>At least 6 units are required (only cavalry).</DisabledTooltip>
<FormationName>Wedge</FormationName>
<FormationShape>triangle</FormationShape>
<ShiftRows>true</ShiftRows>
@@ -21,8 +21,8 @@
</VisualActor>
-->
<Formation>
<RequiredMemberCount>1</RequiredMemberCount>
<DisabledTooltip/>
<RequiredMemberCount>2</RequiredMemberCount>
<DisabledTooltip>At least 2 units are required.</DisabledTooltip>
<SpeedMultiplier>1</SpeedMultiplier>
<FormationShape>square</FormationShape>
<SortingClasses>Hero Champion Cavalry Melee Ranged</SortingClasses>