diff --git a/binaries/data/mods/public/simulation/templates/other/bridge_hele.xml b/binaries/data/mods/public/simulation/templates/other/bridge_hele.xml
index 33dcbb3272..f7ae8243b6 100644
--- a/binaries/data/mods/public/simulation/templates/other/bridge_hele.xml
+++ b/binaries/data/mods/public/simulation/templates/other/bridge_hele.xml
@@ -20,7 +20,9 @@
-
+
+
+
72
true
diff --git a/binaries/data/mods/public/simulation/templates/other/bridge_wooden.xml b/binaries/data/mods/public/simulation/templates/other/bridge_wooden.xml
index 7021a4fd36..514ff7153e 100644
--- a/binaries/data/mods/public/simulation/templates/other/bridge_wooden.xml
+++ b/binaries/data/mods/public/simulation/templates/other/bridge_wooden.xml
@@ -20,7 +20,9 @@
-
+
+
+
72
true
diff --git a/binaries/data/mods/public/simulation/templates/special/territory_block.xml b/binaries/data/mods/public/simulation/templates/special/territory_block.xml
index d63567770d..52432b26b4 100644
--- a/binaries/data/mods/public/simulation/templates/special/territory_block.xml
+++ b/binaries/data/mods/public/simulation/templates/special/territory_block.xml
@@ -21,7 +21,9 @@
false
6.0
-
+
+
+
64
false
diff --git a/binaries/data/mods/public/simulation/templates/special/territory_pull.xml b/binaries/data/mods/public/simulation/templates/special/territory_pull.xml
index ebc95e2971..bd8c4acafe 100644
--- a/binaries/data/mods/public/simulation/templates/special/territory_pull.xml
+++ b/binaries/data/mods/public/simulation/templates/special/territory_pull.xml
@@ -21,7 +21,9 @@
false
6.0
-
+
+
+
0
false
diff --git a/binaries/data/mods/public/simulation/templates/template_entity_quasi.xml b/binaries/data/mods/public/simulation/templates/template_entity_quasi.xml
index f85ed76bae..5f702c5086 100644
--- a/binaries/data/mods/public/simulation/templates/template_entity_quasi.xml
+++ b/binaries/data/mods/public/simulation/templates/template_entity_quasi.xml
@@ -8,4 +8,7 @@
false
6.0
+
+
+
diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_flora_bush_berry.xml b/binaries/data/mods/public/simulation/templates/template_gaia_flora_bush_berry.xml
index 51f170307e..e979c8ed9b 100644
--- a/binaries/data/mods/public/simulation/templates/template_gaia_flora_bush_berry.xml
+++ b/binaries/data/mods/public/simulation/templates/template_gaia_flora_bush_berry.xml
@@ -16,7 +16,9 @@
200
food.fruit
-
+
+
+
diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_flora_tree.xml b/binaries/data/mods/public/simulation/templates/template_gaia_flora_tree.xml
index d0018d15da..bb74672b0f 100644
--- a/binaries/data/mods/public/simulation/templates/template_gaia_flora_tree.xml
+++ b/binaries/data/mods/public/simulation/templates/template_gaia_flora_tree.xml
@@ -15,7 +15,9 @@
200
wood.tree
-
+
+
+
diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_geo_mineral.xml b/binaries/data/mods/public/simulation/templates/template_gaia_geo_mineral.xml
index c190a98b21..f3a147ae96 100644
--- a/binaries/data/mods/public/simulation/templates/template_gaia_geo_mineral.xml
+++ b/binaries/data/mods/public/simulation/templates/template_gaia_geo_mineral.xml
@@ -15,7 +15,9 @@
1000
metal.ore
-
+
+
+
diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_geo_rock.xml b/binaries/data/mods/public/simulation/templates/template_gaia_geo_rock.xml
index 4f419c05d4..a9542935e5 100644
--- a/binaries/data/mods/public/simulation/templates/template_gaia_geo_rock.xml
+++ b/binaries/data/mods/public/simulation/templates/template_gaia_geo_rock.xml
@@ -15,7 +15,9 @@
1000
stone.rock
-
+
+
+
diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_ruins.xml b/binaries/data/mods/public/simulation/templates/template_gaia_ruins.xml
index 6af1b86399..86037bbc57 100644
--- a/binaries/data/mods/public/simulation/templates/template_gaia_ruins.xml
+++ b/binaries/data/mods/public/simulation/templates/template_gaia_ruins.xml
@@ -22,5 +22,7 @@
500
stone.ruins
-
+
+
+
diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_treasure.xml b/binaries/data/mods/public/simulation/templates/template_gaia_treasure.xml
index 6a734878ae..cb45f0a093 100644
--- a/binaries/data/mods/public/simulation/templates/template_gaia_treasure.xml
+++ b/binaries/data/mods/public/simulation/templates/template_gaia_treasure.xml
@@ -22,5 +22,7 @@
300
treasure.metal
-
+
+
+
diff --git a/source/graphics/UnitManager.cpp b/source/graphics/UnitManager.cpp
index 11a460cc44..7d866eb683 100644
--- a/source/graphics/UnitManager.cpp
+++ b/source/graphics/UnitManager.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2010 Wildfire Games.
+/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -85,42 +85,6 @@ void CUnitManager::DeleteAll()
m_Units.clear();
}
-
-///////////////////////////////////////////////////////////////////////////////
-// PickUnit: iterate through units testing given ray against bounds of each
-// unit; return the closest unit, or null if everything missed
-CUnit* CUnitManager::PickUnit(const CVector3D& origin, const CVector3D& dir) const
-{
- // closest object found so far
- CUnit* hit = 0;
- // closest approach offset (easier to pick small stuff in forests than standard ScEd style selection)
- float minrel = FLT_MAX;
-
- for (size_t i=0; iGetModel().GetSelectionBox();
- if (selectionBox.RayIntersect(origin, dir, tmin, tmax))
- {
- // Point of closest approach
- // TODO: this next bit is virtually identical to Selection::PickEntitiesAtPoint; might be useful to factor it out and
- // reuse it
- CVector3D delta = selectionBox.m_Center - origin;
- float distance = delta.Dot(dir);
- CVector3D closest = origin + dir * distance;
- CVector3D offset = selectionBox.m_Center - closest;
-
- float rel = offset.Length();
- if (rel < minrel) {
- hit = unit;
- minrel = rel;
- }
- }
- }
- return hit;
-}
-
///////////////////////////////////////////////////////////////////////////////
// CreateUnit: create a new unit and add it to the world
CUnit* CUnitManager::CreateUnit(const CStrW& actorName, uint32_t seed, const std::set& selections)
diff --git a/source/graphics/UnitManager.h b/source/graphics/UnitManager.h
index 7bbcc7c4b8..2b5252c1df 100644
--- a/source/graphics/UnitManager.h
+++ b/source/graphics/UnitManager.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2010 Wildfire Games.
+/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -55,10 +55,6 @@ public:
// return the units
const std::vector& GetUnits() const { return m_Units; }
- // iterate through units testing given ray against bounds of each unit;
- // return the closest unit, or null if everything missed
- CUnit* PickUnit(const CVector3D& origin, const CVector3D& dir) const;
-
void SetObjectManager(CObjectManager& objectManager) { m_ObjectManager = &objectManager; }
private:
diff --git a/source/gui/scripting/ScriptFunctions.cpp b/source/gui/scripting/ScriptFunctions.cpp
index 38ad32224a..bd4b30fe8e 100644
--- a/source/gui/scripting/ScriptFunctions.cpp
+++ b/source/gui/scripting/ScriptFunctions.cpp
@@ -128,17 +128,17 @@ void PostNetworkCommand(void* cbdata, CScriptVal cmd)
std::vector PickEntitiesAtPoint(void* UNUSED(cbdata), int x, int y)
{
- return EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, g_Game->GetPlayerID());
+ return EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, g_Game->GetPlayerID(), false);
}
std::vector PickFriendlyEntitiesInRect(void* UNUSED(cbdata), int x0, int y0, int x1, int y1, int player)
{
- return EntitySelection::PickEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x0, y0, x1, y1, player);
+ return EntitySelection::PickEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x0, y0, x1, y1, player, false);
}
std::vector 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(), includeOffScreen, matchRank);
+ return EntitySelection::PickSimilarEntities(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), templateName, g_Game->GetPlayerID(), includeOffScreen, matchRank, false);
}
CFixedVector3D GetTerrainAtPoint(void* UNUSED(cbdata), int x, int y)
diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp
index 48d2de8a33..5dcefd47cb 100644
--- a/source/ps/GameSetup/GameSetup.cpp
+++ b/source/ps/GameSetup/GameSetup.cpp
@@ -99,6 +99,10 @@
#include "ps/GameSetup/CmdLineArgs.h"
#include "ps/GameSetup/HWDetect.h"
+#include "tools/atlas/GameInterface/GameLoop.h"
+#include "tools/atlas/GameInterface/View.h"
+
+
#if !(OS_WIN || OS_MACOSX || OS_ANDROID) // assume all other platforms use X11 for wxWidgets
#define MUST_INIT_X11 1
#include
@@ -232,6 +236,13 @@ void Render()
ogl_WarnIfError();
+ // If we're in Atlas game view, render special overlays (e.g. editor bandbox)
+ if (g_GameLoop && g_GameLoop->view)
+ {
+ g_GameLoop->view->DrawOverlays();
+ ogl_WarnIfError();
+ }
+
// Text:
glDisable(GL_DEPTH_TEST);
diff --git a/source/simulation2/components/CCmpSelectable.cpp b/source/simulation2/components/CCmpSelectable.cpp
index fdf715897c..a936d91daf 100644
--- a/source/simulation2/components/CCmpSelectable.cpp
+++ b/source/simulation2/components/CCmpSelectable.cpp
@@ -48,6 +48,7 @@ public:
SOverlayLine m_Overlay;
SOverlayLine* m_DebugBoundingBoxOverlay;
SOverlayLine* m_DebugSelectionBoxOverlay;
+ bool m_EditorOnly;
CCmpSelectable()
: m_DebugBoundingBoxOverlay(NULL), m_DebugSelectionBoxOverlay(NULL)
@@ -66,11 +67,16 @@ public:
return
"Allows this entity to be selected by the player."
""
- "";
+ ""
+ ""
+ ""
+ ""
+ "";
}
- virtual void Init(const CParamNode& UNUSED(paramNode))
+ virtual void Init(const CParamNode& paramNode)
{
+ m_EditorOnly = paramNode.GetChild("EditorOnly").IsOk();
}
virtual void Deinit()
@@ -112,6 +118,11 @@ public:
}
}
+ virtual bool IsEditorOnly()
+ {
+ return m_EditorOnly;
+ }
+
virtual void SetSelectionHighlight(CColor color)
{
m_Overlay.m_Color = color;
diff --git a/source/simulation2/components/CCmpTemplateManager.cpp b/source/simulation2/components/CCmpTemplateManager.cpp
index cfa7bc7593..d5129342f4 100644
--- a/source/simulation2/components/CCmpTemplateManager.cpp
+++ b/source/simulation2/components/CCmpTemplateManager.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2011 Wildfire Games.
+/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -369,9 +369,9 @@ void CCmpTemplateManager::ConstructTemplateActor(const std::string& actorName, C
// Copy the actor template
out = m_TemplateFileData[templateName];
- // Initialise the actor's name
+ // Initialise the actor's name and make it an Atlas selectable entity.
std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(wstring_from_utf8(actorName)));
- std::string xml = "" + name + "";
+ std::string xml = "" + name + "";
CParamNode::LoadXMLString(out, xml.c_str());
}
diff --git a/source/simulation2/components/ICmpSelectable.h b/source/simulation2/components/ICmpSelectable.h
index b8b55d0c26..c7c8ec2436 100644
--- a/source/simulation2/components/ICmpSelectable.h
+++ b/source/simulation2/components/ICmpSelectable.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2010 Wildfire Games.
+/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -25,6 +25,11 @@ struct CColor;
class ICmpSelectable : public IComponent
{
public:
+ /**
+ * Returns true if the entity is only selectable in Atlas editor, e.g. a decorative visual actor.
+ */
+ virtual bool IsEditorOnly() = 0;
+
/**
* Set the color of the selection highlight (typically a circle/square
* around the unit). Set a = 0 to disable.
diff --git a/source/simulation2/helpers/Selection.cpp b/source/simulation2/helpers/Selection.cpp
index 84077508e3..28ea73640b 100644
--- a/source/simulation2/helpers/Selection.cpp
+++ b/source/simulation2/helpers/Selection.cpp
@@ -28,7 +28,7 @@
#include "simulation2/components/ICmpSelectable.h"
#include "simulation2/components/ICmpVisual.h"
-std::vector EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, int player)
+std::vector EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables)
{
CVector3D origin, dir;
camera.BuildCameraRay(screenX, screenY, origin, dir);
@@ -43,6 +43,10 @@ std::vector EntitySelection::PickEntitiesAtPoint(CSimulation2& simu
{
entity_id_t ent = it->first;
+ // Check if this entity is only selectable in Atlas
+ if (static_cast(it->second)->IsEditorOnly() && !allowEditorSelectables)
+ continue;
+
// Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld)
if (cmpRangeManager->GetLosVisibility(ent, player) == ICmpRangeManager::VIS_HIDDEN)
continue;
@@ -51,18 +55,39 @@ std::vector EntitySelection::PickEntitiesAtPoint(CSimulation2& simu
if (!cmpVisual)
continue;
+ CVector3D center;
+ float tmin, tmax;
+
CBoundingBoxOriented selectionBox = cmpVisual->GetSelectionBox();
if (selectionBox.IsEmpty())
- continue;
+ {
+ if (!allowEditorSelectables)
+ continue;
- float tmin, tmax;
- if (!selectionBox.RayIntersect(origin, dir, tmin, tmax))
- continue;
+ // Fall back to using old AABB selection method for decals
+ // see: http://trac.wildfiregames.com/ticket/1032
+ CBoundingBoxAligned aABBox = cmpVisual->GetBounds();
+ if (aABBox.IsEmpty())
+ continue;
+
+ if (!aABBox.RayIntersect(origin, dir, tmin, tmax))
+ continue;
+
+ aABBox.GetCentre(center);
+ }
+ else
+ {
+ if (!selectionBox.RayIntersect(origin, dir, tmin, tmax))
+ continue;
+
+ center = selectionBox.m_Center;
+ }
// Find the perpendicular distance from the object's centre to the picker ray
- CVector3D closest = origin + dir * (selectionBox.m_Center - origin).Dot(dir);
- float dist2 = (closest - selectionBox.m_Center).LengthSquared();
+ float dist2;
+ CVector3D closest = origin + dir * (center - origin).Dot(dir);
+ dist2 = (closest - center).LengthSquared();
hits.push_back(std::make_pair(dist2, ent));
}
@@ -78,7 +103,7 @@ std::vector EntitySelection::PickEntitiesAtPoint(CSimulation2& simu
return hitEnts;
}
-std::vector EntitySelection::PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, int owner)
+std::vector EntitySelection::PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, player_id_t owner, bool allowEditorSelectables)
{
// Make sure sx0 <= sx1, and sy0 <= sy1
if (sx0 > sx1)
@@ -96,13 +121,17 @@ std::vector EntitySelection::PickEntitiesInRect(CSimulation2& simul
{
entity_id_t ent = it->first;
+ // Check if this entity is only selectable in Atlas
+ if (static_cast(it->second)->IsEditorOnly() && !allowEditorSelectables)
+ continue;
+
// Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld)
if (cmpRangeManager->GetLosVisibility(ent, owner) == ICmpRangeManager::VIS_HIDDEN)
continue;
// Ignore entities not owned by 'owner'
CmpPtr cmpOwnership(simulation.GetSimContext(), ent);
- if (!cmpOwnership || cmpOwnership->GetOwner() != owner)
+ if (owner != INVALID_PLAYER && (!cmpOwnership || cmpOwnership->GetOwner() != owner))
continue;
// Find the current interpolated model position.
@@ -132,7 +161,7 @@ std::vector EntitySelection::PickEntitiesInRect(CSimulation2& simul
return hitEnts;
}
-std::vector EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, int owner, bool includeOffScreen, bool matchRank)
+std::vector EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, player_id_t owner, bool includeOffScreen, bool matchRank, bool allowEditorSelectables)
{
CmpPtr cmpTemplateManager(simulation, SYSTEM_ENTITY);
CmpPtr cmpRangeManager(simulation, SYSTEM_ENTITY);
@@ -144,6 +173,10 @@ std::vector EntitySelection::PickSimilarEntities(CSimulation2& simu
{
entity_id_t ent = it->first;
+ // Check if this entity is only selectable in Atlas
+ if (static_cast(it->second)->IsEditorOnly() && !allowEditorSelectables)
+ continue;
+
if (matchRank)
{
// Exact template name matching
@@ -156,14 +189,14 @@ std::vector EntitySelection::PickSimilarEntities(CSimulation2& simu
if (cmpRangeManager->GetLosVisibility(ent, owner) == ICmpRangeManager::VIS_HIDDEN)
continue;
- // Ignore entities not owned by 'owner'
- CmpPtr cmpOwnership(simulation.GetSimContext(), ent);
- if (!cmpOwnership || cmpOwnership->GetOwner() != owner)
- continue;
+ // Ignore entities not owned by 'owner'
+ CmpPtr cmpOwnership(simulation.GetSimContext(), ent);
+ if (owner != INVALID_PLAYER && (!cmpOwnership || cmpOwnership->GetOwner() != owner))
+ continue;
// Ignore off screen entities
- if (!includeOffScreen)
- {
+ if (!includeOffScreen)
+ {
// Find the current interpolated model position.
CmpPtr cmpVisual(simulation.GetSimContext(), ent);
if (!cmpVisual)
@@ -173,7 +206,7 @@ std::vector EntitySelection::PickSimilarEntities(CSimulation2& simu
// Reject if it's not on-screen (e.g. it's behind the camera)
if (!camera.GetFrustum().IsPointVisible(position))
continue;
- }
+ }
if (!matchRank)
{
diff --git a/source/simulation2/helpers/Selection.h b/source/simulation2/helpers/Selection.h
index 16688bf14c..54a700f557 100644
--- a/source/simulation2/helpers/Selection.h
+++ b/source/simulation2/helpers/Selection.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2010 Wildfire Games.
+/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -24,6 +24,7 @@
*/
#include "simulation2/system/Entity.h"
+#include "Player.h"
#include
@@ -37,24 +38,33 @@ namespace EntitySelection
* Finds all selectable entities under the given screen coordinates.
* Returns list ordered by closeness of picking, closest first.
* Restricted to entities in the LOS of @p player, but with any owner.
+ * (For Atlas selections this value is ignored as the whole map is revealed).
+ * If @p allowEditorSelectables then all entities with the IID_Selectable interface
+ * will be selected, else only selectable entities without the EditorOnly flag set.
*/
-std::vector PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, int player);
+std::vector PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables);
/**
* Finds all selectable entities within the given screen coordinate rectangle,
* that belong to player @p owner.
+ * If @p owner is INVALID_PLAYER then ownership is ignored.
+ * If @p allowEditorSelectables then all entities with the IID_Selectable interface
+ * will be selected, else only selectable entities without the EditorOnly flag set.
* Returns unordered list.
*/
-std::vector PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, int owner);
+std::vector PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, player_id_t owner, bool allowEditorSelectables);
/**
* Finds all entities with the given entity template name, that belong to player @p owner.
+ * If @p owner is INVALID_PLAYER then ownership is ignored.
* 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.
+ * If @p allowEditorSelectables then all entities with the IID_Selectable interface
+ * will be selected, else only selectable entities without the EditorOnly flag set.
*/
-std::vector PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, int owner, bool includeOffScreen, bool matchRank);
+std::vector PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, player_id_t owner, bool includeOffScreen, bool matchRank, bool allowEditorSelectables);
} // namespace
diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp
index e33236345d..7b1c645632 100644
--- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp
+++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009 Wildfire Games.
+/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -31,9 +31,16 @@ class TransformObject : public StateDrivenTool
DECLARE_DYNAMIC_CLASS(TransformObject);
int m_dx, m_dy;
+ AtlasMessage::ObjectID m_lastSelected;
+ wxPoint m_startPoint;
+
+ // TODO: If we don't plan to change hotkeys, just replace with evt.ShiftDown(), etc.
+ static const wxKeyCode SELECTION_ADD_HOTKEY = WXK_SHIFT;
+ static const wxKeyCode SELECTION_REMOVE_HOTKEY = WXK_CONTROL; // COMMAND on Macs
+ static const wxKeyCode SELECTION_ACTORS_HOTKEY = WXK_ALT;
public:
- TransformObject()
+ TransformObject() : m_lastSelected(0)
{
SetState(&Waiting);
}
@@ -52,37 +59,77 @@ public:
{
bool OnMouse(TransformObject* obj, wxMouseEvent& evt)
{
- if (evt.LeftDown())
+ if (evt.LeftDClick() && AtlasMessage::ObjectIDIsValid(obj->m_lastSelected))
{
+ SET_STATE(SelectSimilar);
+ return true;
+ }
+ else if (evt.LeftDown())
+ {
+ bool selectionAdd = wxGetKeyState(SELECTION_ADD_HOTKEY);
+ bool selectionRemove = wxGetKeyState(SELECTION_REMOVE_HOTKEY);
+ bool selectionActors = wxGetKeyState(SELECTION_ACTORS_HOTKEY);
+
// New selection - never merge with movements of other objects
ScenarioEditor::GetCommandProc().FinaliseLastCommand();
// Select the object clicked on:
- AtlasMessage::qPickObject qry(Position(evt.GetPosition()));
+ AtlasMessage::qPickObject qry(Position(evt.GetPosition()), selectionActors);
qry.Post();
- // TODO: handle multiple selections
- g_SelectedObjects.clear();
-
// Check they actually clicked on a valid object
if (AtlasMessage::ObjectIDIsValid(qry.id))
{
- g_SelectedObjects.push_back(qry.id);
- // Remember the screen-space offset of the mouse from the
- // object's centre, so we can add that back when moving it
- // (instead of just moving the object's centre to directly
- // beneath the mouse)
- obj->m_dx = qry.offsetx;
- obj->m_dy = qry.offsety;
- SET_STATE(Dragging);
+ std::vector::iterator it = std::find(g_SelectedObjects.begin(), g_SelectedObjects.end(), qry.id);
+ bool objectIsSelected = (it != g_SelectedObjects.end());
+
+ if (selectionRemove)
+ {
+ // Remove from selection
+ if (objectIsSelected)
+ g_SelectedObjects.erase(it);
+ }
+ else if (!objectIsSelected)
+ {
+ // Add to selection
+ if (!selectionAdd)
+ g_SelectedObjects.clear();
+
+ g_SelectedObjects.push_back(qry.id);
+ }
+
+ obj->m_lastSelected = qry.id;
+
+ // If we're selecting the whole group
+ if (!selectionAdd && !selectionRemove && !g_SelectedObjects.empty())
+ {
+ // Remember the screen-space offset of the mouse from the
+ // object's centre, so we can add that back when moving it
+ // (instead of just moving the object's centre to directly
+ // beneath the mouse)
+ obj->m_dx = qry.offsetx;
+ obj->m_dy = qry.offsety;
+ SET_STATE(Dragging);
+ }
+
+ g_SelectedObjects.NotifyObservers();
+ POST_MESSAGE(SetSelectionPreview, (g_SelectedObjects));
}
- g_SelectedObjects.NotifyObservers();
- POST_MESSAGE(SetSelectionPreview, (g_SelectedObjects));
+ else
+ {
+ // Bandboxing
+ obj->m_lastSelected = 0;
+ obj->m_startPoint = evt.GetPosition();
+ SET_STATE(Bandboxing);
+ }
+
return true;
}
- else if ((evt.Dragging() && evt.RightIsDown()) || evt.RightDown())
+ else if (g_SelectedObjects.size() == 1 && ((evt.Dragging() && evt.RightIsDown()) || evt.RightDown()))
{
+ // TODO: Rotation of selections with multiple objects?
+
// Dragging with right mouse button -> rotate objects to look
// at mouse
Position pos (evt.GetPosition());
@@ -91,6 +138,12 @@ public:
return true;
}
+ else if (evt.Moving())
+ {
+ // Prevent certain events from reaching game UI in this mode
+ // to prevent selection ring confusion
+ return true;
+ }
else
return false;
}
@@ -132,8 +185,23 @@ public:
else if (evt.Dragging())
{
Position pos (evt.GetPosition() + wxPoint(obj->m_dx, obj->m_dy));
- for (size_t i = 0; i < g_SelectedObjects.size(); ++i)
- POST_COMMAND(MoveObject, (g_SelectedObjects[i], pos));
+
+ POST_COMMAND(MoveObjects, (g_SelectedObjects, obj->m_lastSelected, pos));
+ return true;
+ }
+ else
+ return false;
+ }
+
+ bool OnKey(TransformObject* obj, wxKeyEvent& evt, KeyEventType type)
+ {
+ if (type == KEY_UP && evt.GetKeyCode() == WXK_ESCAPE)
+ {
+ // Cancel move action
+ ScenarioEditor::GetCommandProc().FinaliseLastCommand();
+ ScenarioEditor::GetCommandProc().Undo();
+ SET_STATE(Waiting);
+
return true;
}
else
@@ -141,6 +209,132 @@ public:
}
}
Dragging;
+
+ struct sBandboxing : public State
+ {
+ bool OnMouse(TransformObject* obj, wxMouseEvent& evt)
+ {
+ if (evt.LeftIsDown() && evt.Dragging())
+ {
+ // Update bandbox overlay
+ POST_MESSAGE(SetBandbox, (true, obj->m_startPoint.x, obj->m_startPoint.y, evt.GetPosition().x, evt.GetPosition().y));
+ return true;
+ }
+ else if (evt.LeftUp())
+ {
+ bool selectionAdd = wxGetKeyState(SELECTION_ADD_HOTKEY);
+ bool selectionRemove = wxGetKeyState(SELECTION_REMOVE_HOTKEY);
+ bool selectionActors = wxGetKeyState(SELECTION_ACTORS_HOTKEY);
+
+ // Now we have both corners of the box, pick objects
+ AtlasMessage::qPickObjectsInRect qry(Position(obj->m_startPoint), Position(evt.GetPosition()), selectionActors);
+ qry.Post();
+
+ std::vector ids = *qry.ids;
+
+ if (!selectionAdd && !selectionRemove)
+ {
+ // Just copy new selections (clears list if no selections)
+ g_SelectedObjects = ids;
+ }
+ else
+ {
+ for (size_t i = 0; i < ids.size(); ++i)
+ {
+ std::vector::iterator it = std::find(g_SelectedObjects.begin(), g_SelectedObjects.end(), ids[i]);
+ bool objectIsSelected = (it != g_SelectedObjects.end());
+ if (selectionRemove)
+ {
+ // Remove from selection
+ if (objectIsSelected)
+ g_SelectedObjects.erase(it);
+ }
+ else if (!objectIsSelected)
+ {
+ // Add to selection
+ g_SelectedObjects.push_back(ids[i]);
+ }
+ }
+ }
+
+ POST_MESSAGE(SetBandbox, (false, 0, 0, 0, 0));
+
+ g_SelectedObjects.NotifyObservers();
+ POST_MESSAGE(SetSelectionPreview, (g_SelectedObjects));
+
+ SET_STATE(Waiting);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ bool OnKey(TransformObject* obj, wxKeyEvent& evt, KeyEventType type)
+ {
+ if (type == KEY_UP && evt.GetKeyCode() == WXK_ESCAPE)
+ {
+ // Clear bandbox and return to waiting state
+ POST_MESSAGE(SetBandbox, (false, 0, 0, 0, 0));
+ SET_STATE(Waiting);
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+ Bandboxing;
+
+ struct sSelectSimilar : public State
+ {
+ bool OnMouse(TransformObject* obj, wxMouseEvent& evt)
+ {
+ if (evt.LeftUp())
+ {
+ bool selectionAdd = wxGetKeyState(SELECTION_ADD_HOTKEY);
+ bool selectionRemove = wxGetKeyState(SELECTION_REMOVE_HOTKEY);
+
+ // Select similar objects
+ AtlasMessage::qPickSimilarObjects qry(obj->m_lastSelected);
+ qry.Post();
+
+ std::vector ids = *qry.ids;
+
+ if (!selectionAdd && !selectionRemove)
+ {
+ // Just copy new selections (clears list if no selections)
+ g_SelectedObjects = ids;
+ }
+ else
+ {
+ for (size_t i = 0; i < ids.size(); ++i)
+ {
+ std::vector::iterator it = std::find(g_SelectedObjects.begin(), g_SelectedObjects.end(), ids[i]);
+ bool objectIsSelected = (it != g_SelectedObjects.end());
+ if (selectionRemove)
+ {
+ // Remove from selection
+ if (objectIsSelected)
+ g_SelectedObjects.erase(it);
+ }
+ else if (!objectIsSelected)
+ {
+ // Add to selection
+ g_SelectedObjects.push_back(ids[i]);
+ }
+ }
+ }
+
+ g_SelectedObjects.NotifyObservers();
+ POST_MESSAGE(SetSelectionPreview, (g_SelectedObjects));
+
+ SET_STATE(Waiting);
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+ SelectSimilar;
};
IMPLEMENT_DYNAMIC_CLASS(TransformObject, StateDrivenTool);
diff --git a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp
index 1a0bb6b788..1eb46be19c 100644
--- a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp
+++ b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp
@@ -18,6 +18,7 @@
#include "precompiled.h"
#include
+#include