1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ags/lib/std/algorithm.h"
24 #include "ags/lib/allegro.h" // find files
25 #include "ags/engine/gui/gui_dialog.h"
26 #include "ags/shared/ac/common.h"
27 #include "ags/engine/ac/draw.h"
28 #include "ags/engine/ac/game.h"
29 #include "ags/engine/ac/game_setup.h"
30 #include "ags/shared/ac/game_setup_struct.h"
31 #include "ags/engine/ac/global_game.h"
32 #include "ags/engine/gui/csci_dialog.h"
33 #include "ags/shared/gfx/bitmap.h"
34 #include "ags/engine/gfx/graphics_driver.h"
35 #include "ags/engine/debugging/debug_log.h"
36 #include "ags/shared/util/path.h"
37 #include "ags/ags.h"
38 #include "ags/globals.h"
39
40 namespace AGS3 {
41
42 using namespace AGS::Shared;
43 using namespace AGS::Engine;
44
45 #undef MAXSAVEGAMES
46 #define MAXSAVEGAMES 20
47
get_gui_dialog_buffer()48 char *get_gui_dialog_buffer() {
49 return _G(buffer2);
50 }
51
52 //
53 // TODO: rewrite the whole thing to work inside the main game update and render loop!
54 //
55
prepare_gui_screen(int x,int y,int width,int height,bool opaque)56 Bitmap *prepare_gui_screen(int x, int y, int width, int height, bool opaque) {
57 _G(windowPosX) = x;
58 _G(windowPosY) = y;
59 _G(windowPosWidth) = width;
60 _G(windowPosHeight) = height;
61 if (_G(windowBuffer)) {
62 _G(windowBuffer) = recycle_bitmap(_G(windowBuffer), _G(windowBuffer)->GetColorDepth(), _G(windowPosWidth), _G(windowPosHeight), !opaque);
63 } else {
64 _G(windowBuffer) = BitmapHelper::CreateBitmap(_G(windowPosWidth), _G(windowPosHeight), _GP(game).GetColorDepth());
65 _G(windowBuffer) = ReplaceBitmapWithSupportedFormat(_G(windowBuffer));
66 }
67 _G(dialogDDB) = recycle_ddb_bitmap(_G(dialogDDB), _G(windowBuffer), false, opaque);
68 return _G(windowBuffer);
69 }
70
get_gui_screen()71 Bitmap *get_gui_screen() {
72 return _G(windowBuffer);
73 }
74
clear_gui_screen()75 void clear_gui_screen() {
76 if (_G(dialogDDB))
77 _G(gfxDriver)->DestroyDDB(_G(dialogDDB));
78 _G(dialogDDB) = nullptr;
79 delete _G(windowBuffer);
80 _G(windowBuffer) = nullptr;
81 }
82
refresh_gui_screen()83 void refresh_gui_screen() {
84 _G(gfxDriver)->UpdateDDBFromBitmap(_G(dialogDDB), _G(windowBuffer), false);
85 render_graphics(_G(dialogDDB), _G(windowPosX), _G(windowPosY));
86 }
87
loadgamedialog()88 int loadgamedialog() {
89 const int wnd_width = 200;
90 const int wnd_height = 120;
91 const int boxleft = _G(myscrnwid) / 2 - wnd_width / 2;
92 const int boxtop = _G(myscrnhit) / 2 - wnd_height / 2;
93 const int buttonhit = _GP(usetup).textheight + 5;
94
95 int handl = CSCIDrawWindow(boxleft, boxtop, wnd_width, wnd_height);
96 int ctrlok =
97 CSCICreateControl(CNT_PUSHBUTTON | CNF_DEFAULT, 135, 5, 60, 10, get_global_message(MSG_RESTORE));
98 int ctrlcancel =
99 CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 135, 5 + buttonhit, 60, 10,
100 get_global_message(MSG_CANCEL));
101 int ctrllist = CSCICreateControl(CNT_LISTBOX, 10, 30, 120, 80, nullptr);
102 int ctrltex1 = CSCICreateControl(CNT_LABEL, 10, 5, 120, 0, get_global_message(MSG_SELECTLOAD));
103 CSCISendControlMessage(ctrllist, CLB_CLEAR, 0, 0);
104
105 preparesavegamelist(ctrllist);
106 CSCIMessage mes;
107 _G(lpTemp) = nullptr;
108 int toret = -1;
109 while (1) {
110 CSCIWaitMessage(&mes); //printf("mess: %d, id %d ",mes.code,mes.id);
111 if (mes.code == CM_COMMAND) {
112 if (mes.id == ctrlok) {
113 int cursel = CSCISendControlMessage(ctrllist, CLB_GETCURSEL, 0, 0);
114 if ((cursel >= _G(numsaves)) | (cursel < 0))
115 _G(lpTemp) = nullptr;
116 else {
117 toret = _G(filenumbers)[cursel];
118 String path = get_save_game_path(toret);
119 strcpy(_G(bufTemp), path.GetCStr());
120 _G(lpTemp) = &_G(bufTemp)[0];
121 }
122 } else if (mes.id == ctrlcancel) {
123 _G(lpTemp) = nullptr;
124 }
125
126 break;
127 }
128 }
129
130 CSCIDeleteControl(ctrltex1);
131 CSCIDeleteControl(ctrllist);
132 CSCIDeleteControl(ctrlok);
133 CSCIDeleteControl(ctrlcancel);
134 CSCIEraseWindow(handl);
135 return toret;
136 }
137
savegamedialog()138 int savegamedialog() {
139 char okbuttontext[50];
140 strcpy(okbuttontext, get_global_message(MSG_SAVEBUTTON));
141 char labeltext[200];
142 strcpy(labeltext, get_global_message(MSG_SAVEDIALOG));
143 const int wnd_width = 200;
144 const int wnd_height = 120;
145 const int boxleft = _G(myscrnwid) / 2 - wnd_width / 2;
146 const int boxtop = _G(myscrnhit) / 2 - wnd_height / 2;
147 const int buttonhit = _GP(usetup).textheight + 5;
148 int labeltop = 5;
149
150 int handl = CSCIDrawWindow(boxleft, boxtop, wnd_width, wnd_height);
151 int ctrlcancel =
152 CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 135, 5 + buttonhit, 60, 10,
153 get_global_message(MSG_CANCEL));
154 int ctrllist = CSCICreateControl(CNT_LISTBOX, 10, 40, 120, 80, nullptr);
155 int ctrltbox = 0;
156
157 CSCISendControlMessage(ctrllist, CLB_CLEAR, 0, 0); // clear the list box
158 preparesavegamelist(ctrllist);
159 if (_G(toomanygames)) {
160 strcpy(okbuttontext, get_global_message(MSG_REPLACE));
161 strcpy(labeltext, get_global_message(MSG_MUSTREPLACE));
162 labeltop = 2;
163 } else
164 ctrltbox = CSCICreateControl(CNT_TEXTBOX, 10, 29, 120, 0, nullptr);
165
166 int ctrlok = CSCICreateControl(CNT_PUSHBUTTON | CNF_DEFAULT, 135, 5, 60, 10, okbuttontext);
167 int ctrltex1 = CSCICreateControl(CNT_LABEL, 10, labeltop, 120, 0, labeltext);
168 CSCIMessage mes;
169
170 _G(lpTemp) = nullptr;
171 if (_G(numsaves) > 0)
172 CSCISendControlMessage(ctrllist, CLB_GETTEXT, 0, &_G(buffer2)[0]);
173 else
174 _G(buffer2)[0] = 0;
175
176 CSCISendControlMessage(ctrltbox, CTB_SETTEXT, 0, &_G(buffer2)[0]);
177
178 int toret = -1;
179 while (1) {
180 CSCIWaitMessage(&mes); //printf("mess: %d, id %d ",mes.code,mes.id);
181 if (mes.code == CM_COMMAND) {
182 if (mes.id == ctrlok) {
183 int cursell = CSCISendControlMessage(ctrllist, CLB_GETCURSEL, 0, 0);
184 CSCISendControlMessage(ctrltbox, CTB_GETTEXT, 0, &_G(buffer2)[0]);
185
186 if (_G(numsaves) > 0)
187 CSCISendControlMessage(ctrllist, CLB_GETTEXT, cursell, &_G(bufTemp)[0]);
188 else
189 strcpy(_G(bufTemp), "_NOSAVEGAMENAME");
190
191 if (_G(toomanygames)) {
192 int nwhand = CSCIDrawWindow(boxleft + 5, boxtop + 20, 190, 65);
193 int lbl1 =
194 CSCICreateControl(CNT_LABEL, 15, 5, 160, 0, get_global_message(MSG_REPLACEWITH1));
195 int lbl2 = CSCICreateControl(CNT_LABEL, 25, 14, 160, 0, _G(bufTemp));
196 int lbl3 =
197 CSCICreateControl(CNT_LABEL, 15, 25, 160, 0, get_global_message(MSG_REPLACEWITH2));
198 int txt1 = CSCICreateControl(CNT_TEXTBOX, 15, 35, 160, 0, _G(bufTemp));
199 int btnOk =
200 CSCICreateControl(CNT_PUSHBUTTON | CNF_DEFAULT, 25, 50, 60, 10,
201 get_global_message(MSG_REPLACE));
202 int btnCancel =
203 CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 95, 50, 60, 10,
204 get_global_message(MSG_CANCEL));
205
206 CSCIMessage cmes;
207 do {
208 CSCIWaitMessage(&cmes);
209 } while (cmes.code != CM_COMMAND);
210
211 CSCISendControlMessage(txt1, CTB_GETTEXT, 0, &_G(buffer2)[0]);
212 CSCIDeleteControl(btnCancel);
213 CSCIDeleteControl(btnOk);
214 CSCIDeleteControl(txt1);
215 CSCIDeleteControl(lbl3);
216 CSCIDeleteControl(lbl2);
217 CSCIDeleteControl(lbl1);
218 CSCIEraseWindow(nwhand);
219 _G(bufTemp)[0] = 0;
220
221 if (cmes.id == btnCancel) {
222 _G(lpTemp) = nullptr;
223 break;
224 } else
225 toret = _G(filenumbers)[cursell];
226
227 } else if (strcmp(_G(buffer2), _G(bufTemp)) != 0) { // create a new game (description different)
228 int highestnum = 0;
229 for (int pp = 0; pp < _G(numsaves); pp++) {
230 if (_G(filenumbers)[pp] > highestnum)
231 highestnum = _G(filenumbers)[pp];
232 }
233
234 if (highestnum > 90)
235 quit("Save game directory overflow");
236
237 toret = highestnum + 1;
238 String path = get_save_game_path(toret);
239 strcpy(_G(bufTemp), path.GetCStr());
240 } else {
241 toret = _G(filenumbers)[cursell];
242 _G(bufTemp)[0] = 0;
243 }
244
245 if (_G(bufTemp)[0] == 0) {
246 String path = get_save_game_path(toret);
247 strcpy(_G(bufTemp), path.GetCStr());
248 }
249
250 _G(lpTemp) = &_G(bufTemp)[0];
251 _G(lpTemp2) = &_G(buffer2)[0];
252 } else if (mes.id == ctrlcancel) {
253 _G(lpTemp) = nullptr;
254 }
255 break;
256 } else if (mes.code == CM_SELCHANGE) {
257 int cursel = CSCISendControlMessage(ctrllist, CLB_GETCURSEL, 0, 0);
258 if (cursel >= 0) {
259 CSCISendControlMessage(ctrllist, CLB_GETTEXT, cursel, &_G(buffer2)[0]);
260 CSCISendControlMessage(ctrltbox, CTB_SETTEXT, 0, &_G(buffer2)[0]);
261 }
262 }
263 }
264
265 CSCIDeleteControl(ctrltbox);
266 CSCIDeleteControl(ctrltex1);
267 CSCIDeleteControl(ctrllist);
268 CSCIDeleteControl(ctrlok);
269 CSCIDeleteControl(ctrlcancel);
270 CSCIEraseWindow(handl);
271 return toret;
272 }
273
preparesavegamelist(int ctrllist)274 void preparesavegamelist(int ctrllist) {
275 // Get a list of savegames
276 SaveStateList saveList = ::AGS::g_vm->listSaves();
277
278 // The original AGS sorts the list from most recent to oldest.
279 // We don't have the modification date in ScummVM though. We could try to
280 // parse the date string, but for now, sort by decreasing slot number, which
281 // should work better than the default sort by increasing slot.
282 Common::sort(saveList.begin(), saveList.end(),
283 [](const SaveStateDescriptor & x, const SaveStateDescriptor & y) {
284 return x.getSaveSlot() > y.getSaveSlot();
285 });
286
287 // fill in the list box and global savegameindex[] array for backward compatibilty
288 for (_G(numsaves) = 0; _G(numsaves) < (int)saveList.size(); ++_G(numsaves)) {
289 CSCISendControlMessage(ctrllist, CLB_ADDITEM, 0,
290 saveList[_G(numsaves)].getDescription().encode().c_str());
291 _G(filenumbers)[_G(numsaves)] = saveList[_G(numsaves)].getSaveSlot();
292 _G(filedates)[_G(numsaves)] = 0;
293 //(long int)saveList[_G(numsaves)].FileTime;
294 }
295 _G(toomanygames) = (_G(numsaves) >= MAXSAVEGAMES) ? 1 : 0;
296 // Select the first item
297 CSCISendControlMessage(ctrllist, CLB_SETCURSEL, 0, 0);
298 }
299
enterstringwindow(const char * prompttext,char * stouse)300 void enterstringwindow(const char *prompttext, char *stouse) {
301 const int wnd_width = 200;
302 const int wnd_height = 40;
303 const int boxleft = 60, boxtop = 80;
304 int wantCancel = 0;
305 if (prompttext[0] == '!') {
306 wantCancel = 1;
307 prompttext++;
308 }
309
310 int handl = CSCIDrawWindow(boxleft, boxtop, wnd_width, wnd_height);
311 int ctrlok = CSCICreateControl(CNT_PUSHBUTTON | CNF_DEFAULT, 135, 5, 60, 10, "OK");
312 int ctrlcancel = -1;
313 if (wantCancel)
314 ctrlcancel = CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 135, 20, 60, 10, get_global_message(MSG_CANCEL));
315 int ctrltbox = CSCICreateControl(CNT_TEXTBOX, 10, 29, 120, 0, nullptr);
316 int ctrltex1 = CSCICreateControl(CNT_LABEL, 10, 5, 120, 0, prompttext);
317 CSCIMessage mes;
318
319 while (1) {
320 CSCIWaitMessage(&mes);
321 if (mes.code == CM_COMMAND) {
322 if (mes.id == ctrlcancel)
323 _G(buffer2)[0] = 0;
324 else
325 CSCISendControlMessage(ctrltbox, CTB_GETTEXT, 0, &_G(buffer2)[0]);
326 break;
327 }
328 }
329
330 CSCIDeleteControl(ctrltex1);
331 CSCIDeleteControl(ctrltbox);
332 CSCIDeleteControl(ctrlok);
333 if (wantCancel)
334 CSCIDeleteControl(ctrlcancel);
335 CSCIEraseWindow(handl);
336 strcpy(stouse, _G(buffer2));
337 }
338
enternumberwindow(char * prompttext)339 int enternumberwindow(char *prompttext) {
340 char ourbuf[200];
341 enterstringwindow(prompttext, ourbuf);
342 if (ourbuf[0] == 0)
343 return -9999;
344 return atoi(ourbuf);
345 }
346
roomSelectorWindow(int currentRoom,int numRooms,int * roomNumbers,char ** roomNames)347 int roomSelectorWindow(int currentRoom, int numRooms, int *roomNumbers, char **roomNames) {
348 char labeltext[200];
349 strcpy(labeltext, get_global_message(MSG_SAVEDIALOG));
350 const int wnd_width = 240;
351 const int wnd_height = 160;
352 const int boxleft = _G(myscrnwid) / 2 - wnd_width / 2;
353 const int boxtop = _G(myscrnhit) / 2 - wnd_height / 2;
354 const int labeltop = 5;
355
356 int handl = CSCIDrawWindow(boxleft, boxtop, wnd_width, wnd_height);
357 int ctrllist = CSCICreateControl(CNT_LISTBOX, 10, 40, 220, 100, nullptr);
358 int ctrlcancel =
359 CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 80, 145, 60, 10, "Cancel");
360
361 CSCISendControlMessage(ctrllist, CLB_CLEAR, 0, 0); // clear the list box
362 for (int aa = 0; aa < numRooms; aa++) {
363 sprintf(_G(buff), "%3d %s", roomNumbers[aa], roomNames[aa]);
364 CSCISendControlMessage(ctrllist, CLB_ADDITEM, 0, &_G(buff)[0]);
365 if (roomNumbers[aa] == currentRoom) {
366 CSCISendControlMessage(ctrllist, CLB_SETCURSEL, aa, 0);
367 }
368 }
369
370 int ctrlok = CSCICreateControl(CNT_PUSHBUTTON | CNF_DEFAULT, 10, 145, 60, 10, "OK");
371 int ctrltex1 = CSCICreateControl(CNT_LABEL, 10, labeltop, 180, 0, "Choose which room to go to:");
372 CSCIMessage mes;
373
374 _G(lpTemp) = nullptr;
375 _G(buffer2)[0] = 0;
376
377 int ctrltbox = CSCICreateControl(CNT_TEXTBOX, 10, 29, 120, 0, nullptr);
378 CSCISendControlMessage(ctrltbox, CTB_SETTEXT, 0, &_G(buffer2)[0]);
379
380 int toret = -1;
381 while (1) {
382 CSCIWaitMessage(&mes); //printf("mess: %d, id %d ",mes.code,mes.id);
383 if (mes.code == CM_COMMAND) {
384 if (mes.id == ctrlok) {
385 CSCISendControlMessage(ctrltbox, CTB_GETTEXT, 0, &_G(buffer2)[0]);
386 if (Common::isDigit(_G(buffer2)[0])) {
387 toret = atoi(_G(buffer2));
388 }
389 } else if (mes.id == ctrlcancel) {
390 }
391 break;
392 } else if (mes.code == CM_SELCHANGE) {
393 int cursel = CSCISendControlMessage(ctrllist, CLB_GETCURSEL, 0, 0);
394 if (cursel >= 0) {
395 sprintf(_G(buffer2), "%d", roomNumbers[cursel]);
396 CSCISendControlMessage(ctrltbox, CTB_SETTEXT, 0, &_G(buffer2)[0]);
397 }
398 }
399 }
400
401 CSCIDeleteControl(ctrltbox);
402 CSCIDeleteControl(ctrltex1);
403 CSCIDeleteControl(ctrllist);
404 CSCIDeleteControl(ctrlok);
405 CSCIDeleteControl(ctrlcancel);
406 CSCIEraseWindow(handl);
407 return toret;
408 }
409
myscimessagebox(const char * lpprompt,char * btn1,char * btn2)410 int myscimessagebox(const char *lpprompt, char *btn1, char *btn2) {
411 const int wnd_width = 240 - 80;
412 const int wnd_height = 120 - 80;
413 const int boxleft = 80;
414 const int boxtop = 80;
415
416 int windl = CSCIDrawWindow(boxleft, boxtop, wnd_width, wnd_height);
417 int lbl1 = CSCICreateControl(CNT_LABEL, 10, 5, 150, 0, lpprompt);
418 int btflag = CNT_PUSHBUTTON;
419
420 if (btn2 == nullptr)
421 btflag |= CNF_DEFAULT | CNF_CANCEL;
422 else
423 btflag |= CNF_DEFAULT;
424
425 int btnQuit = CSCICreateControl(btflag, 10, 25, 60, 10, btn1);
426 int btnPlay = 0;
427
428 if (btn2 != nullptr)
429 btnPlay = CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 85, 25, 60, 10, btn2);
430
431 _GP(smes).code = 0;
432
433 do {
434 if (SHOULD_QUIT)
435 return 1;
436
437 CSCIWaitMessage(&_GP(smes));
438 } while (_GP(smes).code != CM_COMMAND);
439
440 if (btnPlay)
441 CSCIDeleteControl(btnPlay);
442
443 CSCIDeleteControl(btnQuit);
444 CSCIDeleteControl(lbl1);
445 CSCIEraseWindow(windl);
446
447 if (_GP(smes).id == btnQuit)
448 return 1;
449
450 return 0;
451 }
452
quitdialog()453 int quitdialog() {
454 char quitbut[50], playbut[50];
455 strcpy(quitbut, get_global_message(MSG_QUITBUTTON));
456 strcpy(playbut, get_global_message(MSG_PLAYBUTTON));
457 return myscimessagebox(get_global_message(MSG_QUITDIALOG), quitbut, playbut);
458 }
459
460 } // namespace AGS3
461