1
0
forked from 0ad/0ad

Adjusts unit selection behavior to account for ranks. Triple-click now selects units regardless of their rank.

Adds modifier hotkey to select units offscreen.
Fixes #826.

This was SVN commit r9443.
This commit is contained in:
historic_bruno 2011-05-05 01:27:49 +00:00
parent f423e269e2
commit 26de01cdd1
14 changed files with 63 additions and 22 deletions

View File

@ -134,6 +134,7 @@ hotkey.console.paste = "Ctrl+V" ; Paste clipboard to console
hotkey.selection.add = Shift ; Add units to selection
hotkey.selection.remove = Ctrl ; Remove units from selection
hotkey.selection.idle = Period ; Select next idle unit
hotkey.selection.offscreen = Alt ; Include offscreen units in selection
hotkey.selection.group.select.0 = 0
hotkey.selection.group.save.0 = "Ctrl+0"
hotkey.selection.group.add.0 = "Shift+0"

View File

@ -726,7 +726,9 @@ function handleInputAfterGui(ev)
return true;
}
var onScreenOnly;
var showOffscreen = Engine.HotkeyIsPressed("selection.offscreen");
var matchRank;
var templateToMatch;
var selectedEntity = ents[0];
var now = new Date();
@ -738,20 +740,20 @@ function handleInputAfterGui(ev)
if (!doubleClicked)
{
// If double click hasn't already occurred, this is a double click.
// Select only similar on-screen units
onScreenOnly = true;
// Select units matching exact rank
templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
matchRank = true;
doubleClicked = true;
}
else
{
// Double click has already occurred, so this is a triple click.
// Select all similar units whether they are on-screen or not
onScreenOnly = false;
// Select all similar units regardless of rank
templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).identity.selectionGroupName;
matchRank = false;
}
var templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
ents = Engine.PickSimilarFriendlyEntities(templateToMatch, onScreenOnly);
ents = Engine.PickSimilarFriendlyEntities(templateToMatch, showOffscreen, matchRank);
}
else
{

View File

@ -119,7 +119,8 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
{
ret.identity = {
"rank": cmpIdentity.GetRank(),
"classes": cmpIdentity.GetClassesList()
"classes": cmpIdentity.GetClassesList(),
"selectionGroupName": cmpIdentity.GetSelectionGroupName()
};
}

View File

@ -139,4 +139,9 @@ Identity.prototype.HasClass = function(name)
return this.GetClassesList().indexOf(name) != -1;
};
Identity.prototype.GetSelectionGroupName = function()
{
return (this.template.SelectionGroupName || "");
}
Engine.RegisterComponentType(IID_Identity, "Identity", Identity);

View File

@ -1 +0,0 @@
Engine.RegisterInterface("Identity");

View File

@ -2,6 +2,7 @@
<Entity parent="template_unit_cavalry_melee_spearman">
<Identity>
<Civ>iber</Civ>
<SelectionGroupName>units/iber_cavalry_spearman_b</SelectionGroupName>
<SpecificName>Epones</SpecificName>
<Icon>units/iber_cavalry_spearman.png</Icon>
<History>Armed like the light infantry, Iberian cavalry were often pursued as mercenaries, especially by the Carthaginians. Mounted on excellent horses and wielding high-grade swords they were capable of taking on heavy or light cavalry. As with all Iberians armor was scarce, but they wore the ubiquitous sinew caps made famous by the peoples of the peninsula.</History>

View File

@ -2,6 +2,7 @@
<Entity parent="template_unit_infantry_ranged_javelinist">
<Identity>
<Civ>iber</Civ>
<SelectionGroupName>units/iber_infantry_javelinist_b</SelectionGroupName>
<SpecificName>Caetrati Lusitano</SpecificName>
<History>Iberians, especially the Lusitanians, were good at ranged combat and ambushing enemy columns. They throw heavy iron javelins and sometimes even add burning pitch to them, making them good as a cheap siege weapon.</History>
<Icon>units/cart_cavalry_javelinist.png</Icon>

View File

@ -2,6 +2,7 @@
<Entity parent="template_unit_infantry_ranged_slinger">
<Identity>
<Civ>iber</Civ>
<SelectionGroupName>units/iber_infantry_slinger_b</SelectionGroupName>
<SpecificName>Karsken</SpecificName>
<Icon>units/iber_infantry_slinger.png</Icon>
<History>Iberian slingers were the undisputed masters of the weapon and extracted a high toll of the enemy. Going into combat scantily clad at best, the slinger carried three slings tied around his waist, each of a different length allowing him to attack opponents from all ranges. Unlike other cultures, the Iberian slingers threw rocks instead of specially made lead shot.</History>

View File

@ -2,6 +2,7 @@
<Entity parent="template_unit_infantry_melee_spearman">
<Identity>
<Civ>iber</Civ>
<SelectionGroupName>units/iber_infantry_spearman_b</SelectionGroupName>
<SpecificName>Scutari</SpecificName>
<Icon>units/iber_infantry_spearman.png</Icon>
<History>A long-bladed spear was a chief melee weapon of the Iberian infantry, often used after the javelins had been thrown. Typically carried by infantry known as scutarii for their long oval body shields, the spearmen would close in formation to attack their opponents. Usually lightly armored, they were quick and had a ferocious reputation.</History>

View File

@ -2,6 +2,7 @@
<Entity parent="template_unit_infantry_melee_swordsman">
<Identity>
<Civ>iber</Civ>
<SelectionGroupName>units/iber_infantry_swordsman_b</SelectionGroupName>
<SpecificName>Caetrati</SpecificName>
<Icon>units/iber_infantry_swordsman.png</Icon>
<History>The Iberians were master sword-smiths and the falcata was their greatest creation. Wielded by superb swordsmen equipped with light armor and a buckler known as a caetra, they caused untold carnage. Thanks to this Iberian infantry were fast and agile unlike many of their opponents and could bite hard when they attacked. Their skill with sword and buckler were legendary, allowing them to go toe-to-toe with heavy infantry.</History>

View File

@ -133,9 +133,9 @@ std::vector<entity_id_t> PickFriendlyEntitiesInRect(void* UNUSED(cbdata), int x0
return EntitySelection::PickEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x0, y0, x1, y1, player);
}
std::vector<entity_id_t> PickSimilarFriendlyEntities(void* UNUSED(cbdata), std::string templateName, bool onScreenOnly)
std::vector<entity_id_t> PickSimilarFriendlyEntities(void* UNUSED(cbdata), std::string templateName, bool includeOffScreen, bool matchRank)
{
return EntitySelection::PickSimilarEntities(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), templateName, g_Game->GetPlayerID(), onScreenOnly);
return EntitySelection::PickSimilarEntities(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), templateName, g_Game->GetPlayerID(), includeOffScreen, matchRank);
}
CFixedVector3D GetTerrainAtPoint(void* UNUSED(cbdata), int x, int y)
@ -465,7 +465,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
// Entity picking
scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, int, &PickEntitiesAtPoint>("PickEntitiesAtPoint");
scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, int, int, int, int, &PickFriendlyEntitiesInRect>("PickFriendlyEntitiesInRect");
scriptInterface.RegisterFunction<std::vector<entity_id_t>, std::string, bool, &PickSimilarFriendlyEntities>("PickSimilarFriendlyEntities");
scriptInterface.RegisterFunction<std::vector<entity_id_t>, std::string, bool, bool, &PickSimilarFriendlyEntities>("PickSimilarFriendlyEntities");
scriptInterface.RegisterFunction<CFixedVector3D, int, int, &GetTerrainAtPoint>("GetTerrainAtPoint");
// Network / game setup functions

View File

@ -76,6 +76,9 @@ COMPONENT(Footprint)
INTERFACE(GuiInterface)
COMPONENT(GuiInterfaceScripted)
INTERFACE(Identity)
COMPONENT(IdentityScripted)
INTERFACE(Minimap)
COMPONENT(Minimap)

View File

@ -21,6 +21,7 @@
#include "graphics/Camera.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpIdentity.h"
#include "simulation2/components/ICmpOwnership.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/components/ICmpTemplateManager.h"
@ -132,17 +133,38 @@ std::vector<entity_id_t> EntitySelection::PickEntitiesInRect(CSimulation2& simul
return hitEnts;
}
std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, int owner, bool onScreenOnly)
std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, int owner, bool includeOffScreen, bool matchRank)
{
CmpPtr<ICmpTemplateManager> cmpTemplateManager(simulation, SYSTEM_ENTITY);
CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);
std::vector<entity_id_t> hitEnts;
std::vector<entity_id_t> entities = cmpTemplateManager->GetEntitiesUsingTemplate(templateName);
for (std::vector<entity_id_t>::iterator it = entities.begin(); it != entities.end(); ++it)
{
entity_id_t ent = *it;
const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable);
for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it)
{
entity_id_t ent = it->first;
CmpPtr<ICmpIdentity> cmpIdentity(simulation.GetSimContext(), ent);
std::string groupName;
if (!cmpIdentity.null())
{
groupName = cmpIdentity->GetSelectionGroupName();
}
if (!matchRank && !groupName.empty())
{
// There's a selection group so match that
if (groupName.compare(templateName) != 0)
continue;
}
else
{
// Fall back to exact template name matching
if (cmpTemplateManager->GetCurrentTemplateName(ent).compare(templateName) != 0)
continue;
}
// Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld)
// In this case, the checking is done to avoid selecting garrisoned units
@ -154,7 +176,8 @@ std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simu
if (cmpOwnership.null() || cmpOwnership->GetOwner() != owner)
continue;
if (onScreenOnly)
// Ignore off screen entities
if (!includeOffScreen)
{
// Find the current interpolated model position.
CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent);

View File

@ -49,10 +49,12 @@ std::vector<entity_id_t> PickEntitiesInRect(CSimulation2& simulation, const CCam
/**
* Finds all entities with the given entity template name, that belong to player @p owner.
* If @p onScreenOnly then only entities visible on the screen will be selected,
* else all entities visible in the world will be selected.
* If @p includeOffScreen then all entities visible in the world will be selected,
* else only entities visible on the screen will be selected.
* If @p matchRank then only entities that exactly match @p templateName will be selected,
* else entities with matching SelectionGroupName will be selected.
*/
std::vector<entity_id_t> PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, int owner, bool onScreenOnly);
std::vector<entity_id_t> PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, int owner, bool includeOffScreen, bool matchRank);
} // namespace