1 /*
2  *  This file is part of Dune Legacy.
3  *
4  *  Dune Legacy 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  *  Dune Legacy 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 Dune Legacy.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <Menu/CustomGameMenu.h>
19 #include <Menu/CustomGamePlayers.h>
20 
21 #include <FileClasses/GFXManager.h>
22 #include <FileClasses/TextManager.h>
23 #include <FileClasses/INIFile.h>
24 
25 #include <GUI/Spacer.h>
26 #include <GUI/GUIStyle.h>
27 #include <GUI/dune/GameOptionsWindow.h>
28 #include <GUI/dune/LoadSaveWindow.h>
29 #include <GUI/dune/DuneStyle.h>
30 
31 #include <config.h>
32 #include <misc/fnkdat.h>
33 #include <misc/FileSystem.h>
34 #include <misc/draw_util.h>
35 #include <misc/string_util.h>
36 
37 #include <INIMap/INIMapPreviewCreator.h>
38 #include <GameInitSettings.h>
39 
40 #include <globals.h>
41 
42 #include <memory>
43 
44 
CustomGameMenu(bool multiplayer,bool LANServer)45 CustomGameMenu::CustomGameMenu(bool multiplayer, bool LANServer)
46  : MenuBase(), bMultiplayer(multiplayer), bLANServer(LANServer), currentGameOptions(settings.gameOptions) {
47     // set up window
48     SDL_Texture *pBackground = pGFXManager->getUIGraphic(UI_MenuBackground);
49     setBackground(pBackground, false);
50     resize(getTextureSize(pBackground));
51 
52     setWindowWidget(&windowWidget);
53 
54     windowWidget.addWidget(&mainVBox, Point(24,23), Point(getRendererWidth() - 48, getRendererHeight() - 32));
55 
56     captionLabel.setText(bMultiplayer ? (bLANServer ? _("LAN Game") : _("Internet Game")) : _("Custom Game"));
57     captionLabel.setAlignment(Alignment_HCenter);
58     mainVBox.addWidget(&captionLabel, 24);
59     mainVBox.addWidget(VSpacer::create(24));
60 
61     mainVBox.addWidget(Spacer::create(), 0.05);
62 
63     mainVBox.addWidget(&mainHBox, 0.80);
64 
65     mainHBox.addWidget(Spacer::create(), 0.05);
66     mainHBox.addWidget(&leftVBox, 0.8);
67 
68     leftVBox.addWidget(&mapTypeButtonsHBox, 24);
69 
70     singleplayerMapsButton.setText(_("SP Maps"));
71     singleplayerMapsButton.setToggleButton(true);
72     singleplayerMapsButton.setOnClick(std::bind(&CustomGameMenu::onMapTypeChange, this, 0));
73     mapTypeButtonsHBox.addWidget(&singleplayerMapsButton);
74 
75     singleplayerUserMapsButton.setText(_("SP User Maps"));
76     singleplayerUserMapsButton.setToggleButton(true);
77     singleplayerUserMapsButton.setOnClick(std::bind(&CustomGameMenu::onMapTypeChange, this, 1));
78     mapTypeButtonsHBox.addWidget(&singleplayerUserMapsButton);
79 
80     multiplayerMapsButton.setText(_("MP Maps"));
81     multiplayerMapsButton.setToggleButton(true);
82     multiplayerMapsButton.setOnClick(std::bind(&CustomGameMenu::onMapTypeChange, this, 2));
83     mapTypeButtonsHBox.addWidget(&multiplayerMapsButton);
84 
85     multiplayerUserMapsButton.setText(_("MP User Maps"));
86     multiplayerUserMapsButton.setToggleButton(true);
87     multiplayerUserMapsButton.setOnClick(std::bind(&CustomGameMenu::onMapTypeChange, this, 3));
88     mapTypeButtonsHBox.addWidget(&multiplayerUserMapsButton);
89 
90     dummyButton.setEnabled(false);
91     mapTypeButtonsHBox.addWidget(&dummyButton, 17);
92     mapList.setAutohideScrollbar(false);
93     mapList.setOnSelectionChange(std::bind(&CustomGameMenu::onMapListSelectionChange, this, std::placeholders::_1));
94     mapList.setOnDoubleClick(std::bind(&CustomGameMenu::onNext, this));
95     leftVBox.addWidget(&mapList, 0.95);
96 
97     leftVBox.addWidget(VSpacer::create(10));
98 
99     multiplePlayersPerHouseCheckbox.setText(_("Multiple players per house"));
100     optionsHBox.addWidget(&multiplePlayersPerHouseCheckbox);
101     optionsHBox.addWidget(Spacer::create());
102     gameOptionsButton.setText(_("Game Options..."));
103     gameOptionsButton.setOnClick(std::bind(&CustomGameMenu::onGameOptions, this));
104     optionsHBox.addWidget(&gameOptionsButton, 140);
105 
106     leftVBox.addWidget(Spacer::create(), 0.05);
107 
108     leftVBox.addWidget(&optionsHBox, 0.05);
109 
110     mainHBox.addWidget(HSpacer::create(8));
111     mainHBox.addWidget(Spacer::create(), 0.05);
112 
113     mainHBox.addWidget(&rightVBox, 180);
114     mainHBox.addWidget(Spacer::create(), 0.05);
115     minimap.setSurface( GUIStyle::getInstance().createButtonSurface(130,130,_("Choose map"), true, false), true);
116     rightVBox.addWidget(&minimap);
117 
118     rightVBox.addWidget(VSpacer::create(10));
119     rightVBox.addWidget(&mapPropertiesHBox, 0.01);
120     mapPropertiesHBox.addWidget(&mapPropertyNamesVBox, 75);
121     mapPropertiesHBox.addWidget(&mapPropertyValuesVBox, 105);
122     mapPropertyNamesVBox.addWidget(Label::create(_("Size") + ":"));
123     mapPropertyValuesVBox.addWidget(&mapPropertySize);
124     mapPropertyNamesVBox.addWidget(Label::create(_("Players") + ":"));
125     mapPropertyValuesVBox.addWidget(&mapPropertyPlayers);
126     mapPropertyNamesVBox.addWidget(Label::create(_("Author") + ":"));
127     mapPropertyValuesVBox.addWidget(&mapPropertyAuthors);
128     mapPropertyNamesVBox.addWidget(Label::create(_("License") + ":"));
129     mapPropertyValuesVBox.addWidget(&mapPropertyLicense);
130     rightVBox.addWidget(Spacer::create());
131 
132     mainVBox.addWidget(Spacer::create(), 0.05);
133 
134     mainVBox.addWidget(VSpacer::create(20));
135     mainVBox.addWidget(&buttonHBox, 24);
136     mainVBox.addWidget(VSpacer::create(14), 0.0);
137 
138     buttonHBox.addWidget(HSpacer::create(70));
139     cancelButton.setText(_("Back"));
140     cancelButton.setOnClick(std::bind(&CustomGameMenu::onCancel, this));
141     buttonHBox.addWidget(&cancelButton, 0.1);
142 
143     buttonHBox.addWidget(Spacer::create(), 0.0625);
144 
145     buttonHBox.addWidget(Spacer::create(), 0.25);
146     loadButton.setText(_("Load"));
147     loadButton.setVisible(bMultiplayer);
148     loadButton.setEnabled(bMultiplayer);
149     loadButton.setOnClick(std::bind(&CustomGameMenu::onLoad, this));
150     buttonHBox.addWidget(&loadButton, 0.175);
151     buttonHBox.addWidget(Spacer::create(), 0.25);
152 
153     buttonHBox.addWidget(Spacer::create(), 0.0625);
154 
155     nextButton.setText(_("Next"));
156     nextButton.setOnClick(std::bind(&CustomGameMenu::onNext, this));
157     buttonHBox.addWidget(&nextButton, 0.1);
158     buttonHBox.addWidget(HSpacer::create(90));
159 
160     onMapTypeChange(0);
161 }
162 
~CustomGameMenu()163 CustomGameMenu::~CustomGameMenu()
164 {
165     ;
166 }
167 
168 
onChildWindowClose(Window * pChildWindow)169 void CustomGameMenu::onChildWindowClose(Window* pChildWindow) {
170     LoadSaveWindow* pLoadSaveWindow = dynamic_cast<LoadSaveWindow*>(pChildWindow);
171     if(pLoadSaveWindow != nullptr) {
172         std::string filename = pLoadSaveWindow->getFilename();
173 
174         if(filename != "") {
175             std::string savegamedata = readCompleteFile(filename);
176 
177             std::string servername = settings.general.playerName + "'s Game";
178             GameInitSettings gameInitSettings(getBasename(filename, true), savegamedata, servername);
179 
180             CustomGamePlayers* pCustomGamePlayers = new CustomGamePlayers(gameInitSettings, true, bLANServer);
181             int ret = pCustomGamePlayers->showMenu();
182             delete pCustomGamePlayers;
183             if(ret != MENU_QUIT_DEFAULT) {
184                 quit(ret);
185             }
186         }
187     }
188 
189     GameOptionsWindow* pGameOptionsWindow = dynamic_cast<GameOptionsWindow*>(pChildWindow);
190     if(pGameOptionsWindow != nullptr) {
191         currentGameOptions = pGameOptionsWindow->getGameOptions();
192     }
193 }
194 
onNext()195 void CustomGameMenu::onNext()
196 {
197     if(mapList.getSelectedIndex() < 0) {
198         return;
199     }
200 
201     std::string mapFilename = currentMapDirectory + mapList.getSelectedEntry() + ".ini";
202     getCaseInsensitiveFilename(mapFilename);
203 
204     GameInitSettings gameInitSettings;
205     if(bMultiplayer) {
206         std::string servername = settings.general.playerName + "'s Game";
207         gameInitSettings = GameInitSettings(getBasename(mapFilename, true), readCompleteFile(mapFilename), servername, multiplePlayersPerHouseCheckbox.isChecked(), currentGameOptions);
208     } else {
209         gameInitSettings = GameInitSettings(getBasename(mapFilename, true), readCompleteFile(mapFilename), multiplePlayersPerHouseCheckbox.isChecked(), currentGameOptions);
210     }
211 
212     CustomGamePlayers* pCustomGamePlayers = new CustomGamePlayers(gameInitSettings, true, bLANServer);
213     int ret = pCustomGamePlayers->showMenu();
214     delete pCustomGamePlayers;
215     if(ret != MENU_QUIT_DEFAULT) {
216         quit(ret);
217     }
218 }
219 
onCancel()220 void CustomGameMenu::onCancel()
221 {
222     quit();
223 }
224 
onLoad()225 void CustomGameMenu::onLoad()
226 {
227     char tmp[FILENAME_MAX];
228     fnkdat("mpsave/", tmp, FILENAME_MAX, FNKDAT_USER | FNKDAT_CREAT);
229     std::string savepath(tmp);
230     openWindow(LoadSaveWindow::create(false, _("Load Game"), savepath, "dls"));
231 }
232 
onGameOptions()233 void CustomGameMenu::onGameOptions()
234 {
235     openWindow(GameOptionsWindow::create(currentGameOptions));
236 }
237 
onMapTypeChange(int buttonID)238 void CustomGameMenu::onMapTypeChange(int buttonID)
239 {
240     singleplayerMapsButton.setToggleState(buttonID == 0);
241     singleplayerUserMapsButton.setToggleState(buttonID == 1);
242     multiplayerMapsButton.setToggleState(buttonID == 2);
243     multiplayerUserMapsButton.setToggleState(buttonID == 3);
244 
245     switch(buttonID) {
246         case 0: {
247             currentMapDirectory = getDuneLegacyDataDir() + "/maps/singleplayer/";
248         } break;
249         case 1: {
250             char tmp[FILENAME_MAX];
251             fnkdat("maps/singleplayer/", tmp, FILENAME_MAX, FNKDAT_USER | FNKDAT_CREAT);
252             currentMapDirectory = tmp;
253         } break;
254         case 2: {
255             currentMapDirectory = getDuneLegacyDataDir() + "/maps/multiplayer/";
256         } break;
257         case 3: {
258             char tmp[FILENAME_MAX];
259             fnkdat("maps/multiplayer/", tmp, FILENAME_MAX, FNKDAT_USER | FNKDAT_CREAT);
260             currentMapDirectory = tmp;
261         } break;
262     }
263 
264     mapList.clearAllEntries();
265 
266     for(const std::string& file : getFileNamesList(currentMapDirectory, "ini", true, FileListOrder_Name_CaseInsensitive_Asc)) {
267         mapList.addEntry(file.substr(0, file.length() - 4));
268     }
269 
270     if(mapList.getNumEntries() > 0) {
271         mapList.setSelectedItem(0);
272     } else {
273         minimap.setSurface( GUIStyle::getInstance().createButtonSurface(130,130,_("No map available"), true, false), true);
274         mapPropertySize.setText("");
275         mapPropertyPlayers.setText("");
276         mapPropertyAuthors.setText("");
277         mapPropertyLicense.setText("");
278     }
279 }
280 
onMapListSelectionChange(bool bInteractive)281 void CustomGameMenu::onMapListSelectionChange(bool bInteractive)
282 {
283     nextButton.setEnabled(true);
284 
285     if(mapList.getSelectedIndex() < 0) {
286         return;
287     }
288 
289     std::string mapFilename = currentMapDirectory + mapList.getSelectedEntry() + ".ini";
290     getCaseInsensitiveFilename(mapFilename);
291 
292     std::shared_ptr<INIFile> pMap(new INIFile(mapFilename));
293 
294     int sizeX = 0;
295     int sizeY = 0;
296 
297     if(pMap->hasKey("MAP","Seed")) {
298         // old map format with seed value
299         int mapscale = pMap->getIntValue("BASIC", "MapScale", -1);
300 
301         switch(mapscale) {
302             case 0: {
303                 sizeX = 62;
304                 sizeY = 62;
305             } break;
306 
307             case 1: {
308                 sizeX = 32;
309                 sizeY = 32;
310             } break;
311 
312             case 2: {
313                 sizeX = 21;
314                 sizeY = 21;
315             } break;
316 
317             default: {
318                 sizeX = 64;
319                 sizeY = 64;
320             }
321         }
322     } else {
323         // new map format with saved map
324         sizeX = pMap->getIntValue("MAP","SizeX", 0);
325         sizeY = pMap->getIntValue("MAP","SizeY", 0);
326     }
327 
328     mapPropertySize.setText(stringify(sizeX) + " x " + stringify(sizeY));
329 
330     SDL_Surface* pMapSurface = nullptr;
331     try {
332         INIMapPreviewCreator mapPreviewCreator(pMap);
333         pMapSurface = mapPreviewCreator.createMinimapImageOfMap(1, DuneStyle::buttonBorderColor);
334     } catch(...) {
335         pMapSurface = GUIStyle::getInstance().createButtonSurface(130, 130, "Error", true, false);
336         loadButton.setEnabled(false);
337     }
338     minimap.setSurface(pMapSurface, true);
339 
340     int numPlayers = 0;
341     if(pMap->hasSection("Atreides")) numPlayers++;
342     if(pMap->hasSection("Ordos")) numPlayers++;
343     if(pMap->hasSection("Harkonnen")) numPlayers++;
344     if(pMap->hasSection("Fremen")) numPlayers++;
345     if(pMap->hasSection("Mercenary")) numPlayers++;
346     if(pMap->hasSection("Sardaukar")) numPlayers++;
347     if(pMap->hasSection("Player1")) numPlayers++;
348     if(pMap->hasSection("Player2")) numPlayers++;
349     if(pMap->hasSection("Player3")) numPlayers++;
350     if(pMap->hasSection("Player4")) numPlayers++;
351     if(pMap->hasSection("Player5")) numPlayers++;
352     if(pMap->hasSection("Player6")) numPlayers++;
353 
354     mapPropertyPlayers.setText(stringify(numPlayers));
355 
356 
357 
358     std::string authors = pMap->getStringValue("BASIC","Author", "-");
359     if(authors.size() > 11) {
360         authors = authors.substr(0,9) + "...";
361     }
362     mapPropertyAuthors.setText(authors);
363 
364 
365     mapPropertyLicense.setText(pMap->getStringValue("BASIC","License", "-"));
366 
367 }
368