1 /* Copyright (C) 2018 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "Map.h"
21 
22 #include "AtlasObject/AtlasObject.h"
23 #include "GameInterface/Messages.h"
24 #include "ScenarioEditor/ScenarioEditor.h"
25 #include "ScenarioEditor/Tools/Common/Tools.h"
26 
27 #include "wx/busyinfo.h"
28 #include "wx/filename.h"
29 
30 #define CREATE_CHECKBOX(window, parentSizer, name, description, ID) \
31 	parentSizer->Add(new wxStaticText(window, wxID_ANY, _(name)), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); \
32 	parentSizer->Add(Tooltipped(new wxCheckBox(window, ID, wxEmptyString), _(description)));
33 
34 enum
35 {
36 	ID_MapName,
37 	ID_MapDescription,
38 	ID_MapReveal,
39 	ID_MapType,
40 	ID_MapPreview,
41 	ID_MapTeams,
42 	ID_MapKW_Demo,
43 	ID_MapKW_Naval,
44 	ID_VC_Conquest,
45 	ID_VC_ConquestUnits,
46 	ID_VC_ConquestStructures,
47 	ID_VC_CaptureTheRelic,
48 	ID_VC_Wonder,
49 	ID_VC_Regicide,
50 	ID_RandomScript,
51 	ID_RandomSize,
52 	ID_RandomNomad,
53 	ID_RandomSeed,
54 	ID_RandomReseed,
55 	ID_RandomGenerate,
56 	ID_SimPlay,
57 	ID_SimFast,
58 	ID_SimSlow,
59 	ID_SimPause,
60 	ID_SimReset,
61 	ID_OpenPlayerPanel
62 };
63 
64 enum
65 {
66 	SimInactive,
67 	SimPlaying,
68 	SimPlayingFast,
69 	SimPlayingSlow,
70 	SimPaused
71 };
72 
IsPlaying(int s)73 bool IsPlaying(int s) { return (s == SimPlaying || s == SimPlayingFast || s == SimPlayingSlow); }
74 
75 // TODO: Some of these helper things should be moved out of this file
76 // and into shared locations
77 
78 // Helper function for adding tooltips
Tooltipped(wxWindow * window,const wxString & tip)79 static wxWindow* Tooltipped(wxWindow* window, const wxString& tip)
80 {
81 	window->SetToolTip(tip);
82 	return window;
83 }
84 
85 // Helper class for storing AtObjs
86 class AtObjClientData : public wxClientData
87 {
88 public:
AtObjClientData(const AtObj & obj)89 	AtObjClientData(const AtObj& obj) : obj(obj) {}
~AtObjClientData()90 	virtual ~AtObjClientData() {}
GetValue()91 	AtObj GetValue() { return obj; }
92 private:
93 	AtObj obj;
94 };
95 
96 class MapSettingsControl : public wxPanel
97 {
98 public:
99 	MapSettingsControl(wxWindow* parent, ScenarioEditor& scenarioEditor);
100 	void CreateWidgets();
101 	void ReadFromEngine();
102 	void SetMapSettings(const AtObj& obj);
103 	AtObj UpdateSettingsObject();
104 private:
105 	void SendToEngine();
106 	void OnConquestChanged();
107 
OnEdit(wxCommandEvent & evt)108 	void OnEdit(wxCommandEvent& evt)
109 	{
110 		SendToEngine();
111 		if (evt.GetId() == ID_VC_Conquest)
112 			OnConquestChanged();
113 	}
114 
115 	std::set<std::wstring> m_MapSettingsKeywords;
116 	std::set<std::wstring> m_MapSettingsVictoryConditions;
117 	std::vector<wxChoice*> m_PlayerCivChoices;
118 	Observable<AtObj>& m_MapSettings;
119 
120 	DECLARE_EVENT_TABLE();
121 };
122 
123 BEGIN_EVENT_TABLE(MapSettingsControl, wxPanel)
124 	EVT_TEXT(ID_MapName, MapSettingsControl::OnEdit)
125 	EVT_TEXT(ID_MapDescription, MapSettingsControl::OnEdit)
126 	EVT_TEXT(ID_MapPreview, MapSettingsControl::OnEdit)
127 	EVT_CHECKBOX(wxID_ANY, MapSettingsControl::OnEdit)
128 	EVT_CHOICE(wxID_ANY, MapSettingsControl::OnEdit)
129 END_EVENT_TABLE();
130 
MapSettingsControl(wxWindow * parent,ScenarioEditor & scenarioEditor)131 MapSettingsControl::MapSettingsControl(wxWindow* parent, ScenarioEditor& scenarioEditor)
132 	: wxPanel(parent, wxID_ANY), m_MapSettings(scenarioEditor.GetMapSettings())
133 {
134 	wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Map settings"));
135 	SetSizer(sizer);
136 }
137 
CreateWidgets()138 void MapSettingsControl::CreateWidgets()
139 {
140 	wxSizer* sizer = GetSizer();
141 
142 	/////////////////////////////////////////////////////////////////////////
143 	// Map settings
144 	wxBoxSizer* nameSizer = new wxBoxSizer(wxHORIZONTAL);
145 	nameSizer->Add(new wxStaticText(this, wxID_ANY, _("Name")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
146 	nameSizer->Add(8, 0);
147 	nameSizer->Add(Tooltipped(new wxTextCtrl(this, ID_MapName),
148 			_("Displayed name of the map")), wxSizerFlags().Proportion(1));
149 	sizer->Add(nameSizer, wxSizerFlags().Expand());
150 
151 	sizer->Add(0, 2);
152 
153 	sizer->Add(new wxStaticText(this, wxID_ANY, _("Description")));
154 	sizer->Add(Tooltipped(new wxTextCtrl(this, ID_MapDescription, wxEmptyString, wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE),
155 			_("Short description used on the map selection screen")), wxSizerFlags().Expand());
156 
157 	sizer->AddSpacer(5);
158 
159 	wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 5, 5);
160 	gridSizer->AddGrowableCol(1);
161 
162 	// TODO: have preview selector tool?
163 	gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Preview")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
164 	gridSizer->Add(Tooltipped(new wxTextCtrl(this, ID_MapPreview, wxEmptyString),
165 		_("Texture used for map preview")), wxSizerFlags().Expand());
166 	CREATE_CHECKBOX(this, gridSizer, "Reveal map", "If checked, players won't need to explore", ID_MapReveal);
167 	CREATE_CHECKBOX(this, gridSizer, "Lock teams", "If checked, teams will be locked", ID_MapTeams);
168 	sizer->Add(gridSizer, wxSizerFlags().Expand());
169 
170 	sizer->AddSpacer(5);
171 
172 	// TODO: replace by names in binaries/data/mods/public/simulation/data/settings/victory_conditions/
173 	wxStaticBoxSizer* victoryConditionSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Victory Conditions"));
174 	wxFlexGridSizer* vcGridSizer = new wxFlexGridSizer(2, 0, 5);
175 	vcGridSizer->AddGrowableCol(1);
176 	CREATE_CHECKBOX(this, vcGridSizer, "Conquest", "Select Conquest victory condition", ID_VC_Conquest);
177 	CREATE_CHECKBOX(this, vcGridSizer, "Conquest Units", "Select Conquest Units victory condition", ID_VC_ConquestUnits);
178 	CREATE_CHECKBOX(this, vcGridSizer, "Conquest Structures", "Select Conquest Structures victory condition", ID_VC_ConquestStructures);
179 	CREATE_CHECKBOX(this, vcGridSizer, "Capture the Relic", "Select Capture the Relic victory condition", ID_VC_CaptureTheRelic);
180 	CREATE_CHECKBOX(this, vcGridSizer, "Wonder", "Select Wonder victory condition", ID_VC_Wonder);
181 	CREATE_CHECKBOX(this, vcGridSizer, "Regicide", "Select Regicide victory condition", ID_VC_Regicide);
182 	victoryConditionSizer->Add(vcGridSizer);
183 	sizer->Add(victoryConditionSizer, wxSizerFlags().Expand());
184 
185 	sizer->AddSpacer(5);
186 
187 	wxStaticBoxSizer* keywordsSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Keywords"));
188 	wxFlexGridSizer* kwGridSizer = new wxFlexGridSizer(4, 5, 5);
189 	CREATE_CHECKBOX(this, kwGridSizer, "Demo", "If checked, map will only be visible using filters in game setup", ID_MapKW_Demo);
190 	CREATE_CHECKBOX(this, kwGridSizer, "Naval", "If checked, map will only be visible using filters in game setup", ID_MapKW_Naval);
191 	keywordsSizer->Add(kwGridSizer);
192 	sizer->Add(keywordsSizer, wxSizerFlags().Expand());
193 }
194 
ReadFromEngine()195 void MapSettingsControl::ReadFromEngine()
196 {
197 	AtlasMessage::qGetMapSettings qry;
198 	qry.Post();
199 	if (!(*qry.settings).empty())
200 	{
201 		// Prevent error if there's no map settings to parse
202 		m_MapSettings = AtlasObject::LoadFromJSON(*qry.settings);
203 	}
204 
205 	// map name
206 	wxDynamicCast(FindWindow(ID_MapName), wxTextCtrl)->ChangeValue(wxString(m_MapSettings["Name"]));
207 
208 	// map description
209 	wxDynamicCast(FindWindow(ID_MapDescription), wxTextCtrl)->ChangeValue(wxString(m_MapSettings["Description"]));
210 
211 	// map preview
212 	wxDynamicCast(FindWindow(ID_MapPreview), wxTextCtrl)->ChangeValue(wxString(m_MapSettings["Preview"]));
213 
214 	// reveal map
215 	wxDynamicCast(FindWindow(ID_MapReveal), wxCheckBox)->SetValue(wxString(m_MapSettings["RevealMap"]) == L"true");
216 
217 	// victory conditions
218 	m_MapSettingsVictoryConditions.clear();
219 	for (AtIter victoryCondition = m_MapSettings["VictoryConditions"]["item"]; victoryCondition.defined(); ++victoryCondition)
220 		m_MapSettingsVictoryConditions.insert(std::wstring(victoryCondition));
221 
222 	wxWindow* window;
223 #define INIT_CHECKBOX(ID, mapSettings, value) \
224 	window = FindWindow(ID); \
225 	if (window != nullptr) \
226 		wxDynamicCast(window, wxCheckBox)->SetValue(mapSettings.count(value) != 0);
227 
228 	INIT_CHECKBOX(ID_VC_Conquest, m_MapSettingsVictoryConditions, L"conquest");
229 	INIT_CHECKBOX(ID_VC_ConquestUnits, m_MapSettingsVictoryConditions, L"conquest_units");
230 	INIT_CHECKBOX(ID_VC_ConquestStructures, m_MapSettingsVictoryConditions, L"conquest_structures");
231 	INIT_CHECKBOX(ID_VC_CaptureTheRelic, m_MapSettingsVictoryConditions, L"capture_the_relic");
232 	INIT_CHECKBOX(ID_VC_Wonder, m_MapSettingsVictoryConditions, L"wonder");
233 	INIT_CHECKBOX(ID_VC_Regicide, m_MapSettingsVictoryConditions, L"regicide");
234 	OnConquestChanged();
235 
236 	// lock teams
237 	wxDynamicCast(FindWindow(ID_MapTeams), wxCheckBox)->SetValue(wxString(m_MapSettings["LockTeams"]) == L"true");
238 
239 	// keywords
240 	{
241 		m_MapSettingsKeywords.clear();
242 		for (AtIter keyword = m_MapSettings["Keywords"]["item"]; keyword.defined(); ++keyword)
243 			m_MapSettingsKeywords.insert(std::wstring(keyword));
244 
245 		INIT_CHECKBOX(ID_MapKW_Demo, m_MapSettingsKeywords, L"demo");
246 		INIT_CHECKBOX(ID_MapKW_Naval, m_MapSettingsKeywords, L"naval");
247 	}
248 
249 #undef INIT_CHECKBOX
250 }
251 
SetMapSettings(const AtObj & obj)252 void MapSettingsControl::SetMapSettings(const AtObj& obj)
253 {
254 	m_MapSettings = obj;
255 	m_MapSettings.NotifyObservers();
256 
257 	SendToEngine();
258 }
259 
260 // TODO Use the json data for this
OnConquestChanged()261 void MapSettingsControl::OnConquestChanged()
262 {
263 	bool conqestEnabled = wxDynamicCast(FindWindow(ID_VC_Conquest), wxCheckBox)->GetValue();
264 
265 	wxCheckBox* conquestUnitsCheckbox = wxDynamicCast(FindWindow(ID_VC_ConquestUnits), wxCheckBox);
266 	conquestUnitsCheckbox->Enable(!conqestEnabled);
267 	wxCheckBox* conquestStructuresCheckbox = wxDynamicCast(FindWindow(ID_VC_ConquestStructures), wxCheckBox);
268 	conquestStructuresCheckbox->Enable(!conqestEnabled);
269 	if (conqestEnabled)
270 	{
271 		conquestUnitsCheckbox->SetValue(false);
272 		conquestStructuresCheckbox->SetValue(false);
273 	}
274 }
275 
UpdateSettingsObject()276 AtObj MapSettingsControl::UpdateSettingsObject()
277 {
278 	// map name
279 	m_MapSettings.set("Name", wxDynamicCast(FindWindow(ID_MapName), wxTextCtrl)->GetValue());
280 
281 	// map description
282 	m_MapSettings.set("Description", wxDynamicCast(FindWindow(ID_MapDescription), wxTextCtrl)->GetValue());
283 
284 	// map preview
285 	m_MapSettings.set("Preview", wxDynamicCast(FindWindow(ID_MapPreview), wxTextCtrl)->GetValue());
286 
287 	// reveal map
288 	m_MapSettings.setBool("RevealMap", wxDynamicCast(FindWindow(ID_MapReveal), wxCheckBox)->GetValue());
289 
290 	// victory conditions
291 #define INSERT_VICTORY_CONDITION_CHECKBOX(name, ID) \
292 	if (wxDynamicCast(FindWindow(ID), wxCheckBox)->GetValue()) \
293 		m_MapSettingsVictoryConditions.insert(name); \
294 	else \
295 		m_MapSettingsVictoryConditions.erase(name);
296 
297 	INSERT_VICTORY_CONDITION_CHECKBOX(L"conquest", ID_VC_Conquest);
298 	INSERT_VICTORY_CONDITION_CHECKBOX(L"conquest_units", ID_VC_ConquestUnits);
299 	INSERT_VICTORY_CONDITION_CHECKBOX(L"conquest_structures", ID_VC_ConquestStructures);
300 	INSERT_VICTORY_CONDITION_CHECKBOX(L"capture_the_relic", ID_VC_CaptureTheRelic);
301 	INSERT_VICTORY_CONDITION_CHECKBOX(L"wonder", ID_VC_Wonder);
302 	INSERT_VICTORY_CONDITION_CHECKBOX(L"regicide", ID_VC_Regicide);
303 
304 #undef INSERT_VICTORY_CONDITION_CHECKBOX
305 
306 	AtObj victoryConditions;
307 	victoryConditions.set("@array", L"");
308 	for (std::set<std::wstring>::iterator it = m_MapSettingsVictoryConditions.begin(); it != m_MapSettingsVictoryConditions.end(); ++it)
309 		victoryConditions.add("item", it->c_str());
310 	m_MapSettings.set("VictoryConditions", victoryConditions);
311 
312 	// keywords
313 	{
314 		if (wxDynamicCast(FindWindow(ID_MapKW_Demo), wxCheckBox)->GetValue())
315 			m_MapSettingsKeywords.insert(L"demo");
316 		else
317 			m_MapSettingsKeywords.erase(L"demo");
318 
319 		if (wxDynamicCast(FindWindow(ID_MapKW_Naval), wxCheckBox)->GetValue())
320 			m_MapSettingsKeywords.insert(L"naval");
321 		else
322 			m_MapSettingsKeywords.erase(L"naval");
323 
324 		AtObj keywords;
325 		keywords.set("@array", L"");
326 		for (std::set<std::wstring>::iterator it = m_MapSettingsKeywords.begin(); it != m_MapSettingsKeywords.end(); ++it)
327 			keywords.add("item", it->c_str());
328 		m_MapSettings.set("Keywords", keywords);
329 	}
330 
331 	// teams locked
332 	m_MapSettings.setBool("LockTeams", wxDynamicCast(FindWindow(ID_MapTeams), wxCheckBox)->GetValue());
333 
334 	// default AI RNG seed
335 	m_MapSettings.setInt("AISeed", 0);
336 
337 	return m_MapSettings;
338 }
339 
SendToEngine()340 void MapSettingsControl::SendToEngine()
341 {
342 	UpdateSettingsObject();
343 
344 	std::string json = AtlasObject::SaveToJSON(m_MapSettings);
345 
346 	// TODO: would be nice if we supported undo for settings changes
347 
348 	POST_COMMAND(SetMapSettings, (json));
349 }
350 
351 
MapSidebar(ScenarioEditor & scenarioEditor,wxWindow * sidebarContainer,wxWindow * bottomBarContainer)352 MapSidebar::MapSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
353 	: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer), m_SimState(SimInactive)
354 {
355 	wxSizer* scrollSizer = new wxBoxSizer(wxVERTICAL);
356 	wxScrolledWindow* scrolledWindow = new wxScrolledWindow(this);
357 	scrolledWindow->SetScrollRate(10, 10);
358 	scrolledWindow->SetSizer(scrollSizer);
359 	m_MainSizer->Add(scrolledWindow, wxSizerFlags().Expand().Proportion(1));
360 
361 	m_MapSettingsCtrl = new MapSettingsControl(scrolledWindow, m_ScenarioEditor);
362 	scrollSizer->Add(m_MapSettingsCtrl, wxSizerFlags().Expand());
363 
364 	{
365 		/////////////////////////////////////////////////////////////////////////
366 		// Random map settings
367 		wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, scrolledWindow, _("Random map"));
368 		scrollSizer->Add(sizer, wxSizerFlags().Expand());
369 
370 		sizer->Add(new wxChoice(scrolledWindow, ID_RandomScript), wxSizerFlags().Expand());
371 
372 		sizer->AddSpacer(5);
373 
374 		sizer->Add(new wxButton(scrolledWindow, ID_OpenPlayerPanel, _T("Change players")), wxSizerFlags().Expand());
375 
376 		sizer->AddSpacer(5);
377 
378 		wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 5, 5);
379 		gridSizer->AddGrowableCol(1);
380 
381 		wxChoice* sizeChoice = new wxChoice(scrolledWindow, ID_RandomSize);
382 		gridSizer->Add(new wxStaticText(scrolledWindow, wxID_ANY, _("Map size")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
383 		gridSizer->Add(sizeChoice, wxSizerFlags().Expand());
384 
385 		CREATE_CHECKBOX(scrolledWindow, gridSizer, "Nomad", "Place only some units instead of starting bases.", ID_RandomNomad);
386 
387 		gridSizer->Add(new wxStaticText(scrolledWindow, wxID_ANY, _("Random seed")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
388 		wxBoxSizer* seedSizer = new wxBoxSizer(wxHORIZONTAL);
389 		seedSizer->Add(Tooltipped(new wxTextCtrl(scrolledWindow, ID_RandomSeed, _T("0"), wxDefaultPosition, wxDefaultSize, 0, wxTextValidator(wxFILTER_NUMERIC)),
390 			_("Seed value for random map")), wxSizerFlags(1).Expand());
391 		seedSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_RandomReseed, _("R"), wxDefaultPosition, wxSize(40, -1)),
392 			_("New random seed")));
393 		gridSizer->Add(seedSizer, wxSizerFlags().Expand());
394 
395 		sizer->Add(gridSizer, wxSizerFlags().Expand());
396 
397 		sizer->AddSpacer(5);
398 
399 		sizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_RandomGenerate, _("Generate map")),
400 			_("Run selected random map script")), wxSizerFlags().Expand());
401 	}
402 
403 	{
404 		/////////////////////////////////////////////////////////////////////////
405 		// Simulation buttons
406 		wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, scrolledWindow, _("Simulation test"));
407 		scrollSizer->Add(sizer, wxSizerFlags().Expand().Border(wxTOP, 8));
408 
409 		wxGridSizer* gridSizer = new wxGridSizer(5);
410 		gridSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_SimPlay, _("Play"), wxDefaultPosition, wxSize(48, -1)),
411 			_("Run the simulation at normal speed")), wxSizerFlags().Expand());
412 		gridSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_SimFast, _("Fast"), wxDefaultPosition, wxSize(48, -1)),
413 			_("Run the simulation at 8x speed")), wxSizerFlags().Expand());
414 		gridSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_SimSlow, _("Slow"), wxDefaultPosition, wxSize(48, -1)),
415 			_("Run the simulation at 1/8x speed")), wxSizerFlags().Expand());
416 		gridSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_SimPause, _("Pause"), wxDefaultPosition, wxSize(48, -1)),
417 			_("Pause the simulation")), wxSizerFlags().Expand());
418 		gridSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_SimReset, _("Reset"), wxDefaultPosition, wxSize(48, -1)),
419 			_("Reset the editor to initial state")), wxSizerFlags().Expand());
420 		sizer->Add(gridSizer, wxSizerFlags().Expand());
421 		UpdateSimButtons();
422 	}
423 }
424 
OnCollapse(wxCollapsiblePaneEvent & WXUNUSED (evt))425 void MapSidebar::OnCollapse(wxCollapsiblePaneEvent& WXUNUSED(evt))
426 {
427 	Freeze();
428 
429 	// Toggling the collapsing doesn't seem to update the sidebar layout
430 	// automatically, so do it explicitly here
431 	Layout();
432 
433 	Refresh(); // fixes repaint glitch on Windows
434 
435 	Thaw();
436 }
437 
OnFirstDisplay()438 void MapSidebar::OnFirstDisplay()
439 {
440 	// We do this here becase messages are used which requires simulation to be init'd
441 	m_MapSettingsCtrl->CreateWidgets();
442 	m_MapSettingsCtrl->ReadFromEngine();
443 
444 	// Load the map sizes list
445 	AtlasMessage::qGetMapSizes qrySizes;
446 	qrySizes.Post();
447 	AtObj sizes = AtlasObject::LoadFromJSON(*qrySizes.sizes);
448 	wxChoice* sizeChoice = wxDynamicCast(FindWindow(ID_RandomSize), wxChoice);
449 	for (AtIter s = sizes["Data"]["item"]; s.defined(); ++s)
450 	{
451 		long tiles = 0;
452 		wxString(s["Tiles"]).ToLong(&tiles);
453 		sizeChoice->Append(wxString(s["Name"]), (void*)(intptr_t)tiles);
454 	}
455 	sizeChoice->SetSelection(0);
456 
457 	// Load the RMS script list
458 	AtlasMessage::qGetRMSData qry;
459 	qry.Post();
460 	std::vector<std::string> scripts = *qry.data;
461 	wxChoice* scriptChoice = wxDynamicCast(FindWindow(ID_RandomScript), wxChoice);
462 	scriptChoice->Clear();
463 	for (size_t i = 0; i < scripts.size(); ++i)
464 	{
465 		AtObj data = AtlasObject::LoadFromJSON(scripts[i]);
466 		wxString name(data["settings"]["Name"]);
467 		if (!name.IsEmpty())
468 			scriptChoice->Append(name, new AtObjClientData(*data["settings"]));
469 	}
470 	scriptChoice->SetSelection(0);
471 
472 	Layout();
473 }
474 
OnMapReload()475 void MapSidebar::OnMapReload()
476 {
477 	m_MapSettingsCtrl->ReadFromEngine();
478 
479 	// Reset sim test buttons
480 	POST_MESSAGE(SimPlay, (0.f, false));
481 	POST_MESSAGE(SimStopMusic, ());
482 	POST_MESSAGE(GuiSwitchPage, (L"page_atlas.xml"));
483 	m_SimState = SimInactive;
484 	UpdateSimButtons();
485 }
486 
UpdateSimButtons()487 void MapSidebar::UpdateSimButtons()
488 {
489 	wxButton* button;
490 
491 	button = wxDynamicCast(FindWindow(ID_SimPlay), wxButton);
492 	wxCHECK(button, );
493 	button->Enable(m_SimState != SimPlaying);
494 
495 	button = wxDynamicCast(FindWindow(ID_SimFast), wxButton);
496 	wxCHECK(button, );
497 	button->Enable(m_SimState != SimPlayingFast);
498 
499 	button = wxDynamicCast(FindWindow(ID_SimSlow), wxButton);
500 	wxCHECK(button, );
501 	button->Enable(m_SimState != SimPlayingSlow);
502 
503 	button = wxDynamicCast(FindWindow(ID_SimPause), wxButton);
504 	wxCHECK(button, );
505 	button->Enable(IsPlaying(m_SimState));
506 
507 	button = wxDynamicCast(FindWindow(ID_SimReset), wxButton);
508 	wxCHECK(button, );
509 	button->Enable(m_SimState != SimInactive);
510 }
511 
OnSimPlay(wxCommandEvent & event)512 void MapSidebar::OnSimPlay(wxCommandEvent& event)
513 {
514 	float speed = 1.f;
515 	int newState = SimPlaying;
516 	if (event.GetId() == ID_SimFast)
517 	{
518 		speed = 8.f;
519 		newState = SimPlayingFast;
520 	}
521 	else if (event.GetId() == ID_SimSlow)
522 	{
523 		speed = 0.125f;
524 		newState = SimPlayingSlow;
525 	}
526 
527 	if (m_SimState == SimInactive)
528 	{
529 		// Force update of player settings
530 		POST_MESSAGE(LoadPlayerSettings, (false));
531 
532 		POST_MESSAGE(SimStateSave, (L"default"));
533 		POST_MESSAGE(GuiSwitchPage, (L"page_session.xml"));
534 		POST_MESSAGE(SimPlay, (speed, true));
535 		m_SimState = newState;
536 	}
537 	else // paused or already playing at a different speed
538 	{
539 		POST_MESSAGE(SimPlay, (speed, true));
540 		m_SimState = newState;
541 	}
542 	UpdateSimButtons();
543 }
544 
OnSimPause(wxCommandEvent & WXUNUSED (event))545 void MapSidebar::OnSimPause(wxCommandEvent& WXUNUSED(event))
546 {
547 	if (IsPlaying(m_SimState))
548 	{
549 		POST_MESSAGE(SimPlay, (0.f, true));
550 		m_SimState = SimPaused;
551 	}
552 	UpdateSimButtons();
553 }
554 
OnSimReset(wxCommandEvent & WXUNUSED (event))555 void MapSidebar::OnSimReset(wxCommandEvent& WXUNUSED(event))
556 {
557 	if (IsPlaying(m_SimState))
558 	{
559 		POST_MESSAGE(SimPlay, (0.f, true));
560 		POST_MESSAGE(SimStateRestore, (L"default"));
561 		POST_MESSAGE(SimStopMusic, ());
562 		POST_MESSAGE(SimPlay, (0.f, false));
563 		POST_MESSAGE(GuiSwitchPage, (L"page_atlas.xml"));
564 		m_SimState = SimInactive;
565 	}
566 	else if (m_SimState == SimPaused)
567 	{
568 		POST_MESSAGE(SimPlay, (0.f, true));
569 		POST_MESSAGE(SimStateRestore, (L"default"));
570 		POST_MESSAGE(SimStopMusic, ());
571 		POST_MESSAGE(SimPlay, (0.f, false));
572 		POST_MESSAGE(GuiSwitchPage, (L"page_atlas.xml"));
573 		m_SimState = SimInactive;
574 	}
575 	UpdateSimButtons();
576 }
577 
OnRandomReseed(wxCommandEvent & WXUNUSED (evt))578 void MapSidebar::OnRandomReseed(wxCommandEvent& WXUNUSED(evt))
579 {
580 	// Pick a shortish randomish value
581 	wxString seed;
582 	seed << (int)floor((rand() / (float)RAND_MAX) * 10000.f);
583 	wxDynamicCast(FindWindow(ID_RandomSeed), wxTextCtrl)->SetValue(seed);
584 }
585 
OnRandomGenerate(wxCommandEvent & WXUNUSED (evt))586 void MapSidebar::OnRandomGenerate(wxCommandEvent& WXUNUSED(evt))
587 {
588 	if (m_ScenarioEditor.DiscardChangesDialog())
589 		return;
590 
591 	wxChoice* scriptChoice = wxDynamicCast(FindWindow(ID_RandomScript), wxChoice);
592 
593 	if (scriptChoice->GetSelection() < 0)
594 		return;
595 
596 	// TODO: this settings thing seems a bit of a mess,
597 	// since it's mixing data from three different sources
598 
599 	AtObj settings = m_MapSettingsCtrl->UpdateSettingsObject();
600 
601 	AtObj scriptSettings = dynamic_cast<AtObjClientData*>(scriptChoice->GetClientObject(scriptChoice->GetSelection()))->GetValue();
602 
603 	settings.addOverlay(scriptSettings);
604 
605 	wxChoice* sizeChoice = wxDynamicCast(FindWindow(ID_RandomSize), wxChoice);
606 	wxString size;
607 	size << (intptr_t)sizeChoice->GetClientData(sizeChoice->GetSelection());
608 	settings.setInt("Size", wxAtoi(size));
609 
610 	settings.setBool("Nomad", wxDynamicCast(FindWindow(ID_RandomNomad), wxCheckBox)->GetValue());
611 
612 	settings.setInt("Seed", wxAtoi(wxDynamicCast(FindWindow(ID_RandomSeed), wxTextCtrl)->GetValue()));
613 
614 	std::string json = AtlasObject::SaveToJSON(settings);
615 
616 	wxBusyInfo busy(_("Generating map"));
617 	wxBusyCursor busyc;
618 
619 	wxString scriptName(settings["Script"]);
620 
621 	// Copy the old map settings, so we don't lose them if the map generation fails
622 	AtObj oldSettings = settings;
623 
624 	AtlasMessage::qGenerateMap qry((std::wstring)scriptName.wc_str(), json);
625 	qry.Post();
626 
627 	if (qry.status < 0)
628 	{
629 		// Display error message and revert to old map settings
630 		wxLogError(_("Random map script '%ls' failed"), scriptName.wc_str());
631 		m_MapSettingsCtrl->SetMapSettings(oldSettings);
632 	}
633 
634 	m_ScenarioEditor.NotifyOnMapReload();
635 }
636 
OnOpenPlayerPanel(wxCommandEvent & WXUNUSED (evt))637 void MapSidebar::OnOpenPlayerPanel(wxCommandEvent& WXUNUSED(evt))
638 {
639 	m_ScenarioEditor.SelectPage(_T("PlayerSidebar"));
640 }
641 
642 BEGIN_EVENT_TABLE(MapSidebar, Sidebar)
643 	EVT_COLLAPSIBLEPANE_CHANGED(wxID_ANY, MapSidebar::OnCollapse)
644 	EVT_BUTTON(ID_SimPlay, MapSidebar::OnSimPlay)
645 	EVT_BUTTON(ID_SimFast, MapSidebar::OnSimPlay)
646 	EVT_BUTTON(ID_SimSlow, MapSidebar::OnSimPlay)
647 	EVT_BUTTON(ID_SimPause, MapSidebar::OnSimPause)
648 	EVT_BUTTON(ID_SimReset, MapSidebar::OnSimReset)
649 	EVT_BUTTON(ID_RandomReseed, MapSidebar::OnRandomReseed)
650 	EVT_BUTTON(ID_RandomGenerate, MapSidebar::OnRandomGenerate)
651 	EVT_BUTTON(ID_OpenPlayerPanel, MapSidebar::OnOpenPlayerPanel)
652 END_EVENT_TABLE();
653