diff --git a/binaries/data/mods/public/simulation/components/PlayerManager.js b/binaries/data/mods/public/simulation/components/PlayerManager.js index 3a05d8f8e8..0151f21a86 100644 --- a/binaries/data/mods/public/simulation/components/PlayerManager.js +++ b/binaries/data/mods/public/simulation/components/PlayerManager.js @@ -36,4 +36,14 @@ PlayerManager.prototype.GetNumPlayers = function() return this.playerEntities.length; }; +PlayerManager.prototype.RemoveAllPlayers = function() +{ + // Destroy existing player entities + for each (id in this.playerEntities) + { + Engine.DestroyEntity(id); + } + this.playerEntities = []; +}; + Engine.RegisterComponentType(IID_PlayerManager, "PlayerManager", PlayerManager); diff --git a/binaries/data/mods/public/simulation/helpers/Player.js b/binaries/data/mods/public/simulation/helpers/Player.js index 1488e88c2b..89e9265c51 100644 --- a/binaries/data/mods/public/simulation/helpers/Player.js +++ b/binaries/data/mods/public/simulation/helpers/Player.js @@ -3,8 +3,11 @@ * Used to create player entities prior to reading the rest of a map, * all other initialization must be done after loading map (terrain/entities). * DO NOT use other components here, as they may fail unpredictably. + * settings is the object containing settings for this map. + * newPlayers if true will remove any old player entities and add new ones + * (used when loading a map or when Atlas changes the number of players). */ -function LoadPlayerSettings(settings) +function LoadPlayerSettings(settings, newPlayers) { // Default settings if (!settings) @@ -16,7 +19,7 @@ function LoadPlayerSettings(settings) var rawData = Engine.ReadJSONFile("player_defaults.json"); if (!(rawData && rawData.PlayerData)) { - throw("Player.js: Error reading player default data (player_defaults.json)"); + throw("Player.js: Error reading player_defaults.json"); } var playerDefaults = rawData.PlayerData; @@ -24,32 +27,47 @@ function LoadPlayerSettings(settings) // default number of players var numPlayers = 8; - if (settings.PlayerData) - { // Get number of players including gaia - numPlayers = settings.PlayerData.length + 1; - } - else - { - warn("Player.js: Setup has no player data - using defaults"); - } - // Get player manager - var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); - - for (var i = 0; i < numPlayers; ++i) + var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + + // Remove existing players and add new ones + if (newPlayers) { - // Add player entity to engine - var entID = Engine.AddEntity("special/player"); - - // Retrieve entity - var cmpPlayer = Engine.QueryInterface(entID, IID_Player); - if (!cmpPlayer) + cmpPlayerManager.RemoveAllPlayers(); + + if (settings.PlayerData) + { // Get number of players including gaia + numPlayers = settings.PlayerData.length + 1; + } + else { - throw("Player.js: Error creating player entity "+i); + warn("Player.js: Setup has no player data - using defaults"); } - cmpPlayer.SetPlayerID(i); - + for (var i = 0; i < numPlayers; ++i) + { + // Add player entity to engine + // TODO: Get player template name from civ data + var entID = Engine.AddEntity("special/player"); + var cmpPlayer = Engine.QueryInterface(entID, IID_Player); + if (!cmpPlayer) + { + throw("Player.js: Error creating player entity "+i); + } + + cmpPlayer.SetPlayerID(i); + + // Add player to player manager + cmpPlayerManager.AddPlayer(entID); + } + } + + numPlayers = cmpPlayerManager.GetNumPlayers(); + + // Initialize the player data + for (var i = 0; i < numPlayers; ++i) + { + var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(i), IID_Player); var pDefs = playerDefaults ? playerDefaults[i] : {}; // Skip gaia @@ -57,7 +75,6 @@ function LoadPlayerSettings(settings) { var pData = settings.PlayerData ? settings.PlayerData[i-1] : {}; - // Copy player data cmpPlayer.SetName(getSetting(pData, pDefs, "Name")); cmpPlayer.SetCiv(getSetting(pData, pDefs, "Civ")); cmpPlayer.SetAI(pData.AI && pData.AI != ""); @@ -120,9 +137,6 @@ function LoadPlayerSettings(settings) cmpPlayer.SetEnemy(j); } } - - // Add player to player manager - cmpPlayerMan.AddPlayer(entID); } } @@ -146,13 +160,13 @@ function getSetting(settings, defaults, property) */ function QueryOwnerInterface(ent, iid) { - var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership); if (!cmpOwnership) return null; - var playerEnt = cmpPlayerMan.GetPlayerByID(cmpOwnership.GetOwner()); + var playerEnt = cmpPlayerManager.GetPlayerByID(cmpOwnership.GetOwner()); if (!playerEnt) return null; @@ -166,9 +180,9 @@ function QueryOwnerInterface(ent, iid) */ function QueryPlayerIDInterface(id, iid) { - var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); - var playerEnt = cmpPlayerMan.GetPlayerByID(id); + var playerEnt = cmpPlayerManager.GetPlayerByID(id); if (!playerEnt) return null; @@ -193,8 +207,8 @@ function IsOwnedByAllyOfEntity(entity, target) if (cmpOwnershipTarget) targetOwner = cmpOwnershipTarget.GetOwner(); - var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); - var cmpPlayer = Engine.QueryInterface(cmpPlayerMan.GetPlayerByID(owner), IID_Player); + var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(owner), IID_Player); // Check for allied diplomacy status if (cmpPlayer.IsAlly(targetOwner)) @@ -223,8 +237,8 @@ function IsOwnedByAllyOfPlayer(player, target) if (cmpOwnershipTarget) targetOwner = cmpOwnershipTarget.GetOwner(); - var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); - var cmpPlayer = Engine.QueryInterface(cmpPlayerMan.GetPlayerByID(player), IID_Player); + var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(player), IID_Player); // Check for allied diplomacy status if (cmpPlayer.IsAlly(targetOwner)) @@ -244,8 +258,8 @@ function IsOwnedByEnemyOfPlayer(player, target) if (cmpOwnershipTarget) targetOwner = cmpOwnershipTarget.GetOwner(); - var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); - var cmpPlayer = Engine.QueryInterface(cmpPlayerMan.GetPlayerByID(player), IID_Player); + var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(player), IID_Player); // Check for allied diplomacy status if (cmpPlayer.IsEnemy(targetOwner)) diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index a5c0980c77..50912b0b47 100644 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -1029,7 +1029,7 @@ int CMapReader::LoadScriptSettings() // load player settings script int CMapReader::LoadPlayerSettings() { - pSimulation2->LoadPlayerSettings(); + pSimulation2->LoadPlayerSettings(true); return 0; } diff --git a/source/simulation2/Simulation2.cpp b/source/simulation2/Simulation2.cpp index 92b5dca9bb..0243253a6a 100644 --- a/source/simulation2/Simulation2.cpp +++ b/source/simulation2/Simulation2.cpp @@ -478,9 +478,9 @@ CScriptVal CSimulation2::GetMapSettings() return m->m_MapSettings.get(); } -void CSimulation2::LoadPlayerSettings() +void CSimulation2::LoadPlayerSettings(bool newPlayers) { - GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "LoadPlayerSettings", m->m_MapSettings); + GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "LoadPlayerSettings", m->m_MapSettings, newPlayers); } void CSimulation2::LoadMapSettings() diff --git a/source/simulation2/Simulation2.h b/source/simulation2/Simulation2.h index fc0a5bded6..eb705a476f 100644 --- a/source/simulation2/Simulation2.h +++ b/source/simulation2/Simulation2.h @@ -69,8 +69,10 @@ public: /** * Loads the player settings script (called before map is loaded) + * @param newPlayers will delete all the existing player entities (if any) and create new ones + * (needed for loading maps, but Atlas might want to update existing player data) */ - void LoadPlayerSettings(); + void LoadPlayerSettings(bool newPlayers); /** * Loads the map settings script (called after map is loaded) diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp index de0624f3da..8c4b9a4ba6 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp @@ -729,6 +729,9 @@ void ScenarioEditor::SetOpenFilename(const wxString& filename) void ScenarioEditor::NotifyOnMapReload() { m_SectionLayout.OnMapReload(); + + // Notify observers, here so it's independent of individual panels + m_MapSettings.NotifyObservers(); } ////////////////////////////////////////////////////////////////////////// diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp index 1550415986..5f4cd08219 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp @@ -145,13 +145,14 @@ void MapSettingsControl::CreateWidgets() gameTypes.Add(_T("endless")); wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 5, 5); + gridSizer->AddGrowableCol(1); gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Reveal map")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(new wxCheckBox(this, ID_MapReveal, wxEmptyString)); gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Game type")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); - gridSizer->Add(new wxChoice(this, ID_MapType, wxDefaultPosition, wxDefaultSize, gameTypes)); + gridSizer->Add(new wxChoice(this, ID_MapType, wxDefaultPosition, wxDefaultSize, gameTypes), wxSizerFlags().Expand()); gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Lock teams")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(new wxCheckBox(this, ID_MapTeams, wxEmptyString)); - sizer->Add(gridSizer); + sizer->Add(gridSizer, wxSizerFlags().Expand()); sizer->AddSpacer(5); @@ -404,6 +405,9 @@ void MapSidebar::OnSimPlay(wxCommandEvent& event) if (m_SimState == SimInactive) { + // Force update of player settings + POST_MESSAGE(LoadPlayerSettings, (false)); + POST_MESSAGE(SimStateSave, (L"default")); POST_MESSAGE(GuiSwitchPage, (L"page_session.xml")); POST_MESSAGE(SimPlay, (speed)); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp index 430eb00592..eae5744e52 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp @@ -101,23 +101,23 @@ public: gridSizer->AddGrowableCol(1); gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Food")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); wxSpinCtrl* foodCtrl = new wxSpinCtrl(this, ID_PlayerFood, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, 0, INT_MAX); - gridSizer->Add(Tooltipped(foodCtrl, _("Initial value of food resource"))); + gridSizer->Add(Tooltipped(foodCtrl, _("Initial value of food resource")), wxSizerFlags().Expand()); m_Controls.food = foodCtrl; gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Wood")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); wxSpinCtrl* woodCtrl = new wxSpinCtrl(this, ID_PlayerWood, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, 0, INT_MAX); - gridSizer->Add(Tooltipped(woodCtrl, _("Initial value of wood resource"))); + gridSizer->Add(Tooltipped(woodCtrl, _("Initial value of wood resource")), wxSizerFlags().Expand()); m_Controls.wood = woodCtrl; gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Metal")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); wxSpinCtrl* metalCtrl = new wxSpinCtrl(this, ID_PlayerMetal, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, 0, INT_MAX); - gridSizer->Add(Tooltipped(metalCtrl, _("Initial value of metal resource"))); + gridSizer->Add(Tooltipped(metalCtrl, _("Initial value of metal resource")), wxSizerFlags().Expand()); m_Controls.metal = metalCtrl; gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Stone")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); wxSpinCtrl* stoneCtrl = new wxSpinCtrl(this, ID_PlayerStone, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, 0, INT_MAX); - gridSizer->Add(Tooltipped(stoneCtrl, _("Initial value of stone resource"))); + gridSizer->Add(Tooltipped(stoneCtrl, _("Initial value of stone resource")), wxSizerFlags().Expand()); m_Controls.stone = stoneCtrl; gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Pop limit")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); wxSpinCtrl* popCtrl = new wxSpinCtrl(this, ID_PlayerPop, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, 0, INT_MAX); - gridSizer->Add(Tooltipped(popCtrl, _("Population limit for this player"))); + gridSizer->Add(Tooltipped(popCtrl, _("Population limit for this player")), wxSizerFlags().Expand()); m_Controls.pop = popCtrl; resourceSizer->Add(gridSizer, wxSizerFlags(1).Expand()); @@ -285,31 +285,36 @@ public: void ResizePlayers(size_t numPlayers) { + wxASSERT(numPlayers <= m_Pages.size()); + // We don't really want to destroy the windows corresponding - // to the tabs, so we've kept them in a vector and will - // only remove and add them to the notebook as needed - if (numPlayers <= m_Pages.size()) + // to the tabs, so we've kept them in a vector and will + // only remove and add them to the notebook as needed + int selection = GetSelection(); + size_t pageCount = GetPageCount(); + + if (numPlayers > pageCount) { - size_t pageCount = GetPageCount(); - if (numPlayers > pageCount) + // Add previously removed pages + for (size_t i = pageCount; i < numPlayers; ++i) { - // Add previously removed pages - for (size_t i = pageCount; i < numPlayers; ++i) - { - AddPage(m_Pages[i], m_Pages[i]->GetPlayerName()); - } - } - else - { - // Remove previously added pages - // we have to manually hide them or they remain visible - for (size_t i = pageCount - 1; i >= numPlayers; --i) - { - m_Pages[i]->Hide(); - RemovePage(i); - } + AddPage(m_Pages[i], m_Pages[i]->GetPlayerName()); } } + else + { + // Remove previously added pages + // we have to manually hide them or they remain visible + for (size_t i = pageCount - 1; i >= numPlayers; --i) + { + m_Pages[i]->Hide(); + RemovePage(i); + } + } + + // Workaround for bug on wxGTK 2.8: wxChoice selection doesn't update + // (in fact it loses its selection when adding/removing pages) + GetChoiceCtrl()->SetSelection(selection); } protected: @@ -363,18 +368,85 @@ private: } } - void OnNumPlayersChanged(wxSpinEvent& WXUNUSED(evt)) + void OnPlayerColour(wxCommandEvent& WXUNUSED(evt)) { if (!m_InGUIUpdate) { - m_Players->ResizePlayers(wxDynamicCast(FindWindow(ID_NumPlayers), wxSpinCtrl)->GetValue()); SendToEngine(); + + // Update player settings, to show new colour + POST_MESSAGE(LoadPlayerSettings, (false)); + } + } + + void OnNumPlayersText(wxCommandEvent& WXUNUSED(evt)) + { // Ignore because it will also trigger EVT_SPINCTRL + // and we don't want to handle the same event twice + } + + void OnNumPlayersSpin(wxSpinEvent& evt) + { + if (!m_InGUIUpdate) + { + wxASSERT(evt.GetInt() > 0); - // Notify observers + // When wxMessageBox pops up, wxSpinCtrl loses focus, which + // forces another EVT_SPINCTRL event, which we don't want + // to handle, so we check here for a change + if (evt.GetInt() == (int)m_NumPlayers) + { + return; // No change + } + + size_t oldNumPlayers = m_NumPlayers; + m_NumPlayers = evt.GetInt(); + + if (m_NumPlayers < oldNumPlayers) + { + // Remove players, but check if they own any entities + bool notified = false; + for (size_t i = oldNumPlayers; i > m_NumPlayers; --i) + { + qGetPlayerObjects objectsQry(i); + objectsQry.Post(); + + std::vector ids = *objectsQry.ids; + + if (ids.size() > 0) + { + if (!notified) + { + // TODO: Add option to reassign objects? + if (wxMessageBox(_("WARNING: All objects belonging to the removed players will be deleted. Continue anyway?"), _("Remove player confirmation"), wxICON_EXCLAMATION | wxYES_NO) != wxYES) + { + // Restore previous player count + m_NumPlayers = oldNumPlayers; + wxDynamicCast(FindWindow(ID_NumPlayers), wxSpinCtrl)->SetValue(m_NumPlayers); + return; + } + + notified = true; + } + + // Delete objects + // TODO: Merge multiple commands? + POST_COMMAND(DeleteObjects, (ids)); + } + } + } + + m_Players->ResizePlayers(m_NumPlayers); + SendToEngine(); + + // Reload players, notify observers + POST_MESSAGE(LoadPlayerSettings, (true)); m_MapSettings.NotifyObservers(); } } + // TODO: we shouldn't hardcode this, but instead dynamically create + // new player notebook pages on demand; of course the default data + // will be limited by the entries in player_defaults.json static const size_t MAX_NUM_PLAYERS = 8; bool m_InGUIUpdate; @@ -383,17 +455,19 @@ private: ScenarioEditor& m_ScenarioEditor; std::vector m_PlayerControls; Observable& m_MapSettings; + size_t m_NumPlayers; DECLARE_EVENT_TABLE(); }; BEGIN_EVENT_TABLE(PlayerSettingsControl, wxPanel) - EVT_BUTTON(ID_PlayerColour, PlayerSettingsControl::OnEdit) + EVT_BUTTON(ID_PlayerColour, PlayerSettingsControl::OnPlayerColour) EVT_BUTTON(ID_CameraSet, PlayerSettingsControl::OnEdit) EVT_BUTTON(ID_CameraClear, PlayerSettingsControl::OnEdit) EVT_CHOICE(wxID_ANY, PlayerSettingsControl::OnEdit) + EVT_TEXT(ID_NumPlayers, PlayerSettingsControl::OnNumPlayersText) EVT_TEXT(wxID_ANY, PlayerSettingsControl::OnEdit) - EVT_SPINCTRL(ID_NumPlayers, PlayerSettingsControl::OnNumPlayersChanged) + EVT_SPINCTRL(ID_NumPlayers, PlayerSettingsControl::OnNumPlayersSpin) EVT_SPINCTRL(ID_PlayerFood, PlayerSettingsControl::OnEditSpin) EVT_SPINCTRL(ID_PlayerWood, PlayerSettingsControl::OnEditSpin) EVT_SPINCTRL(ID_PlayerMetal, PlayerSettingsControl::OnEditSpin) @@ -402,7 +476,7 @@ BEGIN_EVENT_TABLE(PlayerSettingsControl, wxPanel) END_EVENT_TABLE(); PlayerSettingsControl::PlayerSettingsControl(wxWindow* parent, ScenarioEditor& scenarioEditor) - : wxPanel(parent, wxID_ANY), m_ScenarioEditor(scenarioEditor), m_InGUIUpdate(false), m_MapSettings(scenarioEditor.GetMapSettings()) + : wxPanel(parent, wxID_ANY), m_ScenarioEditor(scenarioEditor), m_InGUIUpdate(false), m_MapSettings(scenarioEditor.GetMapSettings()), m_NumPlayers(0) { // To prevent recursion, don't handle GUI events right now m_InGUIUpdate = true; @@ -502,13 +576,17 @@ void PlayerSettingsControl::ReadFromEngine() } AtIter player = m_MapSettings["PlayerData"]["item"]; - size_t numPlayers = player.count(); - - if (!m_MapSettings.defined() || !player.defined() || numPlayers == 0) + if (!m_MapSettings.defined() || !player.defined() || player.count() == 0) { - // Player data missing - set number of players manually - numPlayers = MAX_NUM_PLAYERS; + // Player data missing - set number of players to max + m_NumPlayers = MAX_NUM_PLAYERS; } + else + { + m_NumPlayers = player.count(); + } + + wxASSERT(m_NumPlayers <= MAX_NUM_PLAYERS && m_NumPlayers != 0); // To prevent recursion, don't handle GUI events right now m_InGUIUpdate = true; @@ -517,12 +595,12 @@ void PlayerSettingsControl::ReadFromEngine() AtIter playerDefs = m_PlayerDefaults["item"]; ++playerDefs; // skip gaia - wxDynamicCast(FindWindow(ID_NumPlayers), wxSpinCtrl)->SetValue(numPlayers); + wxDynamicCast(FindWindow(ID_NumPlayers), wxSpinCtrl)->SetValue(m_NumPlayers); // Remove / add extra player pages as needed - m_Players->ResizePlayers(numPlayers); + m_Players->ResizePlayers(m_NumPlayers); - for (size_t i = 0; i < numPlayers && i < MAX_NUM_PLAYERS; ++i, ++player, ++playerDefs) + for (size_t i = 0; i < MAX_NUM_PLAYERS; ++i, ++player, ++playerDefs) { PlayerPageControls controls = m_PlayerControls[i]; @@ -537,7 +615,12 @@ void PlayerSettingsControl::ReadFromEngine() // civ wxChoice* choice = controls.civ; - wxString civCode(player["Civ"]); + wxString civCode; + if (player["Civ"].defined()) + civCode = wxString(player["Civ"]); + else + civCode = wxString(playerDefs["Civ"]); + for (size_t j = 0; j < choice->GetCount(); ++j) { wxStringClientData* str = dynamic_cast(choice->GetClientObject(j)); @@ -651,11 +734,12 @@ AtObj PlayerSettingsControl::UpdateSettingsObject() { // Update player data in the map settings AtIter oldPlayer = m_MapSettings["PlayerData"]["item"]; - size_t numPlayers = wxDynamicCast(FindWindow(ID_NumPlayers), wxSpinCtrl)->GetValue(); AtObj players; players.set("@array", L""); - for (size_t i = 0; i < numPlayers && i < MAX_NUM_PLAYERS; ++i) + wxASSERT(m_NumPlayers <= MAX_NUM_PLAYERS); + + for (size_t i = 0; i < m_NumPlayers; ++i) { PlayerPageControls controls = m_PlayerControls[i]; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp index 7e45358d7a..1353c3753c 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp @@ -94,8 +94,9 @@ TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebar wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Visualise")); m_MainSizer->Add(sizer, wxSizerFlags().Expand().Border(wxTOP, 10)); - wxSizer* visSizer = new wxFlexGridSizer(2, 5, 5); - sizer->Add(visSizer); + wxFlexGridSizer* visSizer = new wxFlexGridSizer(2, 5, 5); + visSizer->AddGrowableCol(1); + sizer->Add(visSizer, wxSizerFlags().Expand()); wxArrayString defaultChoices; defaultChoices.Add(_("(none)")); @@ -103,7 +104,7 @@ TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebar m_PassabilityChoice->SetSelection(0); visSizer->Add(new wxStaticText(this, wxID_ANY, _("Passability")), wxSizerFlags().Align(wxALIGN_CENTER|wxALIGN_RIGHT)); - visSizer->Add(m_PassabilityChoice); + visSizer->Add(m_PassabilityChoice, wxSizerFlags().Expand()); visSizer->Add(new wxStaticText(this, wxID_ANY, _("Priorities")), wxSizerFlags().Align(wxALIGN_CENTER|wxALIGN_RIGHT)); visSizer->Add(new wxCheckBox(this, ID_ShowPriorities, _(""))); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp index 8246e168c8..b417105229 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp @@ -216,11 +216,12 @@ void Brush::CreateUI(wxWindow* parent, wxSizer* sizer) sizer->AddSpacer(5); // TODO: These are yucky - wxSizer* spinnerSizer = new wxFlexGridSizer(2, 2, 5, 5); + wxFlexGridSizer* spinnerSizer = new wxFlexGridSizer(2, 5, 5); + spinnerSizer->AddGrowableCol(1); spinnerSizer->Add(new wxStaticText(parent, wxID_ANY, _("Size")), wxSizerFlags().Align(wxALIGN_CENTER|wxALIGN_RIGHT)); - spinnerSizer->Add(new BrushSizeCtrl(parent, *this)); + spinnerSizer->Add(new BrushSizeCtrl(parent, *this), wxSizerFlags().Expand()); spinnerSizer->Add(new wxStaticText(parent, wxID_ANY, _("Strength")), wxSizerFlags().Align(wxALIGN_CENTER|wxALIGN_RIGHT)); - spinnerSizer->Add(new BrushStrengthCtrl(parent, *this)); + spinnerSizer->Add(new BrushStrengthCtrl(parent, *this), wxSizerFlags().Expand()); sizer->Add(spinnerSizer, wxSizerFlags().Expand()); } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp index 48df838ed3..e33236345d 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp @@ -99,8 +99,7 @@ public: { if (type == KEY_CHAR && evt.GetKeyCode() == WXK_DELETE) { - for (size_t i = 0; i < g_SelectedObjects.size(); ++i) - POST_COMMAND(DeleteObject, (g_SelectedObjects[i])); + POST_COMMAND(DeleteObjects, (g_SelectedObjects)); g_SelectedObjects.clear(); g_SelectedObjects.NotifyObservers(); diff --git a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp index ddb1e9567e..89318cb8b7 100644 --- a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp @@ -139,31 +139,52 @@ QUERYHANDLER(GetMapSettings) msg->settings = g_Game->GetSimulation2()->GetMapSettingsString(); } -QUERYHANDLER(GetMapSizes) -{ - msg->sizes = g_Game->GetSimulation2()->GetMapSizes(); -} - BEGIN_COMMAND(SetMapSettings) { - void Do() + std::string m_OldSettings, m_NewSettings; + + void SetSettings(const std::string& settings) { - Redo(); + g_Game->GetSimulation2()->SetMapSettings(settings); } + void Do() + { + m_OldSettings = g_Game->GetSimulation2()->GetMapSettingsString(); + m_NewSettings = *msg->settings; + + SetSettings(m_NewSettings); + } + + // TODO: we need some way to notify the Atlas UI when the settings are changed + // externally, otherwise this will have no visible effect void Undo() { - // TODO - debug_warn(L"Can't undo SetMapSettings"); + // SetSettings(m_OldSettings); } void Redo() { - g_Game->GetSimulation2()->SetMapSettings(*msg->settings); + // SetSettings(m_NewSettings); + } + + void MergeIntoPrevious(cSetMapSettings* prev) + { + prev->m_NewSettings = m_NewSettings; } }; END_COMMAND(SetMapSettings) +MESSAGEHANDLER(LoadPlayerSettings) +{ + g_Game->GetSimulation2()->LoadPlayerSettings(msg->newplayers); +} + +QUERYHANDLER(GetMapSizes) +{ + msg->sizes = g_Game->GetSimulation2()->GetMapSizes(); +} + QUERYHANDLER(GetRMSData) { msg->data = g_Game->GetSimulation2()->GetRMSData(); @@ -171,6 +192,8 @@ QUERYHANDLER(GetRMSData) BEGIN_COMMAND(ResizeMap) { + int m_OldTiles, m_NewTiles; + cResizeMap() { } @@ -186,24 +209,39 @@ BEGIN_COMMAND(ResizeMap) g_Game->GetView()->GetLOSTexture().MakeDirty(); } + void ResizeTerrain(int tiles) + { + CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); + + terrain->Resize(tiles / PATCH_SIZE); + + MakeDirty(); + } + void Do() { - Redo(); + CmpPtr cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY); + if (cmpTerrain.null()) + { + m_OldTiles = m_NewTiles = 0; + } + else + { + m_OldTiles = (int)cmpTerrain->GetTilesPerSide(); + m_NewTiles = msg->tiles; + } + + ResizeTerrain(m_NewTiles); } void Undo() { - // TODO - debug_warn(L"Can't undo ResizeMap"); + ResizeTerrain(m_OldTiles); } void Redo() { - CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); - - terrain->Resize(msg->tiles / PATCH_SIZE); - - MakeDirty(); + ResizeTerrain(m_NewTiles); } }; END_COMMAND(ResizeMap) diff --git a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp index cc64ccbb6d..4ef2cf6ab9 100644 --- a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2010 Wildfire Games. +/* Copyright (C) 2011 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -543,18 +543,21 @@ BEGIN_COMMAND(RotateObject) END_COMMAND(RotateObject) -BEGIN_COMMAND(DeleteObject) +BEGIN_COMMAND(DeleteObjects) { // Saved copy of the important aspects of a unit, to allow undo - entity_id_t m_EntityID; - CStr m_TemplateName; - int32_t m_Owner; - CFixedVector3D m_Pos; - CFixedVector3D m_Rot; - // TODO: random selections + struct OldObject + { + entity_id_t entityID; + CStr templateName; + int32_t owner; + CFixedVector3D pos; + CFixedVector3D rot; + }; - cDeleteObject() - : m_EntityID(INVALID_ENTITY), m_Owner(-1) + std::vector oldObjects; + + cDeleteObjects() { } @@ -569,46 +572,77 @@ BEGIN_COMMAND(DeleteObject) CmpPtr cmpTemplateManager(sim, SYSTEM_ENTITY); ENSURE(!cmpTemplateManager.null()); - m_EntityID = (entity_id_t)msg->id; - m_TemplateName = cmpTemplateManager->GetCurrentTemplateName(m_EntityID); - - CmpPtr cmpOwner(sim, m_EntityID); - if (!cmpOwner.null()) - m_Owner = cmpOwner->GetOwner(); - - CmpPtr cmpPosition(sim, m_EntityID); - if (!cmpPosition.null()) + std::vector ids = *msg->ids; + for (size_t i = 0; i < ids.size(); ++i) { - m_Pos = cmpPosition->GetPosition(); - m_Rot = cmpPosition->GetRotation(); - } + OldObject obj; - g_Game->GetSimulation2()->DestroyEntity(m_EntityID); + obj.entityID = (entity_id_t)ids[i]; + obj.templateName = cmpTemplateManager->GetCurrentTemplateName(obj.entityID); + + CmpPtr cmpOwner(sim, obj.entityID); + if (!cmpOwner.null()) + obj.owner = cmpOwner->GetOwner(); + + CmpPtr cmpPosition(sim, obj.entityID); + if (!cmpPosition.null()) + { + obj.pos = cmpPosition->GetPosition(); + obj.rot = cmpPosition->GetRotation(); + } + + oldObjects.push_back(obj); + g_Game->GetSimulation2()->DestroyEntity(obj.entityID); + } } void Undo() { CSimulation2& sim = *g_Game->GetSimulation2(); - entity_id_t ent = sim.AddEntity(m_TemplateName.FromUTF8(), m_EntityID); - if (ent == INVALID_ENTITY) - LOGERROR(L"Failed to load entity template '%hs'", m_TemplateName.c_str()); - else - { - CmpPtr cmpPosition(sim, m_EntityID); - if (!cmpPosition.null()) - { - cmpPosition->JumpTo(m_Pos.X, m_Pos.Z); - cmpPosition->SetXZRotation(m_Rot.X, m_Rot.Z); - cmpPosition->SetYRotation(m_Rot.Y); - } - CmpPtr cmpOwner(sim, m_EntityID); - if (!cmpOwner.null()) - cmpOwner->SetOwner(m_Owner); + for (size_t i = 0; i < oldObjects.size(); ++i) + { + entity_id_t ent = sim.AddEntity(oldObjects[i].templateName.FromUTF8(), oldObjects[i].entityID); + if (ent == INVALID_ENTITY) + { + LOGERROR(L"Failed to load entity template '%hs'", oldObjects[i].templateName.c_str()); + } + else + { + CmpPtr cmpPosition(sim, oldObjects[i].entityID); + if (!cmpPosition.null()) + { + cmpPosition->JumpTo(oldObjects[i].pos.X, oldObjects[i].pos.Z); + cmpPosition->SetXZRotation(oldObjects[i].rot.X, oldObjects[i].rot.Z); + cmpPosition->SetYRotation(oldObjects[i].rot.Y); + } + + CmpPtr cmpOwner(sim, oldObjects[i].entityID); + if (!cmpOwner.null()) + cmpOwner->SetOwner(oldObjects[i].owner); + } } + + oldObjects.clear(); } }; -END_COMMAND(DeleteObject) +END_COMMAND(DeleteObjects) +QUERYHANDLER(GetPlayerObjects) +{ + std::vector ids; + player_id_t playerID = msg->player; + + const CSimulation2::InterfaceListUnordered& cmps = g_Game->GetSimulation2()->GetEntitiesWithInterfaceUnordered(IID_Ownership); + for (CSimulation2::InterfaceListUnordered::const_iterator eit = cmps.begin(); eit != cmps.end(); ++eit) + { + if (static_cast(eit->second)->GetOwner() == playerID) + { + ids.push_back(eit->first); + } + } + + msg->ids = ids; +} } diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index 24ed24f8b6..1c44f3cd15 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -152,15 +152,19 @@ QUERY(GetMapSettings, ((std::string, settings)) ); +COMMAND(SetMapSettings, MERGE, + ((std::string, settings)) + ); + +MESSAGE(LoadPlayerSettings, + ((bool, newplayers)) + ); + QUERY(GetMapSizes, , ((std::string, sizes)) ); -COMMAND(SetMapSettings, NOMERGE, - ((std::string, settings)) - ); - QUERY(GetRMSData, , ((std::vector, data)) @@ -418,7 +422,7 @@ QUERY(GetEnvironmentSettings, ((sEnvironmentSettings, settings)) ); -COMMAND(SetEnvironmentSettings, MERGE, +COMMAND(SetEnvironmentSettings, MERGE, // merge lots of small changes into one undoable command ((sEnvironmentSettings, settings)) ); @@ -486,8 +490,8 @@ COMMAND(RotateObject, MERGE, ((float, angle)) ); -COMMAND(DeleteObject, NOMERGE, - ((ObjectID, id)) +COMMAND(DeleteObjects, NOMERGE, + ((std::vector, ids)) ); MESSAGE(SetSelectionPreview, @@ -507,6 +511,12 @@ COMMAND(SetObjectSettings, NOMERGE, ((sObjectSettings, settings)) ); +QUERY(GetPlayerObjects, + ((int, player)) + , + ((std::vector, ids)) + ); + ////////////////////////////////////////////////////////////////////////// QUERY(GetCinemaPaths,