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