Select formations as a whole by default.

One can override this behaviour by using a hotkey when (de)selecting
entities.
The aim of this system is to reduce micromanagement a bit.

Differential revision: https://code.wildfiregames.com/D2175
Comments by: @Angen, @azayrahmad, @Langbart, @marder, @Stan,
@submariner, @wowgetoffyourcellphone, @wraitii
Refs. #4545

This was SVN commit r25939.
This commit is contained in:
Freagarach 2021-09-24 06:11:10 +00:00
parent 7382a4885b
commit a70a20fd42
6 changed files with 48 additions and 8 deletions

View File

@ -284,6 +284,7 @@ idleworker = Period, NumDecimal ; Select next idle worker
idlewarrior = Slash, NumDivide ; Select next idle warrior
idleunit = BackSlash ; Select next idle unit
offscreen = Alt ; Include offscreen units in selection
singleselection = "" ; Select only one entity of a formation.
[hotkey.selection.group.add]
0 = "Shift+0", "Shift+Num0"
1 = "Shift+1", "Shift+Num1"

View File

@ -55,6 +55,10 @@
"name": "Include offscreen",
"desc": "Include offscreen units in selection."
},
"selection.singleselection": {
"name": "Single selection",
"desc": "Select only one entity of a formation."
},
"selection.group.save.0": {
"name": "Set Control Group 0",
"desc": "Save current selection as Control Group 0."

View File

@ -1170,7 +1170,7 @@ function popOneFromSelection(action)
));
if (unit)
{
g_Selection.removeList([unit]);
g_Selection.removeList([unit], true);
return [unit];
}
return null;

View File

@ -284,9 +284,9 @@ EntitySelection.prototype.addList = function(ents, quiet, force = false)
if (firstEntState && firstEntState.player != g_ViewedPlayer && !force)
return;
let added = [];
const added = [];
for (const ent of ents)
for (const ent of this.addFormationMembers(ents))
{
if (this.selected.size >= g_MaxSelectionSize)
break;
@ -324,11 +324,15 @@ EntitySelection.prototype.addList = function(ents, quiet, force = false)
this.onChange();
};
EntitySelection.prototype.removeList = function(ents)
/**
* @param {number[]} ents - The entities to remove.
* @param {boolean} dontAddFormationMembers - If true we need to exclude adding formation members.
*/
EntitySelection.prototype.removeList = function(ents, dontAddFormationMembers = false)
{
var removed = [];
const removed = [];
for (let ent of ents)
for (const ent of dontAddFormationMembers ? ents : this.addFormationMembers(ents))
if (this.selected.has(ent))
{
this.groups.removeEnt(ent);
@ -407,9 +411,10 @@ EntitySelection.prototype.filter = function(condition)
return result;
};
EntitySelection.prototype.setHighlightList = function(ents)
EntitySelection.prototype.setHighlightList = function(entities)
{
const highlighted = new Set();
const ents = this.addFormationMembers(entities);
for (const ent of ents)
highlighted.add(ent);
@ -461,6 +466,28 @@ EntitySelection.prototype.selectAndMoveTo = function(entityID)
Engine.CameraMoveTo(entState.position.x, entState.position.z);
}
/**
* Adds the formation members of a selected entities to the selection.
* @param {number[]} entities - The entity IDs of selected entities.
* @return {number[]} - Some more entity IDs if part of a formation was selected.
*/
EntitySelection.prototype.addFormationMembers = function(entities)
{
if (!entities.length || Engine.HotkeyIsPressed("selection.singleselection"))
return entities;
const result = new Set(entities);
for (const entity of entities)
{
const entState = GetEntityState(+entity);
if (entState?.unitAI?.formation)
for (const member of GetEntityState(+entState.unitAI.formation).formation.members)
result.add(member);
}
return result;
};
/**
* Cache some quantities which depends only on selection
*/

View File

@ -281,6 +281,12 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
"controllable": cmpIdentity.IsControllable()
};
const cmpFormation = Engine.QueryInterface(ent, IID_Formation);
if (cmpFormation)
ret.formation = {
"members": cmpFormation.GetMembers()
};
let cmpPosition = Engine.QueryInterface(ent, IID_Position);
if (cmpPosition && cmpPosition.IsInWorld())
ret.position = cmpPosition.GetPosition();
@ -416,7 +422,8 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
"isGuarding": cmpUnitAI.IsGuardOf(),
"canPatrol": cmpUnitAI.CanPatrol(),
"selectableStances": cmpUnitAI.GetSelectableStances(),
"isIdle": cmpUnitAI.IsIdle()
"isIdle": cmpUnitAI.IsIdle(),
"formation": cmpUnitAI.GetFormationController()
};
let cmpGuard = Engine.QueryInterface(ent, IID_Guard);

View File

@ -10,6 +10,7 @@ Engine.LoadComponentScript("interfaces/Resistance.js");
Engine.LoadComponentScript("interfaces/DeathDamage.js");
Engine.LoadComponentScript("interfaces/EndGameManager.js");
Engine.LoadComponentScript("interfaces/EntityLimits.js");
Engine.LoadComponentScript("interfaces/Formation.js");
Engine.LoadComponentScript("interfaces/Foundation.js");
Engine.LoadComponentScript("interfaces/Garrisonable.js");
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");