Fixes Atlas player panel getting out of sync with simulation. Fixes #927.

Fixes object panel not being notified of map loading.
Fixes bug where opening a new map before using the player panel
prevented default player data being displayed for new players.
Fixes wxGTK 2.8 bug: wxChoicebook control doesn't update the choice
control when adding/removing pages.
Notifies player that deleting player in Atlas will delete all their
objects (and gives them the option).
Changes DeleteObject to DeleteObjects to support multiple selections.
Implements undo for map resize (experimental).
Removes annoying debug message from attempted undo of map settings.
Tweaks a few Atlas UI controls.

This was SVN commit r10064.
This commit is contained in:
historic_bruno 2011-08-22 21:45:39 +00:00
parent a4dc290c23
commit 27e5581d27
14 changed files with 357 additions and 157 deletions

View File

@ -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);

View File

@ -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))

View File

@ -1029,7 +1029,7 @@ int CMapReader::LoadScriptSettings()
// load player settings script
int CMapReader::LoadPlayerSettings()
{
pSimulation2->LoadPlayerSettings();
pSimulation2->LoadPlayerSettings(true);
return 0;
}

View File

@ -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()

View File

@ -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)

View File

@ -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();
}
//////////////////////////////////////////////////////////////////////////

View File

@ -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));

View File

@ -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<AtlasMessage::ObjectID> 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<PlayerPageControls> m_PlayerControls;
Observable<AtObj>& 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<wxStringClientData*>(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];

View File

@ -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, _("")));

View File

@ -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());
}

View File

@ -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();

View File

@ -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<ICmpTerrain> 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)

View File

@ -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<OldObject> oldObjects;
cDeleteObjects()
{
}
@ -569,46 +572,77 @@ BEGIN_COMMAND(DeleteObject)
CmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY);
ENSURE(!cmpTemplateManager.null());
m_EntityID = (entity_id_t)msg->id;
m_TemplateName = cmpTemplateManager->GetCurrentTemplateName(m_EntityID);
CmpPtr<ICmpOwnership> cmpOwner(sim, m_EntityID);
if (!cmpOwner.null())
m_Owner = cmpOwner->GetOwner();
CmpPtr<ICmpPosition> cmpPosition(sim, m_EntityID);
if (!cmpPosition.null())
std::vector<ObjectID> 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<ICmpOwnership> cmpOwner(sim, obj.entityID);
if (!cmpOwner.null())
obj.owner = cmpOwner->GetOwner();
CmpPtr<ICmpPosition> 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<ICmpPosition> 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<ICmpOwnership> 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<ICmpPosition> 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<ICmpOwnership> 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<ObjectID> 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<ICmpOwnership*>(eit->second)->GetOwner() == playerID)
{
ids.push_back(eit->first);
}
}
msg->ids = ids;
}
}

View File

@ -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<std::string>, 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<ObjectID>, ids))
);
MESSAGE(SetSelectionPreview,
@ -507,6 +511,12 @@ COMMAND(SetObjectSettings, NOMERGE,
((sObjectSettings, settings))
);
QUERY(GetPlayerObjects,
((int, player))
,
((std::vector<ObjectID>, ids))
);
//////////////////////////////////////////////////////////////////////////
QUERY(GetCinemaPaths,