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 "mohawk/myst.h"
24 #include "mohawk/myst_areas.h"
25 #include "mohawk/myst_card.h"
26 #include "mohawk/myst_graphics.h"
27 #include "mohawk/myst_state.h"
28 #include "mohawk/cursors.h"
29 #include "mohawk/sound.h"
30 #include "mohawk/video.h"
31 #include "mohawk/myst_stacks/menu.h"
32
33 #include "common/translation.h"
34 #include "graphics/cursorman.h"
35 #include "gui/message.h"
36
37 namespace Mohawk {
38 namespace MystStacks {
39
Menu(MohawkEngine_Myst * vm)40 Menu::Menu(MohawkEngine_Myst *vm) :
41 MystScriptParser(vm, kMenuStack),
42 _inGame(false),
43 _canSave(false),
44 _wasCursorVisible(true),
45 _introMoviesRunning(false) {
46
47 for (uint i = 0; i < ARRAYSIZE(_menuItemHovered); i++) {
48 _menuItemHovered[i] = false;
49 }
50
51 setupOpcodes();
52 }
53
~Menu()54 Menu::~Menu() {
55 }
56
setupOpcodes()57 void Menu::setupOpcodes() {
58 // "Stack-Specific" Opcodes
59 REGISTER_OPCODE(150, Menu, o_menuItemEnter);
60 REGISTER_OPCODE(151, Menu, o_menuItemLeave);
61 REGISTER_OPCODE(152, Menu, o_menuResume);
62 REGISTER_OPCODE(153, Menu, o_menuLoad);
63 REGISTER_OPCODE(154, Menu, o_menuSave);
64 REGISTER_OPCODE(155, Menu, o_menuNew);
65 REGISTER_OPCODE(156, Menu, o_menuOptions);
66 REGISTER_OPCODE(157, Menu, o_menuQuit);
67
68 // "Init" Opcodes
69 REGISTER_OPCODE(200, Menu, o_playIntroMovies);
70 REGISTER_OPCODE(201, Menu, o_menuInit);
71
72 // "Exit" Opcodes
73 REGISTER_OPCODE(300, Menu, NOP);
74 REGISTER_OPCODE(301, Menu, o_menuExit);
75 }
76
disablePersistentScripts()77 void Menu::disablePersistentScripts() {
78 _introMoviesRunning = false;
79 }
80
runPersistentScripts()81 void Menu::runPersistentScripts() {
82 if (_introMoviesRunning)
83 introMovies_run();
84 }
85
getVar(uint16 var)86 uint16 Menu::getVar(uint16 var) {
87 switch (var) {
88 case 1000: // New game
89 case 1001: // Load
90 case 1004: // Quit
91 case 1005: // Options
92 return _menuItemHovered[var - 1000] ? 1 : 0;
93 case 1002: // Save
94 if (_canSave) {
95 return _menuItemHovered[var - 1000] ? 1 : 0;
96 } else {
97 return 2;
98 }
99 case 1003: // Resume
100 if (_inGame) {
101 return _menuItemHovered[var - 1000] ? 1 : 0;
102 } else {
103 return 2;
104 }
105 default:
106 return MystScriptParser::getVar(var);
107 }
108 }
109
o_menuInit(uint16 var,const ArgumentsArray & args)110 void Menu::o_menuInit(uint16 var, const ArgumentsArray &args) {
111 _pauseToken = _vm->pauseEngine();
112
113 if (_inGame) {
114 _wasCursorVisible = CursorMan.isVisible();
115 }
116
117 if (!_wasCursorVisible) {
118 CursorMan.showMouse(true);
119 }
120
121 struct MenuButton {
122 uint16 highlightedIndex;
123 uint16 disabledIndex;
124 Graphics::TextAlign align;
125 };
126
127 static const MenuButton buttons[] = {
128 { 1, 0, Graphics::kTextAlignRight },
129 { 1, 0, Graphics::kTextAlignRight },
130 { 1, 2, Graphics::kTextAlignRight },
131 { 1, 2, Graphics::kTextAlignRight },
132 { 1, 0, Graphics::kTextAlignRight },
133 { 1, 0, Graphics::kTextAlignLeft }
134 };
135
136 const char **buttonCaptions = getButtonCaptions();
137
138 for (uint i = 0; i < ARRAYSIZE(buttons); i++) {
139 MystAreaImageSwitch *image = _vm->getCard()->getResource<MystAreaImageSwitch>(2 * i + 0);
140 MystAreaHover *hover = _vm->getCard()->getResource<MystAreaHover> (2 * i + 1);
141
142 Common::U32String str = Common::convertUtf8ToUtf32(buttonCaptions[i]);
143 drawButtonImages(str, image, buttons[i].align, buttons[i].highlightedIndex, buttons[i].disabledIndex);
144 hover->setRect(image->getRect());
145 }
146 }
147
getButtonCaptions() const148 const char **Menu::getButtonCaptions() const {
149 static const char *buttonCaptionsEnglish[] = {
150 "NEW GAME",
151 "LOAD GAME",
152 "SAVE GAME",
153 "RESUME",
154 "QUIT",
155 "OPTIONS"
156 };
157
158 static const char *buttonCaptionsFrench[] = {
159 "NOUVEAU",
160 "CHARGER",
161 "SAUVER",
162 "REPRENDRE",
163 "QUITTER",
164 "OPTIONS"
165 };
166
167 static const char *buttonCaptionsGerman[] = {
168 "NEUES SPIEL",
169 "SPIEL LADEN",
170 "SPIEL SPEICHERN",
171 "FORTSETZEN",
172 "BEENDEN",
173 "OPTIONEN"
174 };
175
176 static const char *buttonCaptionsSpanish[] = {
177 "JUEGO NUEVO",
178 "CARGAR JUEGO",
179 "GUARDAR JUEGO",
180 "CONTINUAR",
181 "SALIR",
182 "OPCIONES"
183 };
184
185 static const char *buttonCaptionsPolish[] = {
186 "NOWA GRA",
187 "ZAŁADUJ GRĘ",
188 "ZAPISZ GRĘ",
189 "POWRÓT",
190 "WYJŚCIE",
191 "OPCJE"
192 };
193
194 switch (_vm->getLanguage()) {
195 case Common::FR_FRA:
196 return buttonCaptionsFrench;
197 case Common::DE_DEU:
198 return buttonCaptionsGerman;
199 case Common::ES_ESP:
200 return buttonCaptionsSpanish;
201 case Common::PL_POL:
202 return buttonCaptionsPolish;
203 case Common::EN_ANY:
204 default:
205 return buttonCaptionsEnglish;
206 }
207 }
208
drawButtonImages(const Common::U32String & text,MystAreaImageSwitch * area,Graphics::TextAlign align,uint16 highlightedIndex,uint16 disabledIndex) const209 void Menu::drawButtonImages(const Common::U32String &text, MystAreaImageSwitch *area, Graphics::TextAlign align, uint16 highlightedIndex, uint16 disabledIndex) const {
210 Common::Rect backgroundRect = area->getRect();
211 Common::Rect textBoundingBox = _vm->_gfx->getTextBoundingBox(text, backgroundRect, align);
212
213 // Restrict the rectangle to the portion were the text will be drawn
214 if (align == Graphics::kTextAlignLeft) {
215 backgroundRect.right = textBoundingBox.right;
216 } else if (align == Graphics::kTextAlignRight) {
217 backgroundRect.left = textBoundingBox.left;
218 } else {
219 error("Unexpected align: %d", align);
220 }
221
222 // Update the area with the new background rect
223 area->setRect(backgroundRect);
224
225 MystAreaImageSwitch::SubImage idle = area->getSubImage(0);
226 area->setSubImageRect(0, Common::Rect(backgroundRect.left, idle.rect.top, backgroundRect.right, idle.rect.bottom));
227
228 // Align the text to the top of the destination rectangles
229 int16 deltaY;
230 if (_vm->getLanguage() == Common::PL_POL) {
231 deltaY = -2;
232 } else {
233 deltaY = backgroundRect.top - textBoundingBox.top;
234 }
235
236 if (highlightedIndex) {
237 replaceButtonSubImageWithText(text, align, area, highlightedIndex, backgroundRect, deltaY, 215, 216, 219);
238 }
239
240 if (disabledIndex) {
241 replaceButtonSubImageWithText(text, align, area, disabledIndex, backgroundRect, deltaY, 136, 140, 145);
242 }
243
244 uint16 cardBackground = _vm->getCard()->getBackgroundImageId();
245 _vm->_gfx->drawText(cardBackground, text, backgroundRect, 181, 184, 189, align, deltaY);
246 }
247
replaceButtonSubImageWithText(const Common::U32String & text,const Graphics::TextAlign & align,MystAreaImageSwitch * area,uint16 subimageIndex,const Common::Rect & backgroundRect,int16 deltaY,uint8 r,uint8 g,uint8 b) const248 void Menu::replaceButtonSubImageWithText(const Common::U32String &text, const Graphics::TextAlign &align, MystAreaImageSwitch *area,
249 uint16 subimageIndex, const Common::Rect &backgroundRect, int16 deltaY,
250 uint8 r, uint8 g, uint8 b) const {
251 uint16 cardBackground = _vm->getCard()->getBackgroundImageId();
252
253 MystAreaImageSwitch::SubImage highlighted = area->getSubImage(subimageIndex);
254 Common::Rect subImageRect(0, 0, backgroundRect.width(), backgroundRect.height());
255
256 // Create an image exactly the size of the rendered text with the backdrop as a background
257 _vm->_gfx->replaceImageWithRect(highlighted.wdib, cardBackground, backgroundRect);
258 area->setSubImageRect(subimageIndex, subImageRect);
259
260 // Draw the text in the subimage
261 _vm->_gfx->drawText(highlighted.wdib, text, subImageRect, r, g, b, align, deltaY);
262 }
263
o_menuItemEnter(uint16 var,const ArgumentsArray & args)264 void Menu::o_menuItemEnter(uint16 var, const ArgumentsArray &args) {
265 _menuItemHovered[var - 1000] = true;
266 _vm->getCard()->redrawArea(var);
267 }
268
o_menuItemLeave(uint16 var,const ArgumentsArray & args)269 void Menu::o_menuItemLeave(uint16 var, const ArgumentsArray &args) {
270 _menuItemHovered[var - 1000] = false;
271 _vm->getCard()->redrawArea(var);
272 }
273
o_menuResume(uint16 var,const ArgumentsArray & args)274 void Menu::o_menuResume(uint16 var, const ArgumentsArray &args) {
275 if (!_inGame) {
276 return;
277 }
278
279 _vm->resumeFromMainMenu();
280 }
281
o_menuLoad(uint16 var,const ArgumentsArray & args)282 void Menu::o_menuLoad(uint16 var, const ArgumentsArray &args) {
283 if (!showConfirmationDialog(_("Are you sure you want to load a saved game? All unsaved progress will be lost."),
284 _("Load game"), _("Cancel"))) {
285 return;
286 }
287
288 _vm->loadGameDialog();
289 }
290
o_menuSave(uint16 var,const ArgumentsArray & args)291 void Menu::o_menuSave(uint16 var, const ArgumentsArray &args) {
292 if (!_canSave) {
293 return;
294 }
295
296 _vm->saveGameDialog();
297 }
298
o_menuNew(uint16 var,const ArgumentsArray & args)299 void Menu::o_menuNew(uint16 var, const ArgumentsArray &args) {
300 if (!showConfirmationDialog(_("Are you sure you want to start a new game? All unsaved progress will be lost."),
301 _("New game"), _("Cancel"))) {
302 return;
303 }
304
305 _vm->_gameState->reset();
306 _vm->setTotalPlayTime(0);
307 _vm->setMainCursor(kDefaultMystCursor);
308 _vm->changeToStack(kIntroStack, 1, 0, 0);
309 }
310
o_menuOptions(uint16 var,const ArgumentsArray & args)311 void Menu::o_menuOptions(uint16 var, const ArgumentsArray &args) {
312 resetButtons();
313
314 _vm->runOptionsDialog();
315 }
316
o_menuQuit(uint16 var,const ArgumentsArray & args)317 void Menu::o_menuQuit(uint16 var, const ArgumentsArray &args) {
318 if (!showConfirmationDialog(_("Are you sure you want to quit? All unsaved progress will be lost."), _("Quit"),
319 _("Cancel"))) {
320 return;
321 }
322
323 _vm->changeToStack(kCreditsStack, 10000, 0, 0);
324 }
325
o_menuExit(uint16 var,const ArgumentsArray & args)326 void Menu::o_menuExit(uint16 var, const ArgumentsArray &args) {
327 if (_inGame) {
328 _vm->_gfx->restoreStateForMainMenu();
329 }
330
331 CursorMan.showMouse(_wasCursorVisible);
332
333 _pauseToken.clear();
334 }
335
o_playIntroMovies(uint16 var,const ArgumentsArray & args)336 void Menu::o_playIntroMovies(uint16 var, const ArgumentsArray &args) {
337 _introMoviesRunning = true;
338 _introStep = 0;
339 }
340
introMovies_run()341 void Menu::introMovies_run() {
342 // Play Intro Movies
343 // This is all quite messy...
344
345 VideoEntryPtr video;
346
347 switch (_introStep) {
348 case 0:
349 _introStep = 1;
350 video = _vm->playMovieFullscreen("broder", kIntroStack);
351 break;
352 case 1:
353 if (!_vm->_video->isVideoPlaying())
354 _introStep = 2;
355 break;
356 case 2:
357 _introStep = 3;
358 video = _vm->playMovieFullscreen("cyanlogo", kIntroStack);
359 break;
360 case 3:
361 if (!_vm->_video->isVideoPlaying())
362 _introStep = 4;
363 break;
364 default:
365 _vm->changeToCard(1000, kTransitionCopy);
366 }
367 }
368
showConfirmationDialog(const Common::U32String & message,const Common::U32String & confirmButton,const Common::U32String & cancelButton)369 bool Menu::showConfirmationDialog(const Common::U32String &message, const Common::U32String &confirmButton, const Common::U32String &cancelButton) {
370 if (!_inGame) {
371 return true;
372 }
373
374 resetButtons();
375
376 GUI::MessageDialog dialog(message, confirmButton, cancelButton);
377
378 return dialog.runModal() == GUI::kMessageOK;
379 }
380
resetButtons()381 void Menu::resetButtons() {
382 for (uint i = 0; i < ARRAYSIZE(_menuItemHovered); i++) {
383 _menuItemHovered[i] = false;
384 _vm->getCard()->redrawArea(1000 + i);
385 }
386
387 _vm->doFrame();
388 }
389
390
391 } // End of namespace MystStacks
392 } // End of namespace Mohawk
393