diff --git a/source/graphics/Unit.h b/source/graphics/Unit.h index 678a7a9a79..e437881dcd 100644 --- a/source/graphics/Unit.h +++ b/source/graphics/Unit.h @@ -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; } diff --git a/source/tools/atlas/AtlasUI/CustomControls/SnapSplitterWindow/SnapSplitterWindow.h b/source/tools/atlas/AtlasUI/CustomControls/SnapSplitterWindow/SnapSplitterWindow.h index a76b204f0f..3c5b7b2efe 100644 --- a/source/tools/atlas/AtlasUI/CustomControls/SnapSplitterWindow/SnapSplitterWindow.h +++ b/source/tools/atlas/AtlasUI/CustomControls/SnapSplitterWindow/SnapSplitterWindow.h @@ -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); diff --git a/source/tools/atlas/AtlasUI/General/Observable.h b/source/tools/atlas/AtlasUI/General/Observable.h new file mode 100644 index 0000000000..a2cd227ee7 --- /dev/null +++ b/source/tools/atlas/AtlasUI/General/Observable.h @@ -0,0 +1,28 @@ +#ifndef OBSERVABLE_H__ +#define OBSERVABLE_H__ + +#include +#include + +typedef boost::signals::connection ObservableConnection; + +template class Observable : public T +{ +public: + template 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 m_Signal; +}; + +#endif // OBSERVABLE_H__ diff --git a/source/tools/atlas/AtlasUI/Misc/stdafx.h b/source/tools/atlas/AtlasUI/Misc/stdafx.h index 8211fa894f..128937984d 100644 --- a/source/tools/atlas/AtlasUI/Misc/stdafx.h +++ b/source/tools/atlas/AtlasUI/Misc/stdafx.h @@ -34,6 +34,8 @@ #include #include #include +#include +#include // Nicer memory-leak detection: #ifdef _DEBUG diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp index 13ac5ad08b..70a099e43a 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp @@ -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); } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.cpp index ad957ae3ac..6081d2d778 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.cpp @@ -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); } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.h index 2782eeb177..891bf037ae 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.h @@ -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; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp index 7bfa424556..e9a3517895 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp @@ -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 diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.h index 5802ad8d2d..81b2ab603c 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.h @@ -3,5 +3,5 @@ class MapSidebar : public Sidebar { public: - MapSidebar(wxWindow* parent); + MapSidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer); }; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp index c149055d8f..1be39e1c17 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp @@ -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 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& 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); } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.h index e52ddf54da..1d2e6b0f22 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.h @@ -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: diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp index 2bb8e739e2..b27992745a 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp @@ -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(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(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(); +} \ No newline at end of file diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.h index 75200c7e06..c5f050462c 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.h @@ -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(); }; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.cpp index 3ee45ddbf7..e8dacaa7af 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.cpp @@ -3,3 +3,5 @@ #include "MiscState.h" wxString g_SelectedTexture = _T("grass1_spring"); + +Observable > g_SelectedObjects; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.h index a218fa2685..88d6393b69 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.h @@ -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 > g_SelectedObjects; + #endif // MISCSTATE_H__ diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.cpp new file mode 100644 index 0000000000..d332ffe3df --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.cpp @@ -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& selections) +{ + m_ActorSelections = selections; + PostToGame(); +} + +AtlasMessage::sObjectSettings ObjectSettings::GetSettings() const +{ + AtlasMessage::sObjectSettings settings; + + settings.player = m_PlayerID; + + std::vector selections; + for (std::set::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& 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 selections = *qry.settings->selections; + + m_ActorSelections.clear(); + for (std::vector::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())); +} \ No newline at end of file diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.h new file mode 100644 index 0000000000..9a126f6603 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.h @@ -0,0 +1,47 @@ +#ifndef ObjectSettings_H__ +#define ObjectSettings_H__ + +#include + +#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& 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 m_ActorSelections; + + // Observe changes to unit selection + ObservableConnection m_Conn; + void OnSelectionChange(const std::vector& selection); + + // Transfer current settings to the currently selected unit (if any) + void PostToGame(); +}; + +extern ObjectSettings g_ObjectSettings; + +#endif // ObjectSettings_H__ diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PlaceObject.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PlaceObject.cpp index 23642cfc9d..3a70752b65 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PlaceObject.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PlaceObject.cpp @@ -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) diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp index 19e6740fe0..1f107b721d 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp @@ -11,7 +11,6 @@ class TransformObject : public StateDrivenTool { DECLARE_DYNAMIC_CLASS(TransformObject); - std::vector 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 diff --git a/source/tools/atlas/GameInterface/CommandProc.cpp b/source/tools/atlas/GameInterface/CommandProc.cpp index 36955426e1..41e5b82114 100644 --- a/source/tools/atlas/GameInterface/CommandProc.cpp +++ b/source/tools/atlas/GameInterface/CommandProc.cpp @@ -8,15 +8,6 @@ template T next(T x) { T t = x; return ++t; } -template void delete_erase(T list, I first, I last) -{ - while (first != last) - { - delete *first; - first = list.erase(first); - } -} - template 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); + 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(); diff --git a/source/tools/atlas/GameInterface/CommandProc.h b/source/tools/atlas/GameInterface/CommandProc.h index 7c6af79ad5..7622cd3927 100644 --- a/source/tools/atlas/GameInterface/CommandProc.h +++ b/source/tools/atlas/GameInterface/CommandProc.h @@ -36,6 +36,8 @@ public: private: std::list m_Commands; typedef std::list::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; } diff --git a/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp index 6f9cd740b0..b965e144bc 100644 --- a/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp @@ -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; diff --git a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp index 5c9ba15249..d411ab56bd 100644 --- a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp @@ -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 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()) diff --git a/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp index a841788aaa..802f591be2 100644 --- a/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp @@ -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); diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index e84c66cd5e..b315f16cad 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -102,16 +102,16 @@ QUERY(GetObjectsList, ((std::vector, objects)) ); -struct sUnitSettings +struct sObjectSettings { Shareable player; Shareable > 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, ids)) ); +QUERY(GetObjectSettings, + ((ObjectID, id)) + , + ((sObjectSettings, settings)) + ); + +COMMAND(SetObjectSettings, NOMERGE, + ((ObjectID, id)) + ((sObjectSettings, settings)) + ); + + ////////////////////////////////////////////////////////////////////////// #include "MessagesSetup.h"