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 "common/scummsys.h"
24 #include "xeen/worldofxeen/worldofxeen_menu.h"
25 #include "xeen/worldofxeen/worldofxeen.h"
26 #include "xeen/dialogs/credits_screen.h"
27 #include "xeen/dialogs/dialogs_difficulty.h"
28 #include "xeen/resources.h"
29 
30 namespace Xeen {
31 namespace WorldOfXeen {
32 
show()33 void MainMenuContainer::show() {
34 	MainMenuContainer *menu;
35 
36 	switch (g_vm->getGameID()) {
37 	case GType_Clouds:
38 		menu = new CloudsMainMenuContainer();
39 		break;
40 	case GType_DarkSide:
41 		menu = new DarkSideMainMenuContainer();
42 		break;
43 	case GType_WorldOfXeen:
44 		if (g_vm->getIsCD())
45 			menu = new WorldOfXeenCDMainMenuContainer();
46 		else
47 			menu = new WorldOfXeenMainMenuContainer();
48 		break;
49 	default:
50 		error("Invalid game");
51 	}
52 
53 	menu->execute();
54 	delete menu;
55 }
56 
MainMenuContainer(const char * spritesName1,const char * spritesName2,const char * spritesName3)57 MainMenuContainer::MainMenuContainer(const char *spritesName1, const char *spritesName2, const char *spritesName3) :
58 		_animateCtr(0), _dialog(nullptr) {
59 	g_vm->_files->setGameCc(g_vm->getGameID() == GType_Clouds ? 0 : 1);
60 
61 	_backgroundSprites.resize(1 + (spritesName2 ? 1 : 0) + (spritesName3 ? 1 : 0));
62 	_backgroundSprites[0].load(spritesName1);
63 	if (spritesName2)
64 		_backgroundSprites[1].load(spritesName2);
65 	if (spritesName3)
66 		_backgroundSprites[2].load(spritesName3);
67 
68 	_frameCount = 0;
69 	for (uint idx = 0; idx < _backgroundSprites.size(); ++idx)
70 		_frameCount += _backgroundSprites[idx].size();
71 }
72 
~MainMenuContainer()73 MainMenuContainer::~MainMenuContainer() {
74 	delete _dialog;
75 	g_vm->_windows->closeAll();
76 	g_vm->_sound->stopAllAudio();
77 	g_vm->_events->clearEvents();
78 }
79 
draw()80 void MainMenuContainer::draw() {
81 	g_vm->_screen->restoreBackground();
82 	_animateCtr = (_animateCtr + 1) % _frameCount;
83 
84 	// Draw the next background frame
85 	uint frameNum = _animateCtr;
86 	for (uint idx = 0; idx < _backgroundSprites.size(); ++idx) {
87 		if (frameNum < _backgroundSprites[idx].size()) {
88 			_backgroundSprites[idx].draw(0, frameNum);
89 			return;
90 		} else {
91 			frameNum -= _backgroundSprites[idx].size();
92 		}
93 	}
94 }
95 
execute()96 void MainMenuContainer::execute() {
97 	EventsManager &events = *g_vm->_events;
98 	Screen &screen = *g_vm->_screen;
99 	bool showFlag = false;
100 
101 	// Show the cursor
102 	events.clearEvents();
103 	events.setCursor(0);
104 	events.showCursor();
105 
106 	screen.doScroll(true, false);
107 
108 	while (!g_vm->shouldExit() && g_vm->_gameMode == GMODE_NONE) {
109 		// Draw the menu
110 		draw();
111 		if (_dialog)
112 			_dialog->draw();
113 
114 		// Fade/scroll in screen if first frame showing screen
115 		if (!showFlag) {
116 			display();
117 			screen.doScroll(false, true);
118 			showFlag = true;
119 		}
120 
121 		// Check for events
122 		events.updateGameCounter();
123 
124 		if (events.wait(4, true)) {
125 			if (_dialog) {
126 				// There's a dialog active, so let it handle the event
127 				_dialog->handleEvents();
128 
129 				// If dialog was removed as a result of the event, flag screen for re-showing,
130 				// such as returning to main menu from the Credits screen
131 				if (!_dialog)
132 					showFlag = false;
133 			} else {
134 				// No active dialog. If Escape pressed, exit game entirely. Otherwise,
135 				// open up the main menu dialog
136 				PendingEvent pe;
137 				if (events.getEvent(pe) && pe._keyState.keycode == Common::KEYCODE_ESCAPE)
138 					g_vm->_gameMode = GMODE_QUIT;
139 
140 				events.clearEvents();
141 				showMenuDialog();
142 			}
143 		}
144 	}
145 }
146 
147 /*------------------------------------------------------------------------*/
148 
CloudsMainMenuContainer()149 CloudsMainMenuContainer::CloudsMainMenuContainer() : MainMenuContainer("intro.vga") {
150 }
151 
display()152 void CloudsMainMenuContainer::display() {
153 	FileManager &files = *g_vm->_files;
154 	Screen &screen = *g_vm->_screen;
155 	Sound &sound = *g_vm->_sound;
156 
157 	sound._musicSide = 0;
158 	files.setGameCc(0);
159 
160 	screen.loadPalette("mm4.pal");
161 	screen.loadBackground("intro.raw");
162 	screen.saveBackground();
163 
164 	if (!sound.isMusicPlaying())
165 		sound.playSong("inn.m");
166 }
167 
showMenuDialog()168 void CloudsMainMenuContainer::showMenuDialog() {
169 	setOwner(new CloudsMenuDialog(this));
170 }
171 
172 /*------------------------------------------------------------------------*/
173 
DarkSideMainMenuContainer()174 DarkSideMainMenuContainer::DarkSideMainMenuContainer() : MainMenuContainer("title2a.int") {
175 	Screen &screen = *g_vm->_screen;
176 	Sound &sound = *g_vm->_sound;
177 	screen.loadPalette("dark.pal");
178 	screen.fadeIn(0x81);
179 
180 	sound._musicSide = 1;
181 	sound.playSong("newbrigh.m");
182 
183 	_background.load("title2.int");
184 }
185 
display()186 void DarkSideMainMenuContainer::display() {
187 	FileManager &files = *g_vm->_files;
188 	Screen &screen = *g_vm->_screen;
189 	Sound &sound = *g_vm->_sound;
190 
191 	_background.draw(0, 0, Common::Point(0, 0));
192 	_background.draw(0, 1, Common::Point(160, 0));
193 
194 	sound._musicSide = 1;
195 	files.setGameCc(1);
196 
197 	screen.loadPalette("dark.pal");
198 	screen.saveBackground();
199 
200 	if (!sound.isMusicPlaying())
201 		sound.playSong("newbrigh.m");
202 }
203 
showMenuDialog()204 void DarkSideMainMenuContainer::showMenuDialog() {
205 	setOwner(new DarkSideMenuDialog(this));
206 }
207 
208 /*------------------------------------------------------------------------*/
209 
WorldOfXeenMainMenuContainer()210 WorldOfXeenMainMenuContainer::WorldOfXeenMainMenuContainer() : MainMenuContainer("world.int") {
211 }
212 
display()213 void WorldOfXeenMainMenuContainer::display() {
214 	FileManager &files = *g_vm->_files;
215 	Screen &screen = *g_vm->_screen;
216 	Sound &sound = *g_vm->_sound;
217 
218 	sound._musicSide = 1;
219 	files.setGameCc(1);
220 
221 	screen.loadPalette("dark.pal");
222 	screen.loadBackground("world.raw");
223 	screen.saveBackground();
224 
225 	if (!sound.isMusicPlaying())
226 		sound.playSong("newbrigh.m");
227 }
228 
showMenuDialog()229 void WorldOfXeenMainMenuContainer::showMenuDialog() {
230 	setOwner(new WorldMenuDialog(this));
231 }
232 
233 /*------------------------------------------------------------------------*/
234 
WorldOfXeenCDMainMenuContainer()235 WorldOfXeenCDMainMenuContainer::WorldOfXeenCDMainMenuContainer() : MainMenuContainer("world0.int", "world1.int", "world2.int") {
236 }
237 
display()238 void WorldOfXeenCDMainMenuContainer::display() {
239 	FileManager &files = *g_vm->_files;
240 	Screen &screen = *g_vm->_screen;
241 	Sound &sound = *g_vm->_sound;
242 
243 	sound._musicSide = 1;
244 	files.setGameCc(1);
245 
246 	screen.loadPalette("dark.pal");
247 	screen.loadBackground("world.raw");
248 	screen.saveBackground();
249 
250 	if (!sound.isMusicPlaying())
251 		sound.playSong("newbrigh.m");
252 }
253 
showMenuDialog()254 void WorldOfXeenCDMainMenuContainer::showMenuDialog() {
255 	setOwner(new WorldMenuDialog(this));
256 }
257 
258 /*------------------------------------------------------------------------*/
259 
handleEvents()260 bool MainMenuDialog::handleEvents() {
261 	FileManager &files = *g_vm->_files;
262 	checkEvents(g_vm);
263 	int difficulty;
264 
265 	switch (_buttonValue) {
266 	case Common::KEYCODE_s:
267 		// Start new game
268 		difficulty = DifficultyDialog::show(g_vm);
269 		if (difficulty == -1)
270 			return true;
271 
272 		// Load a new game state and set the difficulty
273 		g_vm->_saves->newGame();
274 		g_vm->_party->_difficulty = (Difficulty)difficulty;
275 		g_vm->_gameMode = GMODE_PLAY_GAME;
276 		break;
277 
278 	case Common::KEYCODE_l: {
279 		// Load existing game
280 		int ccNum = files._ccNum;
281 		g_vm->_saves->newGame();
282 		if (!g_vm->_saves->loadGame()) {
283 			files.setGameCc(ccNum);
284 			return true;
285 		}
286 
287 		g_vm->_gameMode = GMODE_PLAY_GAME;
288 		break;
289 	}
290 
291 	case Common::KEYCODE_c:
292 	case Common::KEYCODE_v:
293 		// Show credits
294 		CreditsScreen::show(g_vm);
295 		break;
296 
297 	case Common::KEYCODE_ESCAPE:
298 		// Exit dialog (returning to just the animated background)
299 		break;
300 
301 	default:
302 		return false;
303 	}
304 
305 	// If this point is reached, delete the dialog itself, which will return the main menu
306 	// to it's default "No dialog showing" state
307 	delete this;
308 	return true;
309 }
310 
311 /*------------------------------------------------------------------------*/
312 
CloudsMenuDialog(MainMenuContainer * owner)313 CloudsMenuDialog::CloudsMenuDialog(MainMenuContainer *owner) : MainMenuDialog(owner) {
314 	Windows &windows = *g_vm->_windows;
315 	Window &w = windows[GAME_WINDOW];
316 	w.setBounds(Common::Rect(72, 25, 248, g_vm->_gameWon[0] ? 175 : 150));
317 	w.open();
318 
319 	loadButtons();
320 }
321 
~CloudsMenuDialog()322 CloudsMenuDialog::~CloudsMenuDialog() {
323 	Windows &windows = *g_vm->_windows;
324 	Window &w = windows[GAME_WINDOW];
325 	w.close();
326 }
327 
loadButtons()328 void CloudsMenuDialog::loadButtons() {
329 	_buttonSprites.load("start.icn");
330 	addButton(Common::Rect(93, 53, 227, 73), Common::KEYCODE_s, &_buttonSprites);
331 	addButton(Common::Rect(93, 78, 227, 98), Common::KEYCODE_l, &_buttonSprites);
332 	addButton(Common::Rect(93, 103, 227, 123), Common::KEYCODE_c, &_buttonSprites);
333 	if (g_vm->_gameWon[0])
334 		addButton(Common::Rect(93, 128, 227, 148), Common::KEYCODE_e, &_buttonSprites);
335 }
336 
draw()337 void CloudsMenuDialog::draw() {
338 	Windows &windows = *g_vm->_windows;
339 	Window &w = windows[GAME_WINDOW];
340 
341 	w.frame();
342 	w.writeString(Common::String::format(Res.OPTIONS_MENU, Res.GAME_NAMES[0], g_vm->_gameWon[0] ? 117 : 92, 1992));
343 	drawButtons(&w);
344 }
345 
handleEvents()346 bool CloudsMenuDialog::handleEvents() {
347 	if (MainMenuDialog::handleEvents())
348 		return true;
349 
350 	switch (_buttonValue) {
351 	case Common::KEYCODE_e:
352 		if (g_vm->_gameWon[0]) {
353 			// Close the window
354 			delete this;
355 
356 			// Show clouds ending
357 			WOX_VM._sound->stopAllAudio();
358 			WOX_VM.showCloudsEnding(g_vm->_finalScore);
359 			return true;
360 		}
361 		break;
362 
363 	default:
364 		break;
365 	}
366 
367 	return false;
368 }
369 
370 /*------------------------------------------------------------------------*/
371 
DarkSideMenuDialog(MainMenuContainer * owner)372 DarkSideMenuDialog::DarkSideMenuDialog(MainMenuContainer *owner) : MainMenuDialog(owner), _firstDraw(true) {
373 	Windows &windows = *g_vm->_windows;
374 	Window &w = windows[GAME_WINDOW];
375 	w.setBounds(Common::Rect(72, 25, 248, 150));
376 	w.open();
377 
378 	loadButtons();
379 }
380 
~DarkSideMenuDialog()381 DarkSideMenuDialog::~DarkSideMenuDialog() {
382 	Windows &windows = *g_vm->_windows;
383 	Window &w = windows[GAME_WINDOW];
384 	w.close();
385 }
386 
loadButtons()387 void DarkSideMenuDialog::loadButtons() {
388 	addButton(Common::Rect(124, 87, 177, 97), Common::KEYCODE_s);
389 	addButton(Common::Rect(126, 98, 173, 108), Common::KEYCODE_l);
390 	addButton(Common::Rect(91, 110, 209, 120), Common::KEYCODE_c);
391 	addButton(Common::Rect(85, 121, 216, 131), Common::KEYCODE_o);
392 }
393 
draw()394 void DarkSideMenuDialog::draw() {
395 	Screen &screen = *g_vm->_screen;
396 	EventsManager &events = *g_vm->_events;
397 	Sound &sound = *g_vm->_sound;
398 	Windows &windows = *g_vm->_windows;
399 
400 	if (!_firstDraw)
401 		return;
402 
403 	SpriteResource kludgeSprites("kludge.int");
404 	SpriteResource title2Sprites[8] = {
405 		SpriteResource("title2b.int"), SpriteResource("title2c.int"),
406 		SpriteResource("title2d.int"), SpriteResource("title2e.int"),
407 		SpriteResource("title2f.int"), SpriteResource("title2g.int"),
408 		SpriteResource("title2h.int"), SpriteResource("title2i.int"),
409 	};
410 
411 	screen.loadBackground("title2b.raw");
412 	kludgeSprites.draw(0, 0, Common::Point(85, 86));
413 	screen.saveBackground();
414 	sound.playSound("elect.voc");
415 
416 	for (int i = 0; i < 30 && !g_vm->shouldExit(); ++i) {
417 		events.updateGameCounter();
418 		screen.restoreBackground();
419 		title2Sprites[i / 4].draw(0, i % 4);
420 		windows[0].update();
421 
422 		if (i == 19)
423 			sound.stopSound();
424 
425 		if (events.wait(2))
426 			break;
427 	}
428 
429 	events.clearEvents();
430 	sound.stopSound();
431 
432 	screen.restoreBackground();
433 	windows[0].update();
434 	_firstDraw = false;
435 }
436 
handleEvents()437 bool DarkSideMenuDialog::handleEvents() {
438 	if (MainMenuDialog::handleEvents())
439 		return true;
440 
441 	switch (_buttonValue) {
442 	case Common::KEYCODE_o: {
443 		// Show other options dialog
444 		// Remove this dialog
445 		MainMenuContainer *owner = _owner;
446 		delete this;
447 
448 		// Set the new options dialog
449 		owner->setOwner(new OtherOptionsDialog(owner));
450 		return true;
451 	}
452 
453 	default:
454 		break;
455 	}
456 
457 	return false;
458 }
459 
460 /*------------------------------------------------------------------------*/
461 
WorldMenuDialog(MainMenuContainer * owner)462 WorldMenuDialog::WorldMenuDialog(MainMenuContainer *owner) : MainMenuDialog(owner) {
463 	Windows &windows = *g_vm->_windows;
464 	Window &w = windows[GAME_WINDOW];
465 	w.setBounds(Common::Rect(72, 25, 248, 175));
466 	w.open();
467 
468 	loadButtons();
469 }
470 
~WorldMenuDialog()471 WorldMenuDialog::~WorldMenuDialog() {
472 	Windows &windows = *g_vm->_windows;
473 	Window &w = windows[GAME_WINDOW];
474 	w.close();
475 }
476 
loadButtons()477 void WorldMenuDialog::loadButtons() {
478 	_buttonSprites.load("start.icn");
479 	addButton(Common::Rect(93, 53, 227, 73), Common::KEYCODE_s, &_buttonSprites);
480 	addButton(Common::Rect(93, 78, 227, 98), Common::KEYCODE_l, &_buttonSprites);
481 	addButton(Common::Rect(93, 103, 227, 123), Common::KEYCODE_c, &_buttonSprites);
482 	addButton(Common::Rect(93, 128, 227, 148), Common::KEYCODE_o, &_buttonSprites);
483 }
484 
draw()485 void WorldMenuDialog::draw() {
486 	Windows &windows = *g_vm->_windows;
487 	Window &w = windows[GAME_WINDOW];
488 
489 	w.frame();
490 	w.writeString(Common::String::format(Res.OPTIONS_MENU, Res.GAME_NAMES[2], 117, 1993));
491 	drawButtons(&w);
492 }
493 
handleEvents()494 bool WorldMenuDialog::handleEvents() {
495 	if (MainMenuDialog::handleEvents())
496 		return true;
497 
498 	switch (_buttonValue) {
499 	case Common::KEYCODE_o: {
500 		// Show other options dialog
501 		// Remove this dialog
502 		MainMenuContainer *owner = _owner;
503 		delete this;
504 
505 		// Set the new options dialog
506 		owner->setOwner(new OtherOptionsDialog(owner));
507 		return true;
508 	}
509 
510 	default:
511 		break;
512 	}
513 
514 	return false;
515 }
516 
517 /*------------------------------------------------------------------------*/
518 
OtherOptionsDialog(MainMenuContainer * owner)519 OtherOptionsDialog::OtherOptionsDialog(MainMenuContainer *owner) : MenuContainerDialog(owner) {
520 	Windows &windows = *g_vm->_windows;
521 	Window &w = windows[GAME_WINDOW];
522 
523 	int height = (g_vm->getGameID() == GType_WorldOfXeen ? 25 : 0)
524 		+ (g_vm->getGameID() == GType_WorldOfXeen && g_vm->_gameWon[0] ? 25 : 0)
525 		+ (g_vm->_gameWon[1] ? 25 : 0)
526 		+ (g_vm->_gameWon[2] ? 25 : 0)
527 		+ 75;
528 
529 	w.setBounds(Common::Rect(72, 25, 248, 25 + height));
530 	w.open();
531 
532 	loadButtons();
533 }
534 
~OtherOptionsDialog()535 OtherOptionsDialog::~OtherOptionsDialog() {
536 	Windows &windows = *g_vm->_windows;
537 	Window &w = windows[GAME_WINDOW];
538 	w.close();
539 }
540 
loadButtons()541 void OtherOptionsDialog::loadButtons() {
542 	_buttonSprites.load("special.icn");
543 	Common::Rect r(93, 53, 227, 73);
544 
545 	// View Darkside Intro
546 	addButton(r, Common::KEYCODE_d, &_buttonSprites);
547 	r.translate(0, 25);
548 
549 	// View Clouds Intro
550 	if (g_vm->getGameID() == GType_WorldOfXeen) {
551 		addButton(r, Common::KEYCODE_c, &_buttonSprites);
552 		r.translate(0, 25);
553 	} else {
554 		addButton(Common::Rect(), Common::KEYCODE_INVALID);
555 	}
556 
557 	// View Darkside End
558 	if (g_vm->_gameWon[1]) {
559 		addButton(r, Common::KEYCODE_e, &_buttonSprites);
560 		r.translate(0, 25);
561 	} else {
562 		addButton(Common::Rect(), Common::KEYCODE_INVALID);
563 	}
564 
565 	// View Clouds End
566 	if (g_vm->_gameWon[0]) {
567 		addButton(r, Common::KEYCODE_v, &_buttonSprites);
568 		r.translate(0, 25);
569 	} else {
570 		addButton(Common::Rect(), Common::KEYCODE_INVALID);
571 	}
572 
573 	// View World End
574 	if (g_vm->_gameWon[2]) {
575 		addButton(r, Common::KEYCODE_w, &_buttonSprites);
576 	} else {
577 		addButton(Common::Rect(), Common::KEYCODE_INVALID);
578 	}
579 }
580 
draw()581 void OtherOptionsDialog::draw() {
582 	Windows &windows = *g_vm->_windows;
583 	Window &w = windows[GAME_WINDOW];
584 
585 	w.frame();
586 	w.writeString(Common::String::format(Res.OPTIONS_MENU,
587 		Res.GAME_NAMES[g_vm->getGameID() == GType_WorldOfXeen ? 2 : 1],
588 		w.getBounds().height() - 33, 1993));
589 	drawButtons(&w);
590 }
591 
handleEvents()592 bool OtherOptionsDialog::handleEvents() {
593 	Sound &sound = *g_vm->_sound;
594 	checkEvents(g_vm);
595 
596 	switch (_buttonValue) {
597 	case Common::KEYCODE_d:
598 		delete this;
599 		sound.stopAllAudio();
600 		WOX_VM.showDarkSideIntro(false);
601 		break;
602 
603 	case Common::KEYCODE_c:
604 		if (g_vm->getGameID() == GType_WorldOfXeen) {
605 			delete this;
606 			sound.stopAllAudio();
607 			WOX_VM.showCloudsIntro();
608 		}
609 		break;
610 
611 	case Common::KEYCODE_e:
612 		if (g_vm->_gameWon[1]) {
613 			delete this;
614 			sound.stopAllAudio();
615 			WOX_VM.showDarkSideEnding(g_vm->_finalScore);
616 		}
617 		break;
618 
619 	case Common::KEYCODE_v:
620 		if (g_vm->_gameWon[0]) {
621 			delete this;
622 			sound.stopAllAudio();
623 			WOX_VM.showCloudsEnding(g_vm->_finalScore);
624 		}
625 		break;
626 
627 	case Common::KEYCODE_w:
628 		if (g_vm->_gameWon[2]) {
629 			delete this;
630 			sound.stopAllAudio();
631 			WOX_VM.showWorldOfXeenEnding(NON_GOOBER, g_vm->_finalScore);
632 		}
633 		break;
634 
635 	case Common::KEYCODE_ESCAPE:
636 		// Exit dialog
637 		delete this;
638 		break;
639 
640 	default:
641 		return false;
642 	}
643 
644 	return true;
645 }
646 
647 } // End of namespace WorldOfXeen
648 } // End of namespace Xeen
649