forked from 0ad/0ad
# Atlas updates: better unit ownership control; fixed bottom-bar display; fixed iterator problem in undo; renamed 'd' to 'msg' for consistency
This was SVN commit r3836.
This commit is contained in:
parent
37663d86fb
commit
9e74e3a077
@ -51,6 +51,9 @@ public:
|
||||
// Set player ID of this unit
|
||||
void SetPlayerID(int id);
|
||||
|
||||
// Get player ID of this unit
|
||||
int GetPlayerID() { return m_PlayerID; }
|
||||
|
||||
int GetID() const { return m_ID; }
|
||||
void SetID(int id) { m_ID = id; }
|
||||
|
||||
|
@ -5,8 +5,8 @@ class SnapSplitterWindow : public wxSplitterWindow
|
||||
public:
|
||||
SnapSplitterWindow(wxWindow* parent, long style = wxSP_3D);
|
||||
void SetDefaultSashPosition(int sashPosition);
|
||||
virtual bool SplitVertically(wxWindow *window1, wxWindow *window2);
|
||||
virtual bool SplitHorizontally(wxWindow *window1, wxWindow *window2);
|
||||
virtual bool SplitVertically(wxWindow* window1, wxWindow* window2);
|
||||
virtual bool SplitHorizontally(wxWindow* window1, wxWindow* window2);
|
||||
|
||||
private:
|
||||
void OnSashPosChanging(wxSplitterEvent& evt);
|
||||
|
28
source/tools/atlas/AtlasUI/General/Observable.h
Normal file
28
source/tools/atlas/AtlasUI/General/Observable.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef OBSERVABLE_H__
|
||||
#define OBSERVABLE_H__
|
||||
|
||||
#include <boost/signals.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
typedef boost::signals::connection ObservableConnection;
|
||||
|
||||
template <typename T> class Observable : public T
|
||||
{
|
||||
public:
|
||||
template<typename C> ObservableConnection RegisterObserver(int order, void (C::*callback) (const T&), C* obj)
|
||||
{
|
||||
return m_Signal.connect(order, boost::bind(std::mem_fun(callback), obj, _1));
|
||||
}
|
||||
void RemoveObserver(ObservableConnection handle)
|
||||
{
|
||||
handle.disconnect();
|
||||
}
|
||||
void NotifyObservers()
|
||||
{
|
||||
m_Signal(*this);
|
||||
}
|
||||
private:
|
||||
boost::signal<void (const T&)> m_Signal;
|
||||
};
|
||||
|
||||
#endif // OBSERVABLE_H__
|
@ -34,6 +34,8 @@
|
||||
#include <boost/preprocessor/seq/for_each_i.hpp>
|
||||
#include <boost/preprocessor/tuple/elem.hpp>
|
||||
#include <boost/preprocessor/punctuation/comma_if.hpp>
|
||||
#include <boost/signals.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
// Nicer memory-leak detection:
|
||||
#ifdef _DEBUG
|
||||
|
@ -36,13 +36,16 @@ protected:
|
||||
if (event.GetSelection() != -1)
|
||||
newPage = wxDynamicCast(GetPage(event.GetSelection()), Sidebar);
|
||||
|
||||
if (oldPage)
|
||||
oldPage->OnSwitchAway();
|
||||
|
||||
if (newPage)
|
||||
newPage->OnSwitchTo();
|
||||
|
||||
if (m_Splitter->IsSplit())
|
||||
{
|
||||
wxWindow* bottom;
|
||||
if (newPage && NULL != (bottom = newPage->GetBottomBar(m_Splitter)))
|
||||
if (newPage && NULL != (bottom = newPage->GetBottomBar()))
|
||||
{
|
||||
m_Splitter->ReplaceWindow(m_Splitter->GetWindow2(), bottom);
|
||||
}
|
||||
@ -54,7 +57,7 @@ protected:
|
||||
else
|
||||
{
|
||||
wxWindow* bottom;
|
||||
if (newPage && NULL != (bottom = newPage->GetBottomBar(m_Splitter)))
|
||||
if (newPage && NULL != (bottom = newPage->GetBottomBar()))
|
||||
{
|
||||
m_Splitter->SplitHorizontally(m_Splitter->GetWindow1(), bottom);
|
||||
}
|
||||
@ -104,14 +107,24 @@ void SectionLayout::Build()
|
||||
{
|
||||
// TODO: wxWidgets bug (http://sourceforge.net/tracker/index.php?func=detail&aid=1298803&group_id=9863&atid=109863)
|
||||
// - pressing menu keys (e.g. alt+f) with notebook tab focussed causes application to freeze
|
||||
wxNotebook* sidebar = new SidebarNotebook(m_HorizSplitter, m_VertSplitter);
|
||||
sidebar->AddPage(new MapSidebar(sidebar), _("Map"), false);
|
||||
sidebar->AddPage(new TerrainSidebar(sidebar), _("Terrain"), false);
|
||||
sidebar->AddPage(new ObjectSidebar(sidebar), _("Object"), false);
|
||||
SidebarNotebook* sidebarBook = new SidebarNotebook(m_HorizSplitter, m_VertSplitter);
|
||||
Sidebar* sidebar;
|
||||
|
||||
#define ADD_SIDEBAR(classname, label) \
|
||||
sidebar = new classname(sidebarBook, m_VertSplitter); \
|
||||
if (sidebar->GetBottomBar()) \
|
||||
sidebar->GetBottomBar()->Show(false); \
|
||||
sidebarBook->AddPage(sidebar, _(label));
|
||||
|
||||
ADD_SIDEBAR(MapSidebar, "Map");
|
||||
ADD_SIDEBAR(TerrainSidebar, "Terrain");
|
||||
ADD_SIDEBAR(ObjectSidebar, "Object");
|
||||
|
||||
#undef ADD_SIDEBAR
|
||||
|
||||
m_VertSplitter->SetDefaultSashPosition(-165);
|
||||
m_VertSplitter->Initialize(m_Canvas);
|
||||
|
||||
m_HorizSplitter->SetDefaultSashPosition(200);
|
||||
m_HorizSplitter->SplitVertically(sidebar, m_VertSplitter);
|
||||
m_HorizSplitter->SplitVertically(sidebarBook, m_VertSplitter);
|
||||
}
|
||||
|
@ -4,23 +4,27 @@
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(Sidebar, wxPanel)
|
||||
|
||||
Sidebar::Sidebar(wxWindow* parent)
|
||||
: wxPanel(parent), m_AlreadyDisplayed(false)
|
||||
Sidebar::Sidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
|
||||
: wxPanel(sidebarContainer), m_BottomBar(NULL), m_AlreadyDisplayed(false)
|
||||
{
|
||||
m_MainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
SetSizer(m_MainSizer);
|
||||
}
|
||||
|
||||
wxWindow* Sidebar::GetBottomBar(wxWindow* WXUNUSED(parent))
|
||||
void Sidebar::OnSwitchAway()
|
||||
{
|
||||
return NULL;
|
||||
if (m_BottomBar)
|
||||
m_BottomBar->Show(false);
|
||||
}
|
||||
|
||||
void Sidebar::OnSwitchTo()
|
||||
{
|
||||
if (m_AlreadyDisplayed)
|
||||
return;
|
||||
if (! m_AlreadyDisplayed)
|
||||
{
|
||||
m_AlreadyDisplayed = true;
|
||||
OnFirstDisplay();
|
||||
}
|
||||
|
||||
m_AlreadyDisplayed = true;
|
||||
OnFirstDisplay();
|
||||
if (m_BottomBar)
|
||||
m_BottomBar->Show(true);
|
||||
}
|
||||
|
@ -7,20 +7,21 @@ class Sidebar : public wxPanel
|
||||
|
||||
public:
|
||||
Sidebar() {}
|
||||
Sidebar(wxWindow* parent);
|
||||
Sidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer);
|
||||
|
||||
void OnSwitchAway();
|
||||
void OnSwitchTo();
|
||||
|
||||
virtual wxWindow* GetBottomBar(wxWindow* parent);
|
||||
// called whenever the bottom bar is made visible; should usually be
|
||||
// lazily constructed, then cached forever (to maximise responsiveness)
|
||||
wxWindow* GetBottomBar() { return m_BottomBar; }
|
||||
|
||||
protected:
|
||||
wxSizer* m_MainSizer; // vertical box sizer, used by most sidebars
|
||||
|
||||
wxWindow* m_BottomBar; // window that goes at the bottom of the screen; may be NULL
|
||||
|
||||
virtual void OnFirstDisplay() {}
|
||||
// should be overridden when sidebars need to do expensive construction,
|
||||
// so it can be delayed until it is required
|
||||
// so it can be delayed until it is required;
|
||||
|
||||
private:
|
||||
bool m_AlreadyDisplayed;
|
||||
|
@ -69,8 +69,8 @@ static void GenerateRMS(void* data)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MapSidebar::MapSidebar(wxWindow* parent)
|
||||
: Sidebar(parent)
|
||||
MapSidebar::MapSidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
|
||||
: Sidebar(sidebarContainer, bottomBarContainer)
|
||||
{
|
||||
// TODO: Less ugliness
|
||||
// TODO: Intercept arrow keys and send them to the GL window
|
||||
|
@ -3,5 +3,5 @@
|
||||
class MapSidebar : public Sidebar
|
||||
{
|
||||
public:
|
||||
MapSidebar(wxWindow* parent);
|
||||
MapSidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer);
|
||||
};
|
||||
|
@ -5,7 +5,8 @@
|
||||
#include "Buttons/ActionButton.h"
|
||||
#include "Buttons/ToolButton.h"
|
||||
#include "ScenarioEditor/Tools/Common/Tools.h"
|
||||
#include "ScenarioEditor/Tools/Common/UnitSettings.h"
|
||||
#include "ScenarioEditor/Tools/Common/ObjectSettings.h"
|
||||
#include "ScenarioEditor/Tools/Common/MiscState.h"
|
||||
|
||||
#include "GameInterface/Messages.h"
|
||||
|
||||
@ -62,8 +63,8 @@ struct ObjectSidebarImpl
|
||||
std::vector<AtlasMessage::sObjectsListItem> m_Objects;
|
||||
};
|
||||
|
||||
ObjectSidebar::ObjectSidebar(wxWindow* parent)
|
||||
: Sidebar(parent), p(new ObjectSidebarImpl())
|
||||
ObjectSidebar::ObjectSidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
|
||||
: Sidebar(sidebarContainer, bottomBarContainer), p(new ObjectSidebarImpl())
|
||||
{
|
||||
wxArrayString strings;
|
||||
strings.Add(_("Entities"));
|
||||
@ -72,6 +73,8 @@ ObjectSidebar::ObjectSidebar(wxWindow* parent)
|
||||
|
||||
p->m_ObjectListBox = new ObjectSelectListBox(this);
|
||||
m_MainSizer->Add(p->m_ObjectListBox, wxSizerFlags().Proportion(1).Expand());
|
||||
|
||||
m_BottomBar = new ObjectBottomBar(bottomBarContainer);
|
||||
}
|
||||
|
||||
ObjectSidebar::~ObjectSidebar()
|
||||
@ -79,15 +82,6 @@ ObjectSidebar::~ObjectSidebar()
|
||||
delete p;
|
||||
}
|
||||
|
||||
wxWindow* ObjectSidebar::GetBottomBar(wxWindow* parent)
|
||||
{
|
||||
if (p->m_BottomBar)
|
||||
return p->m_BottomBar;
|
||||
|
||||
p->m_BottomBar = new ObjectBottomBar(parent);
|
||||
return p->m_BottomBar;
|
||||
}
|
||||
|
||||
void ObjectSidebar::OnFirstDisplay()
|
||||
{
|
||||
AtlasMessage::qGetObjectsList qry;
|
||||
@ -118,15 +112,27 @@ class PlayerComboBox : public wxComboBox
|
||||
{
|
||||
public:
|
||||
PlayerComboBox(wxWindow* parent, wxArrayString& choices)
|
||||
: wxComboBox(parent, -1, choices[g_UnitSettings.GetPlayerID()], wxDefaultPosition, wxDefaultSize, choices, wxCB_READONLY)
|
||||
: wxComboBox(parent, -1, choices[g_ObjectSettings.GetPlayerID()], wxDefaultPosition, wxDefaultSize, choices, wxCB_READONLY)
|
||||
{
|
||||
m_Conn = g_SelectedObjects.RegisterObserver(1, &PlayerComboBox::OnSelectionChange, this);
|
||||
}
|
||||
~PlayerComboBox()
|
||||
{
|
||||
g_SelectedObjects.RemoveObserver(m_Conn);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
ObservableConnection m_Conn;
|
||||
|
||||
void OnSelectionChange(const std::vector<AtlasMessage::ObjectID>& selection)
|
||||
{
|
||||
SetSelection(g_ObjectSettings.GetPlayerID());
|
||||
}
|
||||
|
||||
void OnSelect(wxCommandEvent& evt)
|
||||
{
|
||||
g_UnitSettings.SetPlayerID(evt.GetInt());
|
||||
g_ObjectSettings.SetPlayerID(evt.GetInt());
|
||||
}
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
@ -135,11 +141,12 @@ BEGIN_EVENT_TABLE(PlayerComboBox, wxComboBox)
|
||||
EVT_COMBOBOX(wxID_ANY, OnSelect)
|
||||
END_EVENT_TABLE();
|
||||
|
||||
|
||||
ObjectBottomBar::ObjectBottomBar(wxWindow* parent)
|
||||
: wxPanel(parent, wxID_ANY)
|
||||
{
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
|
||||
wxArrayString players;
|
||||
// TODO: get proper player names
|
||||
players.Add(_("Gaia"));
|
||||
@ -153,5 +160,6 @@ ObjectBottomBar::ObjectBottomBar(wxWindow* parent)
|
||||
players.Add(_("Player 8"));
|
||||
wxComboBox* playerSelect = new PlayerComboBox(this, players);
|
||||
|
||||
sizer->Add(playerSelect);
|
||||
SetSizer(sizer);
|
||||
}
|
||||
|
@ -4,9 +4,8 @@ struct ObjectSidebarImpl;
|
||||
class ObjectSidebar : public Sidebar
|
||||
{
|
||||
public:
|
||||
ObjectSidebar(wxWindow* parent);
|
||||
ObjectSidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer);
|
||||
~ObjectSidebar();
|
||||
wxWindow* GetBottomBar(wxWindow* parent);
|
||||
void SetObjectFilter(int type);
|
||||
|
||||
protected:
|
||||
|
@ -13,8 +13,20 @@
|
||||
|
||||
#include "wx/spinctrl.h"
|
||||
|
||||
TerrainSidebar::TerrainSidebar(wxWindow* parent)
|
||||
: Sidebar(parent), m_BottomBar(NULL)
|
||||
class TextureNotebook;
|
||||
|
||||
class TerrainBottomBar : public wxPanel
|
||||
{
|
||||
public:
|
||||
TerrainBottomBar(wxWindow* parent);
|
||||
void LoadTerrain();
|
||||
private:
|
||||
TextureNotebook* m_Textures;
|
||||
};
|
||||
|
||||
|
||||
TerrainSidebar::TerrainSidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
|
||||
: Sidebar(sidebarContainer, bottomBarContainer)
|
||||
{
|
||||
// TODO: Less ugliness
|
||||
|
||||
@ -36,15 +48,12 @@ TerrainSidebar::TerrainSidebar(wxWindow* parent)
|
||||
m_MainSizer->Add(sizer);
|
||||
}
|
||||
|
||||
m_BottomBar = new TerrainBottomBar(bottomBarContainer);
|
||||
}
|
||||
|
||||
wxWindow* TerrainSidebar::GetBottomBar(wxWindow* parent)
|
||||
void TerrainSidebar::OnFirstDisplay()
|
||||
{
|
||||
if (m_BottomBar)
|
||||
return m_BottomBar;
|
||||
|
||||
m_BottomBar = new TerrainBottomBar(parent);
|
||||
return m_BottomBar;
|
||||
static_cast<TerrainBottomBar*>(m_BottomBar)->LoadTerrain();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -145,6 +154,13 @@ public:
|
||||
TextureNotebook(wxWindow *parent)
|
||||
: wxNotebook(parent, wxID_ANY/*, wxDefaultPosition, wxDefaultSize, wxNB_FIXEDWIDTH*/)
|
||||
{
|
||||
}
|
||||
|
||||
void LoadTerrain()
|
||||
{
|
||||
DeleteAllPages();
|
||||
m_TerrainGroups.Clear();
|
||||
|
||||
// Get the list of terrain groups from the engine
|
||||
AtlasMessage::qGetTerrainGroups qry;
|
||||
qry.Post();
|
||||
@ -164,7 +180,7 @@ public:
|
||||
protected:
|
||||
void OnPageChanged(wxNotebookEvent& event)
|
||||
{
|
||||
if (event.GetSelection() != -1)
|
||||
if (event.GetSelection() >= 0 && event.GetSelection() < (int)GetPageCount())
|
||||
{
|
||||
static_cast<TextureNotebookPage*>(GetPage(event.GetSelection()))->OnDisplay();
|
||||
}
|
||||
@ -186,7 +202,12 @@ TerrainBottomBar::TerrainBottomBar(wxWindow* parent)
|
||||
: wxPanel(parent, wxID_ANY)
|
||||
{
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
wxNotebook* notebook = new TextureNotebook(this);
|
||||
sizer->Add(notebook, wxSizerFlags().Expand().Proportion(1));
|
||||
m_Textures = new TextureNotebook(this);
|
||||
sizer->Add(m_Textures, wxSizerFlags().Expand().Proportion(1));
|
||||
SetSizer(sizer);
|
||||
}
|
||||
|
||||
void TerrainBottomBar::LoadTerrain()
|
||||
{
|
||||
m_Textures->LoadTerrain();
|
||||
}
|
@ -3,16 +3,8 @@
|
||||
class TerrainSidebar : public Sidebar
|
||||
{
|
||||
public:
|
||||
TerrainSidebar(wxWindow* parent);
|
||||
wxWindow* GetBottomBar(wxWindow* parent);
|
||||
TerrainSidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer);
|
||||
|
||||
private:
|
||||
wxWindow* m_BottomBar;
|
||||
};
|
||||
|
||||
class TerrainBottomBar : public wxPanel
|
||||
{
|
||||
public:
|
||||
TerrainBottomBar(wxWindow* parent);
|
||||
private:
|
||||
protected:
|
||||
virtual void OnFirstDisplay();
|
||||
};
|
||||
|
@ -3,3 +3,5 @@
|
||||
#include "MiscState.h"
|
||||
|
||||
wxString g_SelectedTexture = _T("grass1_spring");
|
||||
|
||||
Observable<std::vector<AtlasMessage::ObjectID> > g_SelectedObjects;
|
||||
|
@ -1,6 +1,18 @@
|
||||
#ifndef MISCSTATE_H__
|
||||
#define MISCSTATE_H__
|
||||
|
||||
#include "General/Observable.h"
|
||||
|
||||
namespace AtlasMessage
|
||||
{
|
||||
typedef int ObjectID;
|
||||
}
|
||||
|
||||
extern wxString g_SelectedTexture;
|
||||
|
||||
// Observer order:
|
||||
// 0 = g_UnitSettings
|
||||
// 1 = things that want to access g_UnitSettings
|
||||
extern Observable<std::vector<AtlasMessage::ObjectID> > g_SelectedObjects;
|
||||
|
||||
#endif // MISCSTATE_H__
|
||||
|
@ -0,0 +1,77 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "ObjectSettings.h"
|
||||
|
||||
#include "GameInterface/Messages.h"
|
||||
#include "ScenarioEditor/Tools/Common/Tools.h"
|
||||
|
||||
ObjectSettings g_ObjectSettings;
|
||||
|
||||
ObjectSettings::ObjectSettings()
|
||||
: m_PlayerID(0)
|
||||
{
|
||||
m_Conn = g_SelectedObjects.RegisterObserver(0, &ObjectSettings::OnSelectionChange, this);
|
||||
}
|
||||
|
||||
ObjectSettings::~ObjectSettings()
|
||||
{
|
||||
m_Conn.disconnect();
|
||||
}
|
||||
|
||||
int ObjectSettings::GetPlayerID()
|
||||
{
|
||||
return m_PlayerID;
|
||||
}
|
||||
|
||||
void ObjectSettings::SetPlayerID(int playerID)
|
||||
{
|
||||
m_PlayerID = playerID;
|
||||
PostToGame();
|
||||
}
|
||||
|
||||
void ObjectSettings::SetActorSelections(const std::set<wxString>& selections)
|
||||
{
|
||||
m_ActorSelections = selections;
|
||||
PostToGame();
|
||||
}
|
||||
|
||||
AtlasMessage::sObjectSettings ObjectSettings::GetSettings() const
|
||||
{
|
||||
AtlasMessage::sObjectSettings settings;
|
||||
|
||||
settings.player = m_PlayerID;
|
||||
|
||||
std::vector<std::wstring> selections;
|
||||
for (std::set<wxString>::const_iterator it = m_ActorSelections.begin(); it != m_ActorSelections.end(); ++it)
|
||||
selections.push_back(it->c_str());
|
||||
settings.selections = selections;
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
void ObjectSettings::OnSelectionChange(const std::vector<AtlasMessage::ObjectID>& selection)
|
||||
{
|
||||
// TODO: what would be the sensible action if nothing's selected?
|
||||
// and if multiple objects are selected?
|
||||
|
||||
if (selection.empty())
|
||||
return;
|
||||
|
||||
AtlasMessage::qGetObjectSettings qry (selection[0]);
|
||||
qry.Post();
|
||||
|
||||
m_PlayerID = qry.settings->player;
|
||||
std::vector<std::wstring> selections = *qry.settings->selections;
|
||||
|
||||
m_ActorSelections.clear();
|
||||
for (std::vector<std::wstring>::iterator it = selections.begin(); it != selections.end(); ++it)
|
||||
m_ActorSelections.insert(it->c_str());
|
||||
}
|
||||
|
||||
void ObjectSettings::PostToGame()
|
||||
{
|
||||
if (g_SelectedObjects.empty())
|
||||
return;
|
||||
|
||||
POST_COMMAND(SetObjectSettings, (g_SelectedObjects[0], GetSettings()));
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
#ifndef ObjectSettings_H__
|
||||
#define ObjectSettings_H__
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "ScenarioEditor/Tools/Common/MiscState.h"
|
||||
|
||||
namespace AtlasMessage
|
||||
{
|
||||
struct sObjectSettings;
|
||||
}
|
||||
|
||||
// Various settings to be applied to newly created units, or to the currently
|
||||
// selected unit. If a unit is selected or being previewed, it should match
|
||||
// these settings.
|
||||
class ObjectSettings
|
||||
{
|
||||
public:
|
||||
ObjectSettings();
|
||||
~ObjectSettings();
|
||||
|
||||
int GetPlayerID();
|
||||
void SetPlayerID(int playerID);
|
||||
void SetActorSelections(const std::set<wxString>& selections);
|
||||
|
||||
AtlasMessage::sObjectSettings GetSettings() const;
|
||||
|
||||
private:
|
||||
// 0 = gaia, 1..inf = normal players
|
||||
int m_PlayerID;
|
||||
|
||||
// Set of user-chosen actor selections, potentially a superset of any single
|
||||
// actor's possible variants (since it doesn't get reset if you select
|
||||
// a new actor, and will accumulate variant names)
|
||||
std::set<wxString> m_ActorSelections;
|
||||
|
||||
// Observe changes to unit selection
|
||||
ObservableConnection m_Conn;
|
||||
void OnSelectionChange(const std::vector<AtlasMessage::ObjectID>& selection);
|
||||
|
||||
// Transfer current settings to the currently selected unit (if any)
|
||||
void PostToGame();
|
||||
};
|
||||
|
||||
extern ObjectSettings g_ObjectSettings;
|
||||
|
||||
#endif // ObjectSettings_H__
|
@ -3,7 +3,7 @@
|
||||
#include "Common/Tools.h"
|
||||
#include "Common/Brushes.h"
|
||||
#include "Common/MiscState.h"
|
||||
#include "Common/UnitSettings.h"
|
||||
#include "Common/ObjectSettings.h"
|
||||
#include "GameInterface/Messages.h"
|
||||
|
||||
using AtlasMessage::Position;
|
||||
@ -30,9 +30,9 @@ public:
|
||||
+ (m_ScreenPos.type1.y-m_Target.type1.y)*(m_ScreenPos.type1.y-m_Target.type1.y);
|
||||
bool useTarget = (dragDistSq >= 16*16);
|
||||
if (preview)
|
||||
POST_MESSAGE(ObjectPreview, (m_ObjectID.c_str(), g_UnitSettings.GetSettings(), m_ObjPos, useTarget, m_Target, g_DefaultAngle));
|
||||
POST_MESSAGE(ObjectPreview, (m_ObjectID.c_str(), g_ObjectSettings.GetSettings(), m_ObjPos, useTarget, m_Target, g_DefaultAngle));
|
||||
else
|
||||
POST_COMMAND(CreateObject, (m_ObjectID.c_str(), g_UnitSettings.GetSettings(), m_ObjPos, useTarget, m_Target, g_DefaultAngle));
|
||||
POST_COMMAND(CreateObject, (m_ObjectID.c_str(), g_ObjectSettings.GetSettings(), m_ObjPos, useTarget, m_Target, g_DefaultAngle));
|
||||
}
|
||||
|
||||
virtual void Init(void* initData)
|
||||
|
@ -11,7 +11,6 @@ class TransformObject : public StateDrivenTool<TransformObject>
|
||||
{
|
||||
DECLARE_DYNAMIC_CLASS(TransformObject);
|
||||
|
||||
std::vector<AtlasMessage::ObjectID> m_Selection;
|
||||
int m_dx, m_dy;
|
||||
|
||||
public:
|
||||
@ -22,8 +21,9 @@ public:
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
m_Selection.clear();
|
||||
POST_MESSAGE(SetSelectionPreview, (m_Selection));
|
||||
g_SelectedObjects.clear();
|
||||
g_SelectedObjects.NotifyObservers();
|
||||
POST_MESSAGE(SetSelectionPreview, (g_SelectedObjects));
|
||||
}
|
||||
|
||||
|
||||
@ -36,25 +36,26 @@ public:
|
||||
if (evt.LeftDown())
|
||||
{
|
||||
// TODO: multiple selection
|
||||
AtlasMessage::qSelectObject qry(Position(evt.GetPosition()));
|
||||
AtlasMessage::qPickObject qry(Position(evt.GetPosition()));
|
||||
qry.Post();
|
||||
obj->m_Selection.clear();
|
||||
g_SelectedObjects.clear();
|
||||
if (AtlasMessage::ObjectIDIsValid(qry.id))
|
||||
{
|
||||
obj->m_Selection.push_back(qry.id);
|
||||
g_SelectedObjects.push_back(qry.id);
|
||||
obj->m_dx = qry.offsetx;
|
||||
obj->m_dy = qry.offsety;
|
||||
SET_STATE(Dragging);
|
||||
}
|
||||
POST_MESSAGE(SetSelectionPreview, (obj->m_Selection));
|
||||
g_SelectedObjects.NotifyObservers();
|
||||
POST_MESSAGE(SetSelectionPreview, (g_SelectedObjects));
|
||||
ScenarioEditor::GetCommandProc().FinaliseLastCommand();
|
||||
return true;
|
||||
}
|
||||
else if (evt.Dragging() && evt.RightIsDown() || evt.RightDown())
|
||||
{
|
||||
Position pos (evt.GetPosition());
|
||||
for (size_t i = 0; i < obj->m_Selection.size(); ++i)
|
||||
POST_COMMAND(RotateObject, (obj->m_Selection[i], true, pos, 0.f));
|
||||
for (size_t i = 0; i < g_SelectedObjects.size(); ++i)
|
||||
POST_COMMAND(RotateObject, (g_SelectedObjects[i], true, pos, 0.f));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -65,10 +66,11 @@ public:
|
||||
{
|
||||
if (type == KEY_CHAR && evt.GetKeyCode() == WXK_DELETE)
|
||||
{
|
||||
for (size_t i = 0; i < obj->m_Selection.size(); ++i)
|
||||
POST_COMMAND(DeleteObject, (obj->m_Selection[i]));
|
||||
obj->m_Selection.clear();
|
||||
POST_MESSAGE(SetSelectionPreview, (obj->m_Selection));
|
||||
for (size_t i = 0; i < g_SelectedObjects.size(); ++i)
|
||||
POST_COMMAND(DeleteObject, (g_SelectedObjects[i]));
|
||||
g_SelectedObjects.clear();
|
||||
g_SelectedObjects.NotifyObservers();
|
||||
POST_MESSAGE(SetSelectionPreview, (g_SelectedObjects));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -89,8 +91,8 @@ public:
|
||||
else if (evt.Dragging())
|
||||
{
|
||||
Position pos (evt.GetPosition() + wxPoint(obj->m_dx, obj->m_dy));
|
||||
for (size_t i = 0; i < obj->m_Selection.size(); ++i)
|
||||
POST_COMMAND(MoveObject, (obj->m_Selection[i], pos));
|
||||
for (size_t i = 0; i < g_SelectedObjects.size(); ++i)
|
||||
POST_COMMAND(MoveObject, (g_SelectedObjects[i], pos));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -8,15 +8,6 @@
|
||||
|
||||
template<typename T> T next(T x) { T t = x; return ++t; }
|
||||
|
||||
template<typename T, typename I> void delete_erase(T list, I first, I last)
|
||||
{
|
||||
while (first != last)
|
||||
{
|
||||
delete *first;
|
||||
first = list.erase(first);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> void delete_fn(T* v) { delete v; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -63,7 +54,12 @@ void CommandProc::Destroy()
|
||||
|
||||
void CommandProc::Submit(Command* cmd)
|
||||
{
|
||||
delete_erase(m_Commands, next(m_CurrentCommand), m_Commands.end());
|
||||
// If some commands have been undone at the time we insert this new one,
|
||||
// delete and remove them all.
|
||||
std::for_each(next(m_CurrentCommand), m_Commands.end(), delete_fn<Command>);
|
||||
m_Commands.erase(next(m_CurrentCommand), m_Commands.end());
|
||||
assert(next(m_CurrentCommand) == m_Commands.end());
|
||||
|
||||
m_CurrentCommand = m_Commands.insert(next(m_CurrentCommand), cmd);
|
||||
|
||||
(*m_CurrentCommand)->Do();
|
||||
|
@ -36,6 +36,8 @@ public:
|
||||
private:
|
||||
std::list<Command*> m_Commands;
|
||||
typedef std::list<Command*>::iterator cmdIt;
|
||||
// The 'current' command is the latest one which has been executed
|
||||
// (ignoring any that have been undone)
|
||||
cmdIt m_CurrentCommand;
|
||||
};
|
||||
|
||||
@ -49,7 +51,7 @@ struct DataCommand : public Command // so commands can optionally override (De|C
|
||||
{
|
||||
void Destruct() {};
|
||||
void Construct() {};
|
||||
// MergeWithSelf should be overriden by commands, and implemented
|
||||
// MergeWithSelf should be overridden by commands, and implemented
|
||||
// to update 'prev' to include the effects of 'this'
|
||||
void MergeWithSelf(void*) { debug_warn("MergeWithSelf unimplemented in some command"); }
|
||||
};
|
||||
@ -57,10 +59,10 @@ struct DataCommand : public Command // so commands can optionally override (De|C
|
||||
#define BEGIN_COMMAND(t) \
|
||||
class c##t : public DataCommand \
|
||||
{ \
|
||||
d##t* d; \
|
||||
d##t* msg; \
|
||||
public: \
|
||||
c##t(d##t* data) : d(data) { Construct(); } \
|
||||
~c##t() { Destruct(); AtlasMessage::ShareableDelete(d); /* d was allocated by mDoCommand() */ } \
|
||||
c##t(d##t* data) : msg(data) { Construct(); } \
|
||||
~c##t() { Destruct(); AtlasMessage::ShareableDelete(msg); /* msg was allocated by mDoCommand() */ } \
|
||||
static Command* Create(const void* data) { return new c##t ((d##t*)data); } \
|
||||
virtual void Merge(Command* prev) { MergeWithSelf((c##t*)prev); } \
|
||||
virtual const char* GetType() const { return #t; }
|
||||
|
@ -91,12 +91,12 @@ BEGIN_COMMAND(AlterElevation)
|
||||
|
||||
void Do()
|
||||
{
|
||||
int amount = (int)d->amount;
|
||||
int amount = (int)msg->amount;
|
||||
|
||||
// If the framerate is very high, 'amount' is often very
|
||||
// small (even zero) so the integer truncation is significant
|
||||
static float roundingError = 0.0;
|
||||
roundingError += d->amount - (float)amount;
|
||||
roundingError += msg->amount - (float)amount;
|
||||
if (roundingError >= 1.f)
|
||||
{
|
||||
amount += (int)roundingError;
|
||||
@ -104,7 +104,7 @@ BEGIN_COMMAND(AlterElevation)
|
||||
}
|
||||
|
||||
static CVector3D previousPosition;
|
||||
d->pos->GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition);
|
||||
msg->pos->GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition);
|
||||
previousPosition = g_CurrentBrush.m_Centre;
|
||||
|
||||
int x0, y0;
|
||||
@ -160,10 +160,10 @@ BEGIN_COMMAND(FlattenElevation)
|
||||
|
||||
void Do()
|
||||
{
|
||||
int amount = (int)d->amount;
|
||||
int amount = (int)msg->amount;
|
||||
|
||||
static CVector3D previousPosition;
|
||||
d->pos->GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition);
|
||||
msg->pos->GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition);
|
||||
previousPosition = g_CurrentBrush.m_Centre;
|
||||
|
||||
int xc, yc;
|
||||
|
@ -103,6 +103,54 @@ MESSAGEHANDLER(SetSelectionPreview)
|
||||
g_Selection = *msg->ids;
|
||||
}
|
||||
|
||||
QUERYHANDLER(GetObjectSettings)
|
||||
{
|
||||
CUnit* unit = g_UnitMan.FindByID(msg->id);
|
||||
if (! unit) return;
|
||||
|
||||
sObjectSettings settings;
|
||||
settings.player = unit->GetPlayerID();
|
||||
// TODO: actor variation
|
||||
|
||||
msg->settings = settings;
|
||||
}
|
||||
|
||||
BEGIN_COMMAND(SetObjectSettings)
|
||||
|
||||
int m_PlayerOld, m_PlayerNew;
|
||||
|
||||
void Do()
|
||||
{
|
||||
CUnit* unit = g_UnitMan.FindByID(msg->id);
|
||||
if (! unit) return;
|
||||
|
||||
sObjectSettings settings = msg->settings;
|
||||
|
||||
m_PlayerOld = unit->GetPlayerID();
|
||||
m_PlayerNew = settings.player;
|
||||
// TODO: actor variations
|
||||
|
||||
Redo();
|
||||
}
|
||||
|
||||
void Redo()
|
||||
{
|
||||
CUnit* unit = g_UnitMan.FindByID(msg->id);
|
||||
if (! unit) return;
|
||||
|
||||
unit->SetPlayerID(m_PlayerNew);
|
||||
}
|
||||
|
||||
void Undo()
|
||||
{
|
||||
CUnit* unit = g_UnitMan.FindByID(msg->id);
|
||||
if (! unit) return;
|
||||
|
||||
unit->SetPlayerID(m_PlayerOld);
|
||||
}
|
||||
|
||||
END_COMMAND(SetObjectSettings);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@ -234,21 +282,27 @@ BEGIN_COMMAND(CreateObject)
|
||||
|
||||
void Do()
|
||||
{
|
||||
m_Pos = GetUnitPos(d->pos);
|
||||
m_Player = d->settings->player;
|
||||
// Calculate the position/orientation to create this unit with
|
||||
|
||||
m_Pos = GetUnitPos(msg->pos);
|
||||
|
||||
if (d->usetarget)
|
||||
if (msg->usetarget)
|
||||
{
|
||||
// Aim from m_Pos towards msg->target
|
||||
CVector3D target;
|
||||
d->target->GetWorldSpace(target, m_Pos.Y);
|
||||
msg->target->GetWorldSpace(target, m_Pos.Y);
|
||||
CVector2D dir(target.X-m_Pos.X, target.Z-m_Pos.Z);
|
||||
m_Angle = atan2(dir.x, dir.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Angle = d->angle;
|
||||
m_Angle = msg->angle;
|
||||
}
|
||||
|
||||
// TODO: variations too
|
||||
m_Player = msg->settings->player;
|
||||
|
||||
// Get a new ID, for future reference to this unit
|
||||
m_ID = g_UnitMan.GetNewID();
|
||||
|
||||
Redo();
|
||||
@ -258,7 +312,7 @@ BEGIN_COMMAND(CreateObject)
|
||||
{
|
||||
bool isEntity;
|
||||
CStrW name;
|
||||
if (ParseObjectName(*d->id, isEntity, name))
|
||||
if (ParseObjectName(*msg->id, isEntity, name))
|
||||
{
|
||||
std::set<CStrW> selections;
|
||||
|
||||
@ -309,6 +363,8 @@ BEGIN_COMMAND(CreateObject)
|
||||
m._31 = s; m._32 = 0.0f; m._33 = -c; m._34 = m_Pos.Z;
|
||||
m._41 = 0.0f; m._42 = 0.0f; m._43 = 0.0f; m._44 = 1.0f;
|
||||
unit->GetModel()->SetTransform(m);
|
||||
|
||||
unit->SetPlayerID(m_Player);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -332,7 +388,7 @@ BEGIN_COMMAND(CreateObject)
|
||||
END_COMMAND(CreateObject)
|
||||
|
||||
|
||||
QUERYHANDLER(SelectObject)
|
||||
QUERYHANDLER(PickObject)
|
||||
{
|
||||
float x, y;
|
||||
msg->pos->GetScreenSpace(x, y);
|
||||
@ -369,7 +425,7 @@ BEGIN_COMMAND(MoveObject)
|
||||
|
||||
void Do()
|
||||
{
|
||||
CUnit* unit = g_UnitMan.FindByID(d->id);
|
||||
CUnit* unit = g_UnitMan.FindByID(msg->id);
|
||||
if (! unit) return;
|
||||
|
||||
if (unit->GetEntity())
|
||||
@ -382,14 +438,14 @@ BEGIN_COMMAND(MoveObject)
|
||||
m_PosOld = m.GetTranslation();
|
||||
}
|
||||
|
||||
m_PosNew = GetUnitPos(d->pos);
|
||||
m_PosNew = GetUnitPos(msg->pos);
|
||||
|
||||
SetPos(m_PosNew);
|
||||
}
|
||||
|
||||
void SetPos(CVector3D& pos)
|
||||
{
|
||||
CUnit* unit = g_UnitMan.FindByID(d->id);
|
||||
CUnit* unit = g_UnitMan.FindByID(msg->id);
|
||||
if (! unit) return;
|
||||
|
||||
if (unit->GetEntity())
|
||||
@ -430,23 +486,23 @@ BEGIN_COMMAND(RotateObject)
|
||||
|
||||
void Do()
|
||||
{
|
||||
CUnit* unit = g_UnitMan.FindByID(d->id);
|
||||
CUnit* unit = g_UnitMan.FindByID(msg->id);
|
||||
if (! unit) return;
|
||||
|
||||
if (unit->GetEntity())
|
||||
{
|
||||
m_AngleOld = unit->GetEntity()->m_orientation;
|
||||
if (d->usetarget)
|
||||
if (msg->usetarget)
|
||||
{
|
||||
CVector3D& pos = unit->GetEntity()->m_position;
|
||||
CVector3D target;
|
||||
d->target->GetWorldSpace(target, pos.Y);
|
||||
msg->target->GetWorldSpace(target, pos.Y);
|
||||
CVector2D dir(target.X-pos.X, target.Z-pos.Z);
|
||||
m_AngleNew = atan2(dir.x, dir.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_AngleNew = d->angle;
|
||||
m_AngleNew = msg->angle;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -456,10 +512,10 @@ BEGIN_COMMAND(RotateObject)
|
||||
CVector3D pos = unit->GetModel()->GetTransform().GetTranslation();
|
||||
|
||||
float s, c;
|
||||
if (d->usetarget)
|
||||
if (msg->usetarget)
|
||||
{
|
||||
CVector3D target;
|
||||
d->target->GetWorldSpace(target, pos.Y);
|
||||
msg->target->GetWorldSpace(target, pos.Y);
|
||||
CVector2D dir(target.X-pos.X, target.Z-pos.Z);
|
||||
dir = dir.normalize();
|
||||
s = dir.x;
|
||||
@ -467,8 +523,8 @@ BEGIN_COMMAND(RotateObject)
|
||||
}
|
||||
else
|
||||
{
|
||||
s = sinf(d->angle);
|
||||
c = cosf(d->angle);
|
||||
s = sinf(msg->angle);
|
||||
c = cosf(msg->angle);
|
||||
}
|
||||
CMatrix3D& m = m_TransformNew;
|
||||
m._11 = -c; m._12 = 0.0f; m._13 = -s; m._14 = pos.X;
|
||||
@ -482,7 +538,7 @@ BEGIN_COMMAND(RotateObject)
|
||||
|
||||
void SetAngle(float angle, CMatrix3D& transform)
|
||||
{
|
||||
CUnit* unit = g_UnitMan.FindByID(d->id);
|
||||
CUnit* unit = g_UnitMan.FindByID(msg->id);
|
||||
if (! unit) return;
|
||||
|
||||
if (unit->GetEntity())
|
||||
@ -542,7 +598,7 @@ BEGIN_COMMAND(DeleteObject)
|
||||
|
||||
void Redo()
|
||||
{
|
||||
CUnit* unit = g_UnitMan.FindByID(d->id);
|
||||
CUnit* unit = g_UnitMan.FindByID(msg->id);
|
||||
if (! unit) return;
|
||||
|
||||
if (unit->GetEntity())
|
||||
|
@ -165,12 +165,12 @@ BEGIN_COMMAND(PaintTerrain)
|
||||
void Do()
|
||||
{
|
||||
|
||||
d->pos->GetWorldSpace(g_CurrentBrush.m_Centre);
|
||||
msg->pos->GetWorldSpace(g_CurrentBrush.m_Centre);
|
||||
|
||||
int x0, y0;
|
||||
g_CurrentBrush.GetBottomLeft(x0, y0);
|
||||
|
||||
CTextureEntry* texentry = g_TexMan.FindTexture(CStrW(*d->texture));
|
||||
CTextureEntry* texentry = g_TexMan.FindTexture(CStrW(*msg->texture));
|
||||
if (! texentry)
|
||||
{
|
||||
debug_warn("Can't find texentry"); // TODO: nicer error handling
|
||||
@ -182,7 +182,7 @@ BEGIN_COMMAND(PaintTerrain)
|
||||
for (int dx = 0; dx < g_CurrentBrush.m_W; ++dx)
|
||||
{
|
||||
if (g_CurrentBrush.Get(dx, dy) > 0.5f) // TODO: proper solid brushes
|
||||
m_TerrainDelta.PaintTile(x0+dx, y0+dy, texture, d->priority);
|
||||
m_TerrainDelta.PaintTile(x0+dx, y0+dy, texture, msg->priority);
|
||||
}
|
||||
|
||||
g_Game->GetWorld()->GetTerrain()->MakeDirty(x0, y0, x0+g_CurrentBrush.m_W, y0+g_CurrentBrush.m_H, RENDERDATA_UPDATE_INDICES);
|
||||
|
@ -102,16 +102,16 @@ QUERY(GetObjectsList,
|
||||
((std::vector<sObjectsListItem>, objects))
|
||||
);
|
||||
|
||||
struct sUnitSettings
|
||||
struct sObjectSettings
|
||||
{
|
||||
Shareable<int> player;
|
||||
Shareable<std::vector<std::wstring> > selections;
|
||||
};
|
||||
SHAREABLE_STRUCT(sUnitSettings);
|
||||
SHAREABLE_STRUCT(sObjectSettings);
|
||||
|
||||
MESSAGE(ObjectPreview,
|
||||
((std::wstring, id)) // or empty string => disable
|
||||
((sUnitSettings, settings))
|
||||
((sObjectSettings, settings))
|
||||
((Position, pos))
|
||||
((bool, usetarget)) // true => use 'target' for orientation; false => use 'angle'
|
||||
((Position, target))
|
||||
@ -120,7 +120,7 @@ MESSAGE(ObjectPreview,
|
||||
|
||||
COMMAND(CreateObject, NOMERGE,
|
||||
((std::wstring, id))
|
||||
((sUnitSettings, settings))
|
||||
((sObjectSettings, settings))
|
||||
((Position, pos))
|
||||
((bool, usetarget)) // true => use 'target' for orientation; false => use 'angle'
|
||||
((Position, target))
|
||||
@ -183,7 +183,7 @@ COMMAND(PaintTerrain, MERGE,
|
||||
typedef int ObjectID;
|
||||
inline bool ObjectIDIsValid(ObjectID id) { return (id >= 0); }
|
||||
|
||||
QUERY(SelectObject,
|
||||
QUERY(PickObject,
|
||||
((Position, pos))
|
||||
,
|
||||
((ObjectID, id))
|
||||
@ -211,6 +211,18 @@ MESSAGE(SetSelectionPreview,
|
||||
((std::vector<ObjectID>, ids))
|
||||
);
|
||||
|
||||
QUERY(GetObjectSettings,
|
||||
((ObjectID, id))
|
||||
,
|
||||
((sObjectSettings, settings))
|
||||
);
|
||||
|
||||
COMMAND(SetObjectSettings, NOMERGE,
|
||||
((ObjectID, id))
|
||||
((sObjectSettings, settings))
|
||||
);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "MessagesSetup.h"
|
||||
|
Loading…
Reference in New Issue
Block a user