1
0
forked from 0ad/0ad

# Add double-click and triple-click selection modes, based on patch by Christoph and evans.

Fixes #515.

This was SVN commit r8544.
This commit is contained in:
Ykkrosh 2010-11-07 00:12:55 +00:00
parent f45c74c368
commit 644b9aedea
7 changed files with 130 additions and 6 deletions

View File

@ -37,9 +37,18 @@ specialKeyStates[SDLK_RCTRL] = 0;
specialKeyStates[SDLK_LCTRL] = 0;
specialKeyStates[SDLK_RALT] = 0;
specialKeyStates[SDLK_LALT] = 0;
// (TODO: maybe we should fix the hotkey system to be usable in this situation,
// rather than hardcoding Shift into this code?)
// rather than hardcoding Shift/Ctrl/Alt into this code?)
// Number of pixels the mouse can move before the action is considered a drag
var maxDragDelta = 4;
// Time in milliseconds in which a double click is recognized
const doubleClickTime = 500;
var doubleClickTimer = 0;
var doubleClicked = false;
// Store the previously clicked entity - ensure a double/triple click happens on the same entity
var prevClickedEntity = 0;
function updateCursor()
{
@ -125,7 +134,6 @@ function determineAction(x, y, fromMinimap)
var playerOwned = ((targetState.player == entState.player)? true : false);
var enemyOwned = ((targetState.player != entState.player)? true : false);
var gaiaOwned = ((targetState.player == 0)? true : false);
if (targetState.garrisonHolder && playerOwned && ctrlPressed)
{
@ -571,7 +579,7 @@ function handleInputAfterGui(ev)
// If the mouse moved further than a limit, switch to bandbox mode
var dragDeltaX = ev.x - dragStart[0];
var dragDeltaY = ev.y - dragStart[1];
var maxDragDelta = 4;
if (Math.abs(dragDeltaX) >= maxDragDelta || Math.abs(dragDeltaY) >= maxDragDelta)
{
inputState = INPUT_BANDBOXING;
@ -593,12 +601,51 @@ function handleInputAfterGui(ev)
return true;
}
var onScreenOnly;
var selectedEntity = ents[0];
var now = new Date();
if ((now.getTime() - doubleClickTimer < doubleClickTime) && (selectedEntity == prevClickedEntity))
{
// Double click or triple click has occurred
// Check for double click or triple click
if (!doubleClicked)
{
// If double click hasn't already occurred, this is a double click.
// Select only similar on-screen units
onScreenOnly = 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;
}
var templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
ents = Engine.PickSimilarFriendlyEntities(templateToMatch, onScreenOnly);
}
else
{
// It's single click right now but it may become double or triple click
doubleClicked = false;
doubleClickTimer = now.getTime();
prevClickedEntity = selectedEntity;
// We only want to include the first picked unit in the selection
ents = [ents[0]];
}
// If shift is pressed, don't reset the selection, but allow units to be added to the existing selection
var addition = (specialKeyStates[SDLK_RSHIFT] || specialKeyStates[SDLK_LSHIFT]);
if (!addition)
g_Selection.reset();
g_Selection.reset();
g_Selection.addList([ents[0]]);
g_Selection.addList(ents);
inputState = INPUT_NORMAL;
return true;
}

View File

@ -128,6 +128,11 @@ 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)
{
return EntitySelection::PickSimilarEntities(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), templateName, g_Game->GetPlayerID(), onScreenOnly);
}
CFixedVector3D GetTerrainAtPoint(void* UNUSED(cbdata), int x, int y)
{
CVector3D pos = g_Game->GetView()->GetCamera()->GetWorldCoordinates(x, y, true);
@ -374,6 +379,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<CFixedVector3D, int, int, &GetTerrainAtPoint>("GetTerrainAtPoint");
// Network / game setup functions

View File

@ -131,6 +131,8 @@ public:
virtual std::vector<std::wstring> FindAllTemplates();
virtual std::vector<entity_id_t> GetEntitiesUsingTemplate(std::string templateName);
private:
// Entity template XML validator
RelaxNGValidator m_Validator;
@ -405,6 +407,20 @@ std::vector<std::wstring> CCmpTemplateManager::FindAllTemplates()
return templates;
}
/**
* Get the list of entities using the specified template
*/
std::vector<entity_id_t> CCmpTemplateManager::GetEntitiesUsingTemplate(std::string templateName)
{
std::vector<entity_id_t> entities;
for (std::map<entity_id_t, std::string>::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it)
{
if(it->second == templateName)
entities.push_back(it->first);
}
return entities;
}
void CCmpTemplateManager::CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse)
{
// We only want to include components which are necessary (for the visual previewing of an entity)

View File

@ -25,4 +25,5 @@ BEGIN_INTERFACE_WRAPPER(TemplateManager)
DEFINE_INTERFACE_METHOD_1("GetTemplate", const CParamNode*, ICmpTemplateManager, GetTemplate, std::string)
DEFINE_INTERFACE_METHOD_1("GetCurrentTemplateName", std::string, ICmpTemplateManager, GetCurrentTemplateName, entity_id_t)
DEFINE_INTERFACE_METHOD_0("FindAllTemplates", std::vector<std::wstring>, ICmpTemplateManager, FindAllTemplates)
DEFINE_INTERFACE_METHOD_1("GetEntitiesUsingTemplate", std::vector<entity_id_t>, ICmpTemplateManager, GetEntitiesUsingTemplate, std::string)
END_INTERFACE_WRAPPER(TemplateManager)

View File

@ -79,6 +79,11 @@ public:
*/
virtual std::string GetCurrentTemplateName(entity_id_t ent) = 0;
/**
* Returns the list of entities having the specified template.
*/
virtual std::vector<entity_id_t> GetEntitiesUsingTemplate(std::string templateName) = 0;
/**
* Returns a list of strings that could be validly passed as @c templateName to LoadTemplate.
* (This includes "actor|foo" etc names).

View File

@ -23,6 +23,7 @@
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpOwnership.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/components/ICmpTemplateManager.h"
#include "simulation2/components/ICmpSelectable.h"
#include "simulation2/components/ICmpVisual.h"
@ -130,3 +131,44 @@ std::vector<entity_id_t> EntitySelection::PickEntitiesInRect(CSimulation2& simul
return hitEnts;
}
std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, std::string templateName, int owner, bool onScreenOnly)
{
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;
// 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
if (cmpRangeManager->GetLosVisibility(ent, owner) == ICmpRangeManager::VIS_HIDDEN)
continue;
// Ignore entities not owned by 'owner'
CmpPtr<ICmpOwnership> cmpOwnership(simulation.GetSimContext(), ent);
if (cmpOwnership.null() || cmpOwnership->GetOwner() != owner)
continue;
if (onScreenOnly)
{
// Find the current interpolated model position.
CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent);
if (cmpVisual.null())
continue;
CVector3D position = cmpVisual->GetPosition();
// Reject if it's not on-screen (e.g. it's behind the camera)
if (!camera.GetFrustum().IsPointVisible(position))
continue;
}
hitEnts.push_back(ent);
}
return hitEnts;
}

View File

@ -47,6 +47,13 @@ std::vector<entity_id_t> PickEntitiesAtPoint(CSimulation2& simulation, const CCa
*/
std::vector<entity_id_t> PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, int owner);
/**
* 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.
*/
std::vector<entity_id_t> PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, std::string templateName, int owner, bool onScreenOnly);
} // namespace
#endif // INCLUDED_SELECTION