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