Adds multiple entity selection to Atlas (including move/delete). Fixes #678.
Makes all actors selectable in Atlas and gives them selection rings (an EditorOnly flag is used in the template for Atlas-only selectables). Adds player colors to Atlas selection rings. Fixes decal selection by falling back to old-style AABBs. Refs #1032. Replaces UnitManager selections with EntitySelection helpers. Adds DrawOverlays to Atlas views, for Atlas-specific rendering. Fixes bug where selection rings conflicted with Move/rotate tool in Atlas simulation test. This was SVN commit r11177.
This commit is contained in:
parent
8651452d33
commit
7d9e98b00e
@ -20,7 +20,9 @@
|
||||
<Obstruction>
|
||||
<Static width="59" depth="15"/>
|
||||
</Obstruction>
|
||||
<Selectable disable=""/>
|
||||
<Selectable>
|
||||
<EditorOnly/>
|
||||
</Selectable>
|
||||
<Vision>
|
||||
<Range>72</Range>
|
||||
<RetainInFog>true</RetainInFog>
|
||||
|
@ -20,7 +20,9 @@
|
||||
<Obstruction>
|
||||
<Static width="59" depth="15"/>
|
||||
</Obstruction>
|
||||
<Selectable disable=""/>
|
||||
<Selectable>
|
||||
<EditorOnly/>
|
||||
</Selectable>
|
||||
<Vision>
|
||||
<Range>72</Range>
|
||||
<RetainInFog>true</RetainInFog>
|
||||
|
@ -21,7 +21,9 @@
|
||||
<Floating>false</Floating>
|
||||
<TurnRate>6.0</TurnRate>
|
||||
</Position>
|
||||
<Selectable/>
|
||||
<Selectable>
|
||||
<EditorOnly disable=""/>
|
||||
</Selectable>
|
||||
<TerritoryInfluence>
|
||||
<OverrideCost>64</OverrideCost>
|
||||
<Root>false</Root>
|
||||
|
@ -21,7 +21,9 @@
|
||||
<Floating>false</Floating>
|
||||
<TurnRate>6.0</TurnRate>
|
||||
</Position>
|
||||
<Selectable/>
|
||||
<Selectable>
|
||||
<EditorOnly disable=""/>
|
||||
</Selectable>
|
||||
<TerritoryInfluence>
|
||||
<OverrideCost>0</OverrideCost>
|
||||
<Root>false</Root>
|
||||
|
@ -8,4 +8,7 @@
|
||||
<Floating>false</Floating>
|
||||
<TurnRate>6.0</TurnRate>
|
||||
</Position>
|
||||
<Selectable>
|
||||
<EditorOnly/>
|
||||
</Selectable>
|
||||
</Entity>
|
||||
|
@ -16,7 +16,9 @@
|
||||
<Amount>200</Amount>
|
||||
<Type>food.fruit</Type>
|
||||
</ResourceSupply>
|
||||
<Selectable/>
|
||||
<Selectable>
|
||||
<EditorOnly disable=""/>
|
||||
</Selectable>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/resource/sel_fruit.xml</select>
|
||||
|
@ -15,7 +15,9 @@
|
||||
<Amount>200</Amount>
|
||||
<Type>wood.tree</Type>
|
||||
</ResourceSupply>
|
||||
<Selectable/>
|
||||
<Selectable>
|
||||
<EditorOnly disable=""/>
|
||||
</Selectable>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/resource/sel_tree.xml</select>
|
||||
|
@ -15,7 +15,9 @@
|
||||
<Amount>1000</Amount>
|
||||
<Type>metal.ore</Type>
|
||||
</ResourceSupply>
|
||||
<Selectable/>
|
||||
<Selectable>
|
||||
<EditorOnly disable=""/>
|
||||
</Selectable>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/resource/sel_metal.xml</select>
|
||||
|
@ -15,7 +15,9 @@
|
||||
<Amount>1000</Amount>
|
||||
<Type>stone.rock</Type>
|
||||
</ResourceSupply>
|
||||
<Selectable/>
|
||||
<Selectable>
|
||||
<EditorOnly disable=""/>
|
||||
</Selectable>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/resource/sel_stone.xml</select>
|
||||
|
@ -22,5 +22,7 @@
|
||||
<Amount>500</Amount>
|
||||
<Type>stone.ruins</Type>
|
||||
</ResourceSupply>
|
||||
<Selectable/>
|
||||
<Selectable>
|
||||
<EditorOnly disable=""/>
|
||||
</Selectable>
|
||||
</Entity>
|
||||
|
@ -22,5 +22,7 @@
|
||||
<Amount>300</Amount>
|
||||
<Type>treasure.metal</Type>
|
||||
</ResourceSupply>
|
||||
<Selectable/>
|
||||
<Selectable>
|
||||
<EditorOnly disable=""/>
|
||||
</Selectable>
|
||||
</Entity>
|
||||
|
@ -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; i<m_Units.size(); i++) {
|
||||
CUnit* unit = m_Units[i];
|
||||
float tmin, tmax;
|
||||
|
||||
const CBoundingBoxOriented& selectionBox = unit->GetModel().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<CStr8>& selections)
|
||||
|
@ -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<CUnit*>& 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:
|
||||
|
@ -128,17 +128,17 @@ void PostNetworkCommand(void* cbdata, CScriptVal cmd)
|
||||
|
||||
std::vector<entity_id_t> 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<entity_id_t> 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<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(), 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)
|
||||
|
@ -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 <X11/Xlib.h>
|
||||
@ -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);
|
||||
|
@ -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
|
||||
"<a:help>Allows this entity to be selected by the player.</a:help>"
|
||||
"<a:example/>"
|
||||
"<empty/>";
|
||||
"<optional>"
|
||||
"<element name='EditorOnly' a:help='If this element is present, the entity is only selectable in Atlas'>"
|
||||
"<empty/>"
|
||||
"</element>"
|
||||
"</optional>";
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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 = "<Entity><VisualActor><Actor>" + name + "</Actor></VisualActor></Entity>";
|
||||
std::string xml = "<Entity><VisualActor><Actor>" + name + "</Actor></VisualActor><Selectable><EditorOnly/></Selectable></Entity>";
|
||||
CParamNode::LoadXMLString(out, xml.c_str());
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "simulation2/components/ICmpSelectable.h"
|
||||
#include "simulation2/components/ICmpVisual.h"
|
||||
|
||||
std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, int player)
|
||||
std::vector<entity_id_t> 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<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simu
|
||||
{
|
||||
entity_id_t ent = it->first;
|
||||
|
||||
// Check if this entity is only selectable in Atlas
|
||||
if (static_cast<ICmpSelectable*>(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<entity_id_t> 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<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simu
|
||||
return hitEnts;
|
||||
}
|
||||
|
||||
std::vector<entity_id_t> EntitySelection::PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, int owner)
|
||||
std::vector<entity_id_t> 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<entity_id_t> EntitySelection::PickEntitiesInRect(CSimulation2& simul
|
||||
{
|
||||
entity_id_t ent = it->first;
|
||||
|
||||
// Check if this entity is only selectable in Atlas
|
||||
if (static_cast<ICmpSelectable*>(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<ICmpOwnership> 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<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 includeOffScreen, bool matchRank)
|
||||
std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, player_id_t owner, bool includeOffScreen, bool matchRank, bool allowEditorSelectables)
|
||||
{
|
||||
CmpPtr<ICmpTemplateManager> cmpTemplateManager(simulation, SYSTEM_ENTITY);
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);
|
||||
@ -144,6 +173,10 @@ std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simu
|
||||
{
|
||||
entity_id_t ent = it->first;
|
||||
|
||||
// Check if this entity is only selectable in Atlas
|
||||
if (static_cast<ICmpSelectable*>(it->second)->IsEditorOnly() && !allowEditorSelectables)
|
||||
continue;
|
||||
|
||||
if (matchRank)
|
||||
{
|
||||
// Exact template name matching
|
||||
@ -156,14 +189,14 @@ std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simu
|
||||
if (cmpRangeManager->GetLosVisibility(ent, owner) == ICmpRangeManager::VIS_HIDDEN)
|
||||
continue;
|
||||
|
||||
// Ignore entities not owned by 'owner'
|
||||
CmpPtr<ICmpOwnership> cmpOwnership(simulation.GetSimContext(), ent);
|
||||
if (!cmpOwnership || cmpOwnership->GetOwner() != owner)
|
||||
continue;
|
||||
// Ignore entities not owned by 'owner'
|
||||
CmpPtr<ICmpOwnership> 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<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent);
|
||||
if (!cmpVisual)
|
||||
@ -173,7 +206,7 @@ std::vector<entity_id_t> 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)
|
||||
{
|
||||
|
@ -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 <vector>
|
||||
|
||||
@ -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<entity_id_t> PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, int player);
|
||||
std::vector<entity_id_t> 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<entity_id_t> PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, int owner);
|
||||
std::vector<entity_id_t> 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<entity_id_t> PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, int owner, bool includeOffScreen, bool matchRank);
|
||||
std::vector<entity_id_t> PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, player_id_t owner, bool includeOffScreen, bool matchRank, bool allowEditorSelectables);
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -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<TransformObject>
|
||||
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<AtlasMessage::ObjectID>::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<AtlasMessage::ObjectID> 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<AtlasMessage::ObjectID>::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<AtlasMessage::ObjectID> 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<AtlasMessage::ObjectID>::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<TransformObject>);
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include <cfloat>
|
||||
#include <map>
|
||||
|
||||
#include "MessageHandler.h"
|
||||
#include "../CommandProc.h"
|
||||
@ -31,7 +32,6 @@
|
||||
#include "graphics/ObjectManager.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "graphics/Unit.h"
|
||||
#include "graphics/UnitManager.h"
|
||||
#include "lib/ogl.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "maths/Matrix3D.h"
|
||||
@ -43,8 +43,11 @@
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpOwnership.h"
|
||||
#include "simulation2/components/ICmpPosition.h"
|
||||
#include "simulation2/components/ICmpPlayer.h"
|
||||
#include "simulation2/components/ICmpPlayerManager.h"
|
||||
#include "simulation2/components/ICmpSelectable.h"
|
||||
#include "simulation2/components/ICmpTemplateManager.h"
|
||||
#include "simulation2/helpers/Selection.h"
|
||||
|
||||
|
||||
namespace AtlasMessage {
|
||||
@ -55,22 +58,6 @@ namespace
|
||||
{
|
||||
return wcscmp(a.name.c_str(), b.name.c_str()) < 0;
|
||||
}
|
||||
|
||||
bool IsFloating(const CUnit* unit)
|
||||
{
|
||||
if (! unit)
|
||||
return false;
|
||||
|
||||
CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), unit->GetID());
|
||||
if (!cmpPosition)
|
||||
return false;
|
||||
return cmpPosition->IsFloating();
|
||||
}
|
||||
|
||||
CUnitManager& GetUnitManager()
|
||||
{
|
||||
return g_Game->GetWorld()->GetUnitManager();
|
||||
}
|
||||
}
|
||||
|
||||
QUERYHANDLER(GetObjectsList)
|
||||
@ -111,20 +98,57 @@ static std::vector<ObjectID> g_Selection;
|
||||
|
||||
MESSAGEHANDLER(SetSelectionPreview)
|
||||
{
|
||||
CSimulation2& sim = *g_Game->GetSimulation2();
|
||||
|
||||
// Cache player colours for performance
|
||||
typedef std::map<player_id_t, CColor> PlayerColourMap;
|
||||
PlayerColourMap playerColours;
|
||||
|
||||
CmpPtr<ICmpPlayerManager> cmpPlayerManager(sim, SYSTEM_ENTITY);
|
||||
|
||||
// Clear old selection rings
|
||||
for (size_t i = 0; i < g_Selection.size(); ++i)
|
||||
{
|
||||
CmpPtr<ICmpSelectable> cmpSelectable(*g_Game->GetSimulation2(), g_Selection[i]);
|
||||
CmpPtr<ICmpSelectable> cmpSelectable(sim, g_Selection[i]);
|
||||
if (cmpSelectable)
|
||||
cmpSelectable->SetSelectionHighlight(CColor(1, 1, 1, 0));
|
||||
}
|
||||
|
||||
g_Selection = *msg->ids;
|
||||
|
||||
// Set new selection rings
|
||||
for (size_t i = 0; i < g_Selection.size(); ++i)
|
||||
{
|
||||
CmpPtr<ICmpSelectable> cmpSelectable(*g_Game->GetSimulation2(), g_Selection[i]);
|
||||
if (cmpSelectable)
|
||||
cmpSelectable->SetSelectionHighlight(CColor(1, 1, 1, 1));
|
||||
entity_id_t ent = g_Selection[i];
|
||||
CmpPtr<ICmpSelectable> cmpSelectable(sim, ent);
|
||||
if (!cmpSelectable)
|
||||
continue;
|
||||
|
||||
// Default to white for ownerless entities
|
||||
CColor colour(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
CmpPtr<ICmpOwnership> cmpOwnership(sim, ent);
|
||||
if (cmpOwnership && cmpPlayerManager)
|
||||
{
|
||||
player_id_t owner = cmpOwnership->GetOwner();
|
||||
if (playerColours.find(owner) != playerColours.end())
|
||||
{
|
||||
colour = playerColours[owner];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add colour to cache
|
||||
entity_id_t playerEnt = cmpPlayerManager->GetPlayerByID(owner);
|
||||
CmpPtr<ICmpPlayer> cmpPlayer(sim, playerEnt);
|
||||
if (cmpPlayer)
|
||||
{
|
||||
colour = cmpPlayer->GetColour();
|
||||
playerColours[owner] = colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmpSelectable->SetSelectionHighlight(colour);
|
||||
}
|
||||
}
|
||||
|
||||
@ -401,71 +425,129 @@ QUERYHANDLER(PickObject)
|
||||
float x, y;
|
||||
msg->pos->GetScreenSpace(x, y);
|
||||
|
||||
CVector3D rayorigin, raydir;
|
||||
g_Game->GetView()->GetCamera()->BuildCameraRay((int)x, (int)y, rayorigin, raydir);
|
||||
// Normally this function would be called with a player ID to check LOS,
|
||||
// but in Atlas the entire map is revealed, so just pass INVALID_PLAYER
|
||||
std::vector<AtlasMessage::ObjectID> ents = EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, INVALID_PLAYER, msg->selectActors);
|
||||
|
||||
CUnit* target = GetUnitManager().PickUnit(rayorigin, raydir);
|
||||
|
||||
if (target)
|
||||
msg->id = target->GetID();
|
||||
else
|
||||
msg->id = INVALID_ENTITY;
|
||||
|
||||
if (target)
|
||||
// Multiple entities may have been picked, but they are sorted by distance,
|
||||
// so only take the first one
|
||||
if (!ents.empty())
|
||||
{
|
||||
// Get screen coordinates of the point on the ground underneath the
|
||||
// object's model-centre, so that callers know the offset to use when
|
||||
// working out the screen coordinates to move the object to.
|
||||
|
||||
CVector3D centre = target->GetModel().GetTransform().GetTranslation();
|
||||
msg->id = ents[0];
|
||||
|
||||
centre.Y = g_Game->GetWorld()->GetTerrain()->GetExactGroundLevel(centre.X, centre.Z);
|
||||
if (IsFloating(target))
|
||||
centre.Y = std::max(centre.Y, g_Renderer.GetWaterManager()->m_WaterHeight);
|
||||
// Calculate offset of object from original mouse click position
|
||||
// so it gets moved by that offset
|
||||
CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), (entity_id_t)ents[0]);
|
||||
if (!cmpPosition || !cmpPosition->IsInWorld())
|
||||
{
|
||||
// error
|
||||
msg->offsetx = msg->offsety = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CFixedVector3D fixed = cmpPosition->GetPosition();
|
||||
CVector3D centre = CVector3D(fixed.X.ToFloat(), fixed.Y.ToFloat(), fixed.Z.ToFloat());
|
||||
|
||||
float cx, cy;
|
||||
g_Game->GetView()->GetCamera()->GetScreenCoordinates(centre, cx, cy);
|
||||
float cx, cy;
|
||||
g_Game->GetView()->GetCamera()->GetScreenCoordinates(centre, cx, cy);
|
||||
|
||||
msg->offsetx = (int)(cx - x);
|
||||
msg->offsety = (int)(cy - y);
|
||||
msg->offsetx = (int)(cx - x);
|
||||
msg->offsety = (int)(cy - y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg->offsetx = msg->offsety = 0;
|
||||
// No entity picked
|
||||
msg->id = INVALID_ENTITY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BEGIN_COMMAND(MoveObject)
|
||||
QUERYHANDLER(PickObjectsInRect)
|
||||
{
|
||||
CVector3D m_PosOld, m_PosNew;
|
||||
float x0, y0, x1, y1;
|
||||
msg->start->GetScreenSpace(x0, y0);
|
||||
msg->end->GetScreenSpace(x1, y1);
|
||||
|
||||
// Since owner selections are meaningless in Atlas, use INVALID_PLAYER
|
||||
msg->ids = EntitySelection::PickEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x0, y0, x1, y1, INVALID_PLAYER, msg->selectActors);
|
||||
}
|
||||
|
||||
|
||||
QUERYHANDLER(PickSimilarObjects)
|
||||
{
|
||||
CmpPtr<ICmpTemplateManager> cmpTemplateManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
||||
ENSURE(cmpTemplateManager);
|
||||
std::string templateName = cmpTemplateManager->GetCurrentTemplateName((entity_id_t)msg->id);
|
||||
|
||||
// Since owner selections are meaningless in Atlas, use INVALID_PLAYER
|
||||
msg->ids = EntitySelection::PickSimilarEntities(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), templateName, INVALID_PLAYER, false, true, true);
|
||||
}
|
||||
|
||||
|
||||
BEGIN_COMMAND(MoveObjects)
|
||||
{
|
||||
// Mapping from object to position
|
||||
typedef std::map<AtlasMessage::ObjectID, CVector3D> ObjectPositionMap;
|
||||
ObjectPositionMap m_PosOld, m_PosNew;
|
||||
|
||||
void Do()
|
||||
{
|
||||
CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), (entity_id_t)msg->id);
|
||||
if (!cmpPosition)
|
||||
{
|
||||
// error
|
||||
m_PosOld = m_PosNew = CVector3D(0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PosNew = GetUnitPos(msg->pos, cmpPosition->IsFloating());
|
||||
std::vector<ObjectID> ids = *msg->ids;
|
||||
|
||||
CFixedVector3D pos = cmpPosition->GetPosition();
|
||||
m_PosOld = CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat());
|
||||
// All selected objects move relative to a pivot object,
|
||||
// so get its position and whether it's floating
|
||||
CVector3D pivotPos(0, 0, 0);
|
||||
bool pivotFloating = false;
|
||||
|
||||
CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), (entity_id_t)msg->pivot);
|
||||
if (cmpPosition && cmpPosition->IsInWorld())
|
||||
{
|
||||
pivotFloating = cmpPosition->IsFloating();
|
||||
CFixedVector3D pivotFixed = cmpPosition->GetPosition();
|
||||
pivotPos = CVector3D(pivotFixed.X.ToFloat(), pivotFixed.Y.ToFloat(), pivotFixed.Z.ToFloat());
|
||||
}
|
||||
|
||||
// Calculate directional vector of movement for pivot object,
|
||||
// we apply the same movement to all objects
|
||||
CVector3D targetPos = GetUnitPos(msg->pos, pivotFloating);
|
||||
CVector3D dir = targetPos - pivotPos;
|
||||
|
||||
for (size_t i = 0; i < ids.size(); ++i)
|
||||
{
|
||||
entity_id_t id = (entity_id_t)ids[i];
|
||||
CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), id);
|
||||
if (!cmpPosition || !cmpPosition->IsInWorld())
|
||||
{
|
||||
// error
|
||||
m_PosOld[id] = m_PosNew[id] = CVector3D(0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculate this object's position
|
||||
CFixedVector3D posFixed = cmpPosition->GetPosition();
|
||||
CVector3D pos = CVector3D(posFixed.X.ToFloat(), posFixed.Y.ToFloat(), posFixed.Z.ToFloat());
|
||||
m_PosNew[id] = pos + dir;
|
||||
m_PosOld[id] = pos;
|
||||
}
|
||||
}
|
||||
|
||||
SetPos(m_PosNew);
|
||||
}
|
||||
|
||||
void SetPos(CVector3D& pos)
|
||||
void SetPos(ObjectPositionMap& map)
|
||||
{
|
||||
CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), (entity_id_t)msg->id);
|
||||
if (!cmpPosition)
|
||||
return;
|
||||
ObjectPositionMap::iterator it;
|
||||
for (it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), (entity_id_t)it->first);
|
||||
if (!cmpPosition)
|
||||
return;
|
||||
|
||||
cmpPosition->JumpTo(entity_pos_t::FromFloat(pos.X), entity_pos_t::FromFloat(pos.Z));
|
||||
// Set 2D position, ignoring height
|
||||
CVector3D pos = it->second;
|
||||
cmpPosition->JumpTo(entity_pos_t::FromFloat(pos.X), entity_pos_t::FromFloat(pos.Z));
|
||||
}
|
||||
}
|
||||
|
||||
void Redo()
|
||||
@ -478,14 +560,14 @@ BEGIN_COMMAND(MoveObject)
|
||||
SetPos(m_PosOld);
|
||||
}
|
||||
|
||||
void MergeIntoPrevious(cMoveObject* prev)
|
||||
void MergeIntoPrevious(cMoveObjects* prev)
|
||||
{
|
||||
// TODO: do something valid if prev unit != this unit
|
||||
ENSURE(prev->msg->id == msg->id);
|
||||
// TODO: do something valid if prev selection != this selection
|
||||
ENSURE(*(prev->msg->ids) == *(msg->ids));
|
||||
prev->m_PosNew = m_PosNew;
|
||||
}
|
||||
};
|
||||
END_COMMAND(MoveObject)
|
||||
END_COMMAND(MoveObjects)
|
||||
|
||||
|
||||
BEGIN_COMMAND(RotateObject)
|
||||
@ -645,4 +727,9 @@ QUERYHANDLER(GetPlayerObjects)
|
||||
msg->ids = ids;
|
||||
}
|
||||
|
||||
MESSAGEHANDLER(SetBandbox)
|
||||
{
|
||||
View::GetView_Game()->SetBandbox(msg->show, (float)msg->sx0, (float)msg->sy0, (float)msg->sx1, (float)msg->sy1);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
@ -484,15 +484,31 @@ COMMAND(FillTerrain, NOMERGE,
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
QUERY(PickObject,
|
||||
((Position, pos))
|
||||
,
|
||||
((ObjectID, id))
|
||||
((int, offsetx)) // offset of object centre from input position
|
||||
((int, offsety)) //
|
||||
);
|
||||
|
||||
COMMAND(MoveObject, MERGE,
|
||||
((Position, pos))
|
||||
((bool, selectActors))
|
||||
,
|
||||
((ObjectID, id))
|
||||
((int, offsetx)) // offset of object centre from input position
|
||||
((int, offsety)) //
|
||||
);
|
||||
|
||||
QUERY(PickObjectsInRect,
|
||||
((Position, start))
|
||||
((Position, end))
|
||||
((bool, selectActors))
|
||||
,
|
||||
((std::vector<ObjectID>, ids))
|
||||
);
|
||||
|
||||
QUERY(PickSimilarObjects,
|
||||
((ObjectID, id))
|
||||
,
|
||||
((std::vector<ObjectID>, ids))
|
||||
);
|
||||
|
||||
COMMAND(MoveObjects, MERGE,
|
||||
((std::vector<ObjectID>, ids))
|
||||
((ObjectID, pivot))
|
||||
((Position, pos))
|
||||
);
|
||||
|
||||
@ -512,11 +528,11 @@ MESSAGE(SetSelectionPreview,
|
||||
);
|
||||
|
||||
QUERY(GetObjectSettings,
|
||||
((int, view)) // eRenderView
|
||||
((ObjectID, id))
|
||||
,
|
||||
((sObjectSettings, settings))
|
||||
);
|
||||
((int, view)) // eRenderView
|
||||
((ObjectID, id))
|
||||
,
|
||||
((sObjectSettings, settings))
|
||||
);
|
||||
|
||||
COMMAND(SetObjectSettings, NOMERGE,
|
||||
((int, view)) // eRenderView
|
||||
@ -530,6 +546,14 @@ QUERY(GetPlayerObjects,
|
||||
((std::vector<ObjectID>, ids))
|
||||
);
|
||||
|
||||
MESSAGE(SetBandbox,
|
||||
((bool, show))
|
||||
((int, sx0))
|
||||
((int, sy0))
|
||||
((int, sx1))
|
||||
((int, sy1))
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
QUERY(GetCinemaPaths,
|
||||
|
@ -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
|
||||
@ -84,7 +84,7 @@ SHAREABLE_STRUCT(Colour);
|
||||
|
||||
|
||||
typedef size_t ObjectID;
|
||||
inline bool ObjectIDIsValid(ObjectID id) { return (id != ~(size_t)0); }
|
||||
inline bool ObjectIDIsValid(ObjectID id) { return (id != 0); }
|
||||
|
||||
|
||||
struct sCinemaSplineNode
|
||||
|
@ -166,7 +166,7 @@ static void delete_pair_2nd(std::pair<T,S> v)
|
||||
}
|
||||
|
||||
ViewGame::ViewGame()
|
||||
: m_SpeedMultiplier(0.f)
|
||||
: m_SpeedMultiplier(0.f)
|
||||
{
|
||||
ENSURE(g_Game);
|
||||
}
|
||||
@ -262,6 +262,49 @@ void ViewGame::Render()
|
||||
Atlas_GLSwapBuffers((void*)g_GameLoop->glCanvas);
|
||||
}
|
||||
|
||||
void ViewGame::DrawOverlays()
|
||||
{
|
||||
#if CONFIG2_GLES
|
||||
#warning TODO: implement Atlas game overlays for GLES
|
||||
#else
|
||||
// Set up transform for overlays
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
CMatrix3D transform;
|
||||
transform.SetIdentity();
|
||||
transform.Scale(1.0f, -1.f, 1.0f);
|
||||
transform.Translate(0.0f, (float)g_yres, -1000.0f);
|
||||
CMatrix3D proj;
|
||||
proj.SetOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f);
|
||||
transform = proj * transform;
|
||||
glLoadMatrixf(&transform._11);
|
||||
|
||||
if (m_BandboxArray.size() > 0)
|
||||
{
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
// Render bandbox as array of lines
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(SBandboxVertex), &m_BandboxArray[0].x);
|
||||
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(SBandboxVertex), &m_BandboxArray[0].r);
|
||||
|
||||
glDrawArrays(GL_LINES, 0, m_BandboxArray.size());
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
}
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ViewGame::SetParam(const std::wstring& name, bool value)
|
||||
{
|
||||
if (name == L"priorities")
|
||||
@ -353,6 +396,39 @@ std::wstring ViewGame::DumpState(bool binary)
|
||||
}
|
||||
}
|
||||
|
||||
void ViewGame::SetBandbox(bool visible, float x0, float y0, float x1, float y1)
|
||||
{
|
||||
m_BandboxArray.clear();
|
||||
|
||||
if (visible)
|
||||
{
|
||||
// Make sure corners are arranged in correct order
|
||||
if (x0 > x1)
|
||||
std::swap(x0, x1);
|
||||
if (y0 > y1)
|
||||
std::swap(y0, y1);
|
||||
|
||||
// Bandbox is draw as lines comprising two rectangles
|
||||
SBandboxVertex vert[] = {
|
||||
// Black - outer rectangle
|
||||
SBandboxVertex(x0, y0, 0, 0, 0, 255), SBandboxVertex(x1, y0, 0, 0, 0, 255), SBandboxVertex(x1, y1, 0, 0, 0, 255), SBandboxVertex(x0, y1, 0, 0, 0, 255),
|
||||
// White - inner rectangle
|
||||
SBandboxVertex(x0+1.0f, y0+1.0f, 255, 255, 255, 255), SBandboxVertex(x1-1.0f, y0+1.0f, 255, 255, 255, 255), SBandboxVertex(x1-1.0f, y1-1.0f, 255, 255, 255, 255), SBandboxVertex(x0+1.0f, y1-1.0f, 255, 255, 255, 255)
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
m_BandboxArray.push_back(vert[i]);
|
||||
m_BandboxArray.push_back(vert[(i+1)%4]);
|
||||
}
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
m_BandboxArray.push_back(vert[i+4]);
|
||||
m_BandboxArray.push_back(vert[(i+1)%4+4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ViewNone* view_None = NULL;
|
||||
|
@ -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
|
||||
@ -37,6 +37,7 @@ public:
|
||||
virtual ~View();
|
||||
virtual void Update(float UNUSED(frameLength)) { };
|
||||
virtual void Render() { };
|
||||
virtual void DrawOverlays() { };
|
||||
virtual CCamera& GetCamera() = 0;
|
||||
virtual CSimulation2* GetSimulation2() { return NULL; }
|
||||
virtual entity_id_t GetEntityId(AtlasMessage::ObjectID obj) { return (entity_id_t)obj; }
|
||||
@ -76,6 +77,7 @@ public:
|
||||
virtual ~ViewGame();
|
||||
virtual void Update(float frameLength);
|
||||
virtual void Render();
|
||||
virtual void DrawOverlays();
|
||||
virtual CCamera& GetCamera();
|
||||
virtual CSimulation2* GetSimulation2();
|
||||
virtual bool WantsHighFramerate();
|
||||
@ -87,11 +89,21 @@ public:
|
||||
void SaveState(const std::wstring& label);
|
||||
void RestoreState(const std::wstring& label);
|
||||
std::wstring DumpState(bool binary);
|
||||
void SetBandbox(bool visible, float x0, float y0, float x1, float y1);
|
||||
|
||||
private:
|
||||
float m_SpeedMultiplier;
|
||||
std::map<std::wstring, SimState*> m_SavedStates;
|
||||
std::string m_DisplayPassability;
|
||||
|
||||
typedef struct SBandboxVertex
|
||||
{
|
||||
SBandboxVertex(float x, float y, u8 r, u8 g, u8 b, u8 a) : x(x), y(y), r(r), g(g), b(b), a(a) {}
|
||||
u8 r, g, b, a;
|
||||
float x, y;
|
||||
} SBandboxVertex;
|
||||
|
||||
std::vector<SBandboxVertex> m_BandboxArray;
|
||||
};
|
||||
|
||||
class ActorViewer;
|
||||
|
Loading…
Reference in New Issue
Block a user