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 /*
24  * This code is based on original Mortville Manor DOS source code
25  * Copyright (c) 1987-1989 Lankhor
26  */
27 
28 #include "mortevielle/mortevielle.h"
29 
30 #include "mortevielle/dialogs.h"
31 #include "mortevielle/menu.h"
32 #include "mortevielle/mouse.h"
33 #include "mortevielle/outtext.h"
34 
35 #include "common/scummsys.h"
36 #include "graphics/cursorman.h"
37 
38 namespace Mortevielle {
39 
40 /**
41  * Check is a key was pressed
42  * It also delays the engine and check if the screen has to be updated
43  * @remarks	Originally called 'keypressed'
44  */
keyPressed()45 bool MortevielleEngine::keyPressed() {
46 	// Check for any pending key presses
47 	handleEvents();
48 
49 	// Check if it's time to draw the next frame
50 	if (g_system->getMillis() > (_lastGameFrame + GAME_FRAME_DELAY)) {
51 		_lastGameFrame = g_system->getMillis();
52 
53 		_screenSurface->updateScreen();
54 
55 		_debugger->onFrame();
56 	}
57 
58 	// Delay briefly to keep CPU usage down
59 	g_system->delayMillis(5);
60 
61 	// Return if there are any pending key presses
62 	return !_keypresses.empty();
63 }
64 
65 /**
66  * Wait for a keypress
67  * @remarks	Originally called 'get_ch'
68  */
getChar()69 int MortevielleEngine::getChar() {
70 	bool end = false;
71 	// If there isn't any pending keypress, wait until there is
72 	while (!shouldQuit() && !end) {
73 		end = keyPressed();
74 	}
75 
76 	// Return the top keypress
77 	return shouldQuit() ? 0 : _keypresses.pop();
78 }
79 
80 /**
81  * Handle pending events
82  * @remarks		Since the ScummVM screen surface is double height to handle 640x200 using 640x400,
83  * the mouse Y position is divided by 2 to keep the game thinking the Y goes from 0 - 199
84  */
handleEvents()85 bool MortevielleEngine::handleEvents() {
86 	Common::Event event;
87 	if (!g_system->getEventManager()->pollEvent(event))
88 		return false;
89 
90 	switch (event.type) {
91 	case Common::EVENT_LBUTTONDOWN:
92 	case Common::EVENT_LBUTTONUP:
93 	case Common::EVENT_MOUSEMOVE:
94 		_mousePos = Common::Point(event.mouse.x, event.mouse.y / 2);
95 		_mouse->_pos.x = event.mouse.x;
96 		_mouse->_pos.y = event.mouse.y / 2;
97 
98 		if (event.type == Common::EVENT_LBUTTONDOWN)
99 			_mouseClick = true;
100 		else if (event.type == Common::EVENT_LBUTTONUP)
101 			_mouseClick = false;
102 
103 		break;
104 	case Common::EVENT_KEYDOWN:
105 		addKeypress(event);
106 		break;
107 	default:
108 		break;
109 	}
110 
111 	return true;
112 }
113 
114 /**
115  * Add the specified key to the pending keypress stack
116  */
addKeypress(Common::Event & evt)117 void MortevielleEngine::addKeypress(Common::Event &evt) {
118 	// Character to add
119 	char ch = evt.kbd.ascii;
120 
121 	// Check for debugger
122 	if ((evt.kbd.keycode == Common::KEYCODE_d) && (evt.kbd.flags & Common::KBD_CTRL)) {
123 		// Attach to the debugger
124 		_debugger->attach();
125 		_debugger->onFrame();
126 	} else if ((evt.kbd.keycode >= Common::KEYCODE_a) && (evt.kbd.keycode <= Common::KEYCODE_z)) {
127 		// Handle alphabetic keys
128 		if (evt.kbd.hasFlags(Common::KBD_CTRL))
129 			ch = evt.kbd.keycode - Common::KEYCODE_a + 1;
130 		else
131 			ch = evt.kbd.keycode - Common::KEYCODE_a + 'A';
132 	} else if ((evt.kbd.keycode >= Common::KEYCODE_F1) && (evt.kbd.keycode <= Common::KEYCODE_F12)) {
133 		// Handle function keys
134 		ch = 59 + evt.kbd.keycode - Common::KEYCODE_F1;
135 	} else {
136 		// Series of special cases
137 		switch (evt.kbd.keycode) {
138 		case Common::KEYCODE_KP4:
139 		case Common::KEYCODE_LEFT:
140 			ch = '4';
141 			break;
142 		case Common::KEYCODE_KP2:
143 		case Common::KEYCODE_DOWN:
144 			ch = '2';
145 			break;
146 		case Common::KEYCODE_KP6:
147 		case Common::KEYCODE_RIGHT:
148 			ch = '6';
149 			break;
150 		case Common::KEYCODE_KP8:
151 		case Common::KEYCODE_UP:
152 			ch = '8';
153 			break;
154 		case Common::KEYCODE_KP7:
155 			ch = '7';
156 			break;
157 		case Common::KEYCODE_KP1:
158 			ch = '1';
159 			break;
160 		case Common::KEYCODE_KP9:
161 			ch = '9';
162 			break;
163 		case Common::KEYCODE_KP3:
164 			ch = '3';
165 			break;
166 		case Common::KEYCODE_KP5:
167 			ch = '5';
168 			break;
169 		case Common::KEYCODE_RETURN:
170 			ch = '\13';
171 			break;
172 		case Common::KEYCODE_ESCAPE:
173 			ch = '\33';
174 			break;
175 		default:
176 			break;
177 		}
178 	}
179 
180 	if (ch != 0)
181 		_keypresses.push(ch);
182 }
183 
184 
185 static const byte CURSOR_ARROW_DATA[16 * 16] = {
186 	0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
187 	0x0f, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
188 	0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
189 	0x0f, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
190 	0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
191 	0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
192 	0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
193 	0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
194 	0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
195 	0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff,
196 	0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
197 	0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
198 	0x0f, 0x0f, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
199 	0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
200 	0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
201 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
202 };
203 
204 /**
205  * Initialize the mouse
206  */
initMouse()207 void MortevielleEngine::initMouse() {
208 	CursorMan.replaceCursor(CURSOR_ARROW_DATA, 16, 16, 0, 0, 0xff);
209 	CursorMan.showMouse(true);
210 
211 	_mouse->initMouse();
212 }
213 
214 /**
215  * Sets the mouse position
216  * @remarks		Since the ScummVM screen surface is double height to handle 640x200 using 640x400,
217  * the mouse Y position is doubled to convert from 0-199 to 0-399
218  */
setMousePos(const Common::Point & pt)219 void MortevielleEngine::setMousePos(const Common::Point &pt) {
220 	// Adjust the passed position from simulated 640x200 to 640x400 co-ordinates
221 	Common::Point newPoint(pt.x, (pt.y == 199) ? 399 : pt.y * 2);
222 
223 	if (newPoint != _mousePos)
224 		// Warp the mouse to the new position
225 		g_system->warpMouse(newPoint.x, newPoint.y);
226 
227 	// Save the new position
228 	_mousePos = newPoint;
229 }
230 
231 /**
232  * Delay by a given amount
233  */
delay(int amount)234 void MortevielleEngine::delay(int amount) {
235 	uint32 endTime = g_system->getMillis() + amount;
236 
237 	g_system->showMouse(false);
238 	while (g_system->getMillis() < endTime) {
239 		if (g_system->getMillis() > (_lastGameFrame + GAME_FRAME_DELAY)) {
240 			_lastGameFrame = g_system->getMillis();
241 			_screenSurface->updateScreen();
242 
243 			_debugger->onFrame();
244 		}
245 
246 		g_system->delayMillis(10);
247 	}
248 	g_system->showMouse(true);
249 }
250 
251 /**
252  * Waits for the user to select an action, and then handles it
253  * @remarks	Originally called tecran
254  */
handleAction()255 void MortevielleEngine::handleAction() {
256 	const int lim = 20000;
257 	int temps = 0;
258 	char inkey = '\0';
259 	bool funct = false;
260 
261 	clearVerbBar();
262 
263 	_controlMenu = 0;
264 	if (!_keyPressedEsc) {
265 		_menu->drawMenu();
266 		_menu->_menuDisplayed = true;
267 		temps = 0;
268 		_key = 0;
269 		funct = false;
270 		inkey = '.';
271 
272 		_inMainGameLoop = true;
273 		do {
274 			_menu->updateMenu();
275 			prepareRoom();
276 			_mouse->moveMouse(funct, inkey);
277 			if (shouldQuit())
278 				return;
279 			++temps;
280 			if (keyPressed() || _mouseClick) {
281 				_soundManager->_mixer->stopHandle(_soundManager->_soundHandle);
282 			}
283 		} while (!((_menu->_menuSelected) || (temps > lim) || (funct) || (_anyone)));
284 		_inMainGameLoop = false;
285 
286 		_menu->eraseMenu();
287 		_menu->_menuDisplayed = false;
288 		if (_menu->_menuSelected && (_currMenu == MENU_SAVE)) {
289 			Common::String saveName = Common::String::format("Savegame #%d", _currAction & 15);
290 			_savegameManager->saveGame(_currAction & 15, saveName);
291 		}
292 		if (_menu->_menuSelected && (_currMenu == MENU_LOAD))
293 			_savegameManager->loadGame((_currAction & 15) - 1);
294 		if (inkey == '\103') {       /* F9 */
295 			temps = _dialogManager->show(_hintPctMessage);
296 			return;
297 		} else if (inkey == '\77') {
298 			if ((_menuOpcode != OPCODE_NONE) && ((_currMenu == MENU_ACTION) || (_currMenu == MENU_SELF))) {
299 				_currAction = _menuOpcode;
300 				displayTextInVerbBar(getEngineString(S_IDEM));
301 			} else
302 				return;
303 		} else if (inkey == '\104') {
304 			if ((_x != 0) && (_y != 0))
305 				_num = 9999;
306 			return;
307 		}
308 	}
309 	if (inkey == '\73') {
310 		_quitGame = true;
311 		hourToChar();
312 	} else {
313 		if ((funct) && (inkey != '\77'))
314 			return;
315 		if (temps > lim) {
316 			handleDescriptionText(2, 141);
317 			if (_num == 9999)
318 				_num = 0;
319 		} else {
320 			_menuOpcode = _currMenu;
321 			if ((_currMenu == MENU_ACTION) || (_currMenu == MENU_SELF))
322 				_menuOpcode = _currAction;
323 			bool handledOpcodeFl = false;
324 			if (!_anyone) {
325 				if ((_heroSearching) || (_obpart)) {
326 					if (_mouse->_pos.y < 12)
327 						return;
328 
329 					if ((_currAction == _menu->_opcodeSound) || (_currAction == _menu->_opcodeLift)) {
330 						handledOpcodeFl = true;
331 						if ((_currAction == _menu->_opcodeLift) || (_obpart)) {
332 							endSearch();
333 							_caff = _coreVar._currPlace;
334 							_crep = 998;
335 						} else
336 							prepareNextObject();
337 						menuUp();
338 					}
339 				}
340 			}
341 			do {
342 				if (!handledOpcodeFl)
343 					handleOpcode();
344 
345 				if ((_controlMenu == 0) && (! _loseGame) && (! _endGame)) {
346 					_text->taffich();
347 					if (_destinationOk) {
348 						_destinationOk = false;
349 						drawPicture();
350 					}
351 					if ((!_syn) || (_col))
352 						handleDescriptionText(2, _crep);
353 				}
354 			} while (_syn);
355 			if (_controlMenu != 0)
356 				displayControlMenu();
357 		}
358 	}
359 }
360 
361 /**
362  * Engine function - Init Places
363  * @remarks	Originally called 'init_lieu'
364  */
loadPlaces()365 void MortevielleEngine::loadPlaces() {
366 	Common::File f;
367 
368 	if (!f.open("MXX.mor"))
369 		if (!f.open("MFXX.mor"))
370 			error("Missing file - MXX.mor");
371 
372 	for (int i = 0; i < 7; ++i) {
373 		for (int j = 0; j < 25; ++j)
374 			_destinationArray[i][j] = f.readByte();
375 	}
376 
377 	f.close();
378 }
379 
380 /**
381  * Set Text Color
382  * @remarks	Originally called 'text_color'
383  */
setTextColor(int col)384 void MortevielleEngine::setTextColor(int col) {
385 	_textColor = col;
386 }
387 
388 /**
389  * Prepare screen - Type 1!
390  * @remarks	Originally called 'ecrf1'
391  */
prepareScreenType1()392 void MortevielleEngine::prepareScreenType1() {
393 	// Large drawing
394 	_screenSurface->drawBox(0, 11, 512, 163, 15);
395 }
396 
397 /**
398  * Prepare room - Type 2!
399  * @remarks	Originally called 'ecrf2'
400  */
prepareScreenType2()401 void MortevielleEngine::prepareScreenType2() {
402 	setTextColor(5);
403 }
404 
405 /**
406  * Prepare room - Type 3!
407  * @remarks	Originally called 'ecrf7'
408  */
prepareScreenType3()409 void MortevielleEngine::prepareScreenType3() {
410 	setTextColor(4);
411 }
412 
413 /**
414  * Engine function - Update hour
415  * @remarks	Originally called 'calch'
416  */
updateHour(int & day,int & hour,int & minute)417 void MortevielleEngine::updateHour(int &day, int &hour, int &minute) {
418 	int newTime = readclock();
419 	int th = _currentHourCount + ((newTime - _currentTime) / _inGameHourDuration);
420 	minute = ((th % 2) + _currHalfHour) * 30;
421 	hour = ((uint)th >> 1) + _currHour;
422 	if (minute == 60) {
423 		minute = 0;
424 		++hour;
425 	}
426 	day = (hour / 24) + _currDay;
427 	hour = hour - ((day - _currDay) * 24);
428 }
429 
430 /**
431  * Engine function - Convert character index to bit index
432  * @remarks	Originally called 'conv'
433  */
convertCharacterIndexToBitIndex(int characterIndex)434 int MortevielleEngine::convertCharacterIndexToBitIndex(int characterIndex) {
435 	return 128 >> (characterIndex - 1);
436 }
437 
438 /**
439  * Engine function - Convert bit index to character index
440  * @remarks	Originally called 'tip'
441  */
convertBitIndexToCharacterIndex(int bitIndex)442 int MortevielleEngine::convertBitIndexToCharacterIndex(int bitIndex) {
443 	int retVal = 0;
444 
445 	if (bitIndex == 128)
446 		retVal = 1;
447 	else if (bitIndex == 64)
448 		retVal = 2;
449 	else if (bitIndex == 32)
450 		retVal = 3;
451 	else if (bitIndex == 16)
452 		retVal = 4;
453 	else if (bitIndex == 8)
454 		retVal = 5;
455 	else if (bitIndex == 4)
456 		retVal = 6;
457 	else if (bitIndex == 2)
458 		retVal = 7;
459 	else if (bitIndex == 1)
460 		retVal = 8;
461 
462 	return retVal;
463 }
464 
465 /**
466  * Engine function - Reset presence in other rooms
467  * @remarks	Originally called 't5'
468  */
resetPresenceInRooms(int roomId)469 void MortevielleEngine::resetPresenceInRooms(int roomId) {
470 	if (roomId == DINING_ROOM)
471 		_outsideOnlyFl = false;
472 
473 	if (roomId != GREEN_ROOM) {
474 		_roomPresenceLuc = false;
475 		_roomPresenceIda = false;
476 	}
477 
478 	if (roomId != PURPLE_ROOM)
479 		_purpleRoomPresenceLeo = false;
480 
481 	if (roomId != DARKBLUE_ROOM) {
482 		_roomPresenceGuy = false;
483 		_roomPresenceEva = false;
484 	}
485 
486 	if (roomId != BLUE_ROOM)
487 		_roomPresenceMax = false;
488 	if (roomId != RED_ROOM)
489 		_roomPresenceBob = false;
490 	if (roomId != GREEN_ROOM2)
491 		_roomPresencePat = false;
492 	if (roomId != TOILETS)
493 		_toiletsPresenceBobMax = false;
494 	if (roomId != BATHROOM)
495 		_bathRoomPresenceBobMax = false;
496 	if (roomId != JULIA_ROOM)
497 		_juliaRoomPresenceLeo = false;
498 }
499 
500 /**
501  * Engine function - Show the people present in the given room
502  * @remarks	Originally called 'affper'
503  */
showPeoplePresent(int bitIndex)504 void MortevielleEngine::showPeoplePresent(int bitIndex) {
505 	int xp = 580 - (_screenSurface->getStringWidth("LEO") / 2);
506 
507 	for (int i = 1; i <= 8; ++i)
508 		_menu->disableMenuItem(_menu->_discussMenu[i]);
509 
510 	clearUpperRightPart();
511 	if ((bitIndex & 128) == 128) {
512 		_screenSurface->putxy(xp, 24);
513 		_screenSurface->drawString("LEO", 4);
514 		_menu->enableMenuItem(_menu->_discussMenu[1]);
515 	}
516 	if ((bitIndex & 64) == 64) {
517 		_screenSurface->putxy(xp, 32);
518 		_screenSurface->drawString("PAT", 4);
519 		_menu->enableMenuItem(_menu->_discussMenu[2]);
520 	}
521 	if ((bitIndex & 32) == 32) {
522 		_screenSurface->putxy(xp, 40);
523 		_screenSurface->drawString("GUY", 4);
524 		_menu->enableMenuItem(_menu->_discussMenu[3]);
525 	}
526 	if ((bitIndex & 16) == 16) {
527 		_screenSurface->putxy(xp, 48);
528 		_screenSurface->drawString("EVA", 4);
529 		_menu->enableMenuItem(_menu->_discussMenu[4]);
530 	}
531 	if ((bitIndex & 8) == 8) {
532 		_screenSurface->putxy(xp, 56);
533 		_screenSurface->drawString("BOB", 4);
534 		_menu->enableMenuItem(_menu->_discussMenu[5]);
535 	}
536 	if ((bitIndex & 4) == 4) {
537 		_screenSurface->putxy(xp, 64);
538 		_screenSurface->drawString("LUC", 4);
539 		_menu->enableMenuItem(_menu->_discussMenu[6]);
540 	}
541 	if ((bitIndex & 2) == 2) {
542 		_screenSurface->putxy(xp, 72);
543 		_screenSurface->drawString("IDA", 4);
544 		_menu->enableMenuItem(_menu->_discussMenu[7]);
545 	}
546 	if ((bitIndex & 1) == 1) {
547 		_screenSurface->putxy(xp, 80);
548 		_screenSurface->drawString("MAX", 4);
549 		_menu->enableMenuItem(_menu->_discussMenu[8]);
550 	}
551 	_currBitIndex = bitIndex;
552 }
553 
554 /**
555  * Engine function - Select random characters
556  * @remarks	Originally called 'choix'
557  */
selectCharacters(int min,int max)558 int MortevielleEngine::selectCharacters(int min, int max) {
559 	bool invertSelection = false;
560 	int rand = getRandomNumber(min, max);
561 
562 	if (rand > 4) {
563 		rand = 8 - rand;
564 		invertSelection = true;
565 	}
566 
567 	int i = 0;
568 	int retVal = 0;
569 	while (i < rand) {
570 		int charIndex = getRandomNumber(1, 8);
571 		int charBitIndex = convertCharacterIndexToBitIndex(charIndex);
572 		if ((retVal & charBitIndex) != charBitIndex) {
573 			++i;
574 			retVal |= charBitIndex;
575 		}
576 	}
577 	if (invertSelection)
578 		retVal = 255 - retVal;
579 
580 	return retVal;
581 }
582 
583 /**
584  * Engine function - Get Presence Statistics - Green Room
585  * @remarks	Originally called 'cpl1'
586  */
getPresenceStatsGreenRoom()587 int MortevielleEngine::getPresenceStatsGreenRoom() {
588 	int day, hour, minute;
589 	int retVal = 0;
590 
591 	updateHour(day, hour, minute);
592 	// The original uses an || instead of an &&, resulting
593 	// in an always true condition. Based on the other tests,
594 	// and on other scenes, we use an && instead.
595 	if ((hour > 7) && (hour < 11))
596 		retVal = 25;
597 	else if ((hour > 10) && (hour < 14))
598 		retVal = 35;
599 	else if ((hour > 13) && (hour < 16))
600 		retVal = 50;
601 	else if ((hour > 15) && (hour < 18))
602 		retVal = 5;
603 	else if ((hour > 17) && (hour < 22))
604 		retVal = 35;
605 	else if ((hour > 21) && (hour < 24))
606 		retVal = 50;
607 	else if ((hour >= 0) && (hour < 8))
608 		retVal = 70;
609 
610 	_menu->updateMenu();
611 
612 	return retVal;
613 }
614 /**
615  * Engine function - Get Presence Statistics - Purple Room
616  * @remarks	Originally called 'cpl2'
617  */
getPresenceStatsPurpleRoom()618 int MortevielleEngine::getPresenceStatsPurpleRoom() {
619 	int day, hour, minute;
620 	int retVal = 0;
621 
622 	updateHour(day, hour, minute);
623 	if ((hour > 7) && (hour < 11))
624 		retVal = -2;
625 	else if (hour == 11)
626 		retVal = 100;
627 	else if ((hour > 11) && (hour < 23))
628 		retVal = 10;
629 	else if (hour == 23)
630 		retVal = 20;
631 	else if ((hour >= 0) && (hour < 8))
632 		retVal = 50;
633 
634 	return retVal;
635 }
636 
637 /**
638  * Engine function - Get Presence Statistics - Toilets
639  * @remarks	Originally called 'cpl3'
640  */
getPresenceStatsToilets()641 int MortevielleEngine::getPresenceStatsToilets() {
642 	int day, hour, minute;
643 	int retVal = 0;
644 
645 	updateHour(day, hour, minute);
646 	if (((hour > 8) && (hour < 10)) || ((hour > 19) && (hour < 24)))
647 		retVal = 34;
648 	else if (((hour > 9) && (hour < 20)) || ((hour >= 0) && (hour < 9)))
649 		retVal = 0;
650 
651 	return retVal;
652 }
653 
654 /**
655  * Engine function - Get Presence Statistics - Blue Room
656  * @remarks	Originally called 'cpl5'
657  */
getPresenceStatsBlueRoom()658 int MortevielleEngine::getPresenceStatsBlueRoom() {
659 	int day, hour, minute;
660 	int retVal = 0;
661 
662 	updateHour(day, hour, minute);
663 	if ((hour > 6) && (hour < 10))
664 		retVal = 0;
665 	else if (hour == 10)
666 		retVal = 100;
667 	else if ((hour > 10) && (hour < 24))
668 		retVal = 15;
669 	else if ((hour >= 0) && (hour < 7))
670 		retVal = 50;
671 
672 	return retVal;
673 }
674 
675 /**
676  * Engine function - Get Presence Statistics - Red Room
677  * @remarks	Originally called 'cpl6'
678  */
getPresenceStatsRedRoom()679 int MortevielleEngine::getPresenceStatsRedRoom() {
680 	int day, hour, minute;
681 	int retVal = 0;
682 
683 	updateHour(day, hour, minute);
684 	if (((hour > 7) && (hour < 13)) || ((hour > 17) && (hour < 20)))
685 		retVal = -2;
686 	else if (((hour > 12) && (hour < 17)) || ((hour > 19) && (hour < 24)))
687 		retVal = 35;
688 	else if (hour == 17)
689 		retVal = 100;
690 	else if ((hour >= 0) && (hour < 8))
691 		retVal = 60;
692 
693 	return retVal;
694 }
695 
696 /**
697  * Shows the "you are alone" message in the status area
698  * on the right hand side of the screen
699  * @remarks	Originally called 'person'
700  */
displayAloneText()701 void MortevielleEngine::displayAloneText() {
702 	for (int i = 1; i <= 8; ++i)
703 		_menu->disableMenuItem(_menu->_discussMenu[i]);
704 
705 	Common::String sYou = getEngineString(S_YOU);
706 	Common::String sAre = getEngineString(S_ARE);
707 	Common::String sAlone = getEngineString(S_ALONE);
708 
709 	clearUpperRightPart();
710 	_screenSurface->putxy(560, 30);
711 	_screenSurface->drawString(sYou, 4);
712 	_screenSurface->putxy(560, 50);
713 	_screenSurface->drawString(sAre, 4);
714 	_screenSurface->putxy(560, 70);
715 	_screenSurface->drawString(sAlone, 4);
716 
717 	_currBitIndex = 0;
718 }
719 
720 /**
721  * Engine function - Get Presence Statistics - Room Bureau
722  * @remarks	Originally called 'cpl10'
723  */
getPresenceStatsDiningRoom(int & hour)724 int MortevielleEngine::getPresenceStatsDiningRoom(int &hour) {
725 	int day, minute;
726 
727 	int retVal = 0;
728 	updateHour(day, hour, minute);
729 	if (((hour > 7) && (hour < 11)) || ((hour > 11) && (hour < 14)) || ((hour > 18) && (hour < 21)))
730 		retVal = 100;
731 	else if ((hour == 11) || ((hour > 20) && (hour < 24)))
732 		retVal = 45;
733 	else if (((hour > 13) && (hour < 17)) || (hour == 18))
734 		retVal = 35;
735 	else if (hour == 17)
736 		retVal = 60;
737 	else if ((hour >= 0) && (hour < 8))
738 		retVal = 5;
739 
740 	return retVal;
741 }
742 
743 /**
744  * Engine function - Get Presence Statistics - Room Bureau
745  * @remarks	Originally called 'cpl11'
746  */
getPresenceStatsBureau(int & hour)747 int MortevielleEngine::getPresenceStatsBureau(int &hour) {
748 	int day, minute;
749 	int retVal = 0;
750 
751 	updateHour(day, hour, minute);
752 	if (((hour > 8) && (hour < 12)) || ((hour > 20) && (hour < 24)))
753 		retVal = 25;
754 	else if (((hour > 11) && (hour < 14)) || ((hour > 18) && (hour < 21)))
755 		retVal = 5;
756 	else if ((hour > 13) && (hour < 17))
757 		retVal = 55;
758 	else if ((hour > 16) && (hour < 19))
759 		retVal = 45;
760 	else if ((hour >= 0) && (hour < 9))
761 		retVal = 0;
762 
763 	return retVal;
764 }
765 
766 /**
767  * Engine function - Get Presence Statistics - Room Kitchen
768  * @remarks	Originally called 'cpl12'
769  */
getPresenceStatsKitchen()770 int MortevielleEngine::getPresenceStatsKitchen() {
771 	int day, hour, minute;
772 	int retVal = 0;
773 
774 	updateHour(day, hour, minute);
775 	if (((hour > 8) && (hour < 15)) || ((hour > 16) && (hour < 22)))
776 		retVal = 55;
777 	else if (((hour > 14) && (hour < 17)) || ((hour > 21) && (hour < 24)))
778 		retVal = 25;
779 	else if ((hour >= 0) && (hour < 5))
780 		retVal = 0;
781 	else if ((hour > 4) && (hour < 9))
782 		retVal = 15;
783 
784 	return retVal;
785 }
786 
787 /**
788  * Engine function - Get Presence Statistics - Room Attic
789  * @remarks	Originally called 'cpl13'
790  */
getPresenceStatsAttic()791 int MortevielleEngine::getPresenceStatsAttic() {
792 	return 0;
793 }
794 
795 /**
796  * Engine function - Get Presence Statistics - Room Landing
797  * @remarks	Originally called 'cpl15'
798  */
getPresenceStatsLanding()799 int MortevielleEngine::getPresenceStatsLanding() {
800 	int day, hour, minute;
801 	int retVal = 0;
802 
803 	updateHour(day, hour, minute);
804 	if ((hour > 7) && (hour < 12))
805 		retVal = 25;
806 	else if ((hour > 11) && (hour < 14))
807 		retVal = 0;
808 	else if ((hour > 13) && (hour < 18))
809 		retVal = 10;
810 	else if ((hour > 17) && (hour < 20))
811 		retVal = 55;
812 	else if ((hour > 19) && (hour < 22))
813 		retVal = 5;
814 	else if ((hour > 21) && (hour < 24))
815 		retVal = 15;
816 	else if ((hour >= 0) && (hour < 8))
817 		retVal = -15;
818 
819 	return retVal;
820 }
821 
822 /**
823  * Engine function - Get Presence Statistics - Room Chapel
824  * @remarks	Originally called 'cpl20'
825  */
getPresenceStatsChapel(int & hour)826 int MortevielleEngine::getPresenceStatsChapel(int &hour) {
827 	int day, minute;
828 	int retVal = 0;
829 
830 	updateHour(day, hour, minute);
831 	if (hour == 10)
832 		retVal = 65;
833 	else if ((hour > 10) && (hour < 21))
834 		retVal = 5;
835 	else if ((hour > 20) && (hour < 24))
836 		retVal = -15;
837 	else if ((hour >= 0) && (hour < 5))
838 		retVal = -300;
839 	else if ((hour > 4) && (hour < 10))
840 		retVal = -5;
841 
842 	return retVal;
843 }
844 
845 /**
846  * Engine function - Check who is in the Green Room
847  * @remarks	Originally called 'quelq1'
848  */
setPresenceGreenRoom(int roomId)849 void MortevielleEngine::setPresenceGreenRoom(int roomId) {
850 	int rand = getRandomNumber(1, 2);
851 	if (roomId == GREEN_ROOM) {
852 		if (rand == 1)
853 			_roomPresenceLuc = true;
854 		else
855 			_roomPresenceIda = true;
856 	} else if (roomId == DARKBLUE_ROOM) {
857 		if (rand == 1)
858 			_roomPresenceGuy = true;
859 		else
860 			_roomPresenceEva = true;
861 	}
862 
863 	_currBitIndex = 10;
864 }
865 
866 /**
867  * Engine function - Check who is in the Purple Room
868  * @remarks	Originally called 'quelq2'
869  */
setPresencePurpleRoom()870 void MortevielleEngine::setPresencePurpleRoom() {
871 	if (_place == PURPLE_ROOM)
872 		_purpleRoomPresenceLeo = true;
873 	else
874 		_juliaRoomPresenceLeo = true;
875 
876 	_currBitIndex = 10;
877 }
878 
879 /**
880  * Engine function - Check who is in the Blue Room
881  * @remarks	Originally called 'quelq5'
882  */
setPresenceBlueRoom()883 void MortevielleEngine::setPresenceBlueRoom() {
884 	_roomPresenceMax = true;
885 	_currBitIndex = 10;
886 }
887 
888 /**
889  * Engine function - Check who is in the Red Room
890  * @remarks	Originally called 'quelq6'
891  */
setPresenceRedRoom(int roomId)892 void MortevielleEngine::setPresenceRedRoom(int roomId) {
893 	if (roomId == RED_ROOM)
894 		_roomPresenceBob = true;
895 	else if (roomId == GREEN_ROOM2)
896 		_roomPresencePat = true;
897 
898 	_currBitIndex = 10;
899 }
900 
901 /**
902  * Engine function - Check who is in the Dining Room
903  * @remarks	Originally called 'quelq10'
904  */
setPresenceDiningRoom(int hour)905 int MortevielleEngine::setPresenceDiningRoom(int hour) {
906 	int retVal = 0;
907 
908 	if ((hour >= 0) && (hour < 8))
909 		retVal = checkLeoMaxRandomPresence();
910 	else {
911 		int min = 0, max = 0;
912 		if ((hour > 7) && (hour < 10)) {
913 			min = 5;
914 			max = 7;
915 		} else if ((hour > 9) && (hour < 12)) {
916 			min = 1;
917 			max = 4;
918 		} else if (((hour > 11) && (hour < 15)) || ((hour > 18) && (hour < 21))) {
919 			min = 6;
920 			max = 8;
921 		} else if (((hour > 14) && (hour < 19)) || ((hour > 20) && (hour < 24))) {
922 			min = 1;
923 			max = 5;
924 		}
925 		retVal = selectCharacters(min, max);
926 	}
927 	showPeoplePresent(retVal);
928 
929 	return retVal;
930 }
931 
932 /**
933  * Engine function - Check who is in the Bureau
934  * @remarks	Originally called 'quelq11'
935  */
setPresenceBureau(int hour)936 int MortevielleEngine::setPresenceBureau(int hour) {
937 	int retVal = 0;
938 
939 	if ((hour >= 0) && (hour < 8))
940 		retVal = checkLeoMaxRandomPresence();
941 	else {
942 		int min = 0, max = 0;
943 		if (((hour > 7) && (hour < 10)) || ((hour > 20) && (hour < 24))) {
944 			min = 1;
945 			max = 3;
946 		} else if (((hour > 9) && (hour < 12)) || ((hour > 13) && (hour < 19))) {
947 			min = 1;
948 			max = 4;
949 		} else if (((hour > 11) && (hour < 14)) || ((hour > 18) && (hour < 21))) {
950 			min = 1;
951 			max = 2;
952 		}
953 		retVal = selectCharacters(min, max);
954 	}
955 	showPeoplePresent(retVal);
956 
957 	return retVal;
958 }
959 
960 /**
961  * Engine function - Check who is in the Kitchen
962  * @remarks	Originally called 'quelq12'
963  */
setPresenceKitchen()964 int MortevielleEngine::setPresenceKitchen() {
965 	int retVal = checkLeoMaxRandomPresence();
966 	showPeoplePresent(retVal);
967 
968 	return retVal;
969 }
970 
971 /**
972  * Engine function - Check who is in the Landing
973  * @remarks	Originally called 'quelq15'
974  */
setPresenceLanding()975 int MortevielleEngine::setPresenceLanding() {
976 	bool test = false;
977 	int rand = 0;
978 	do {
979 		rand = getRandomNumber(1, 8);
980 		test = (((rand == 1) && (_purpleRoomPresenceLeo || _juliaRoomPresenceLeo)) ||
981 		        ((rand == 2) && _roomPresencePat) ||
982 		        ((rand == 3) && _roomPresenceGuy) ||
983 		        ((rand == 4) && _roomPresenceEva) ||
984 		        ((rand == 5) && _roomPresenceBob) ||
985 		        ((rand == 6) && _roomPresenceLuc) ||
986 		        ((rand == 7) && _roomPresenceIda) ||
987 		        ((rand == 8) && _roomPresenceMax));
988 	} while (test);
989 
990 	int retVal = convertCharacterIndexToBitIndex(rand);
991 	showPeoplePresent(retVal);
992 
993 	return retVal;
994 }
995 
996 /**
997  * Engine function - Check who is in the chapel
998  * @remarks	Originally called 'quelq20'
999  */
setPresenceChapel(int hour)1000 int MortevielleEngine::setPresenceChapel(int hour) {
1001 	int retVal = 0;
1002 
1003 	if (((hour >= 0) && (hour < 10)) || ((hour > 18) && (hour < 24)))
1004 		retVal = checkLeoMaxRandomPresence();
1005 	else {
1006 		int min = 0, max = 0;
1007 		if ((hour > 9) && (hour < 12)) {
1008 			min = 3;
1009 			max = 7;
1010 		} else if ((hour > 11) && (hour < 18)) {
1011 			min = 1;
1012 			max = 2;
1013 		} else if (hour == 18) {
1014 			min = 2;
1015 			max = 4;
1016 		}
1017 		retVal = selectCharacters(min, max);
1018 	}
1019 	showPeoplePresent(retVal);
1020 
1021 	return retVal;
1022 }
1023 
1024 /**
1025  * Engine function - Get the answer after you known a door
1026  * @remarks	Originally called 'frap'
1027  */
getKnockAnswer()1028 void MortevielleEngine::getKnockAnswer() {
1029 	int day, hour, minute;
1030 
1031 	updateHour(day, hour, minute);
1032 	if ((hour >= 0) && (hour < 8))
1033 		_crep = 190;
1034 	else {
1035 		if (getRandomNumber(1, 100) > 70)
1036 			_crep = 190;
1037 		else
1038 			_crep = 147;
1039 	}
1040 }
1041 
1042 /**
1043  * Engine function - Get Room Presence Bit Index
1044  * @remarks	Originally called 'nouvp'
1045  */
getPresenceBitIndex(int roomId)1046 int MortevielleEngine::getPresenceBitIndex(int roomId) {
1047 	int bitIndex = 0;
1048 	if (roomId == GREEN_ROOM) {
1049 		if (_roomPresenceLuc)
1050 			bitIndex = 4;  // LUC
1051 		if (_roomPresenceIda)
1052 			bitIndex = 2;  // IDA
1053 	} else if ( ((roomId == PURPLE_ROOM) && (_purpleRoomPresenceLeo))
1054 			 || ((roomId == JULIA_ROOM) && (_juliaRoomPresenceLeo)))
1055 		bitIndex = 128;    // LEO
1056 	else if (roomId == DARKBLUE_ROOM) {
1057 		if (_roomPresenceGuy)
1058 			bitIndex = 32; // GUY
1059 		if (_roomPresenceEva)
1060 			bitIndex = 16; // EVA
1061 	} else if ((roomId == BLUE_ROOM) && (_roomPresenceMax))
1062 		bitIndex = 1;      // MAX
1063 	else if ((roomId == RED_ROOM) && (_roomPresenceBob))
1064 		bitIndex = 8;      // BOB
1065 	else if ((roomId == GREEN_ROOM2) && (_roomPresencePat))
1066 		bitIndex = 64;     // PAT
1067 	else if ( ((roomId == TOILETS) && (_toiletsPresenceBobMax))
1068 		   || ((roomId == BATHROOM) && (_bathRoomPresenceBobMax)) )
1069 		bitIndex = 9;      // BOB + MAX
1070 
1071 	if (bitIndex != 9)
1072 		showPeoplePresent(bitIndex);
1073 
1074 	return bitIndex;
1075 }
1076 
1077 /**
1078  * Engine function - initGame
1079  * @remarks	Originally called 'dprog'
1080  */
initGame()1081 void MortevielleEngine::initGame() {
1082 	_place = MANOR_FRONT;
1083 	_currentHourCount = 0;
1084 	if (!_coreVar._alreadyEnteredManor)
1085 		_outsideOnlyFl = true;
1086 	_inGameHourDuration = kTime1;
1087 	_currentTime = readclock();
1088 }
1089 
1090 /**
1091  * Engine function - Set Random Presence - Green Room
1092  * @remarks	Originally called 'pl1'
1093  */
setRandomPresenceGreenRoom(int faithScore)1094 void MortevielleEngine::setRandomPresenceGreenRoom(int faithScore) {
1095 	if ( ((_place == GREEN_ROOM) && (!_roomPresenceLuc) && (!_roomPresenceIda))
1096 	  || ((_place == DARKBLUE_ROOM) && (!_roomPresenceGuy) && (!_roomPresenceEva)) ) {
1097 		int p = getPresenceStatsGreenRoom();
1098 		p += faithScore;
1099 		if (getRandomNumber(1, 100) > p)
1100 			displayAloneText();
1101 		else
1102 			setPresenceGreenRoom(_place);
1103 	}
1104 }
1105 
1106 /**
1107  * Engine function - Set Random Presence - Purple Room
1108  * @remarks	Originally called 'pl2'
1109  */
setRandomPresencePurpleRoom(int faithScore)1110 void MortevielleEngine::setRandomPresencePurpleRoom(int faithScore) {
1111 	if (!_purpleRoomPresenceLeo) {
1112 		int p = getPresenceStatsPurpleRoom();
1113 		p += faithScore;
1114 		if (getRandomNumber(1, 100) > p)
1115 			displayAloneText();
1116 		else
1117 			setPresencePurpleRoom();
1118 	}
1119 }
1120 
1121 /**
1122  * Engine function - Set Random Presence - Blue Room
1123  * @remarks	Originally called 'pl5'
1124  */
setRandomPresenceBlueRoom(int faithScore)1125 void MortevielleEngine::setRandomPresenceBlueRoom(int faithScore) {
1126 	if (!_roomPresenceMax) {
1127 		int p = getPresenceStatsBlueRoom();
1128 		p += faithScore;
1129 		if (getRandomNumber(1, 100) > p)
1130 			displayAloneText();
1131 		else
1132 			setPresenceBlueRoom();
1133 	}
1134 }
1135 
1136 /**
1137  * Engine function - Set Random Presence - Red Room
1138  * @remarks	Originally called 'pl6'
1139  */
setRandomPresenceRedRoom(int faithScore)1140 void MortevielleEngine::setRandomPresenceRedRoom(int faithScore) {
1141 	if ( ((_place == RED_ROOM) && (!_roomPresenceBob))
1142 	  || ((_place == GREEN_ROOM2) && (!_roomPresencePat)) ) {
1143 		int p = getPresenceStatsRedRoom();
1144 		p += faithScore;
1145 		if (getRandomNumber(1, 100) > p)
1146 			displayAloneText();
1147 		else
1148 			setPresenceRedRoom(_place);
1149 	}
1150 }
1151 
1152 /**
1153  * Engine function - Set Random Presence - Room 9
1154  * @remarks	Originally called 'pl9'
1155  */
setRandomPresenceJuliaRoom(int faithScore)1156 void MortevielleEngine::setRandomPresenceJuliaRoom(int faithScore) {
1157 	if (!_juliaRoomPresenceLeo) {
1158 		faithScore = -10;
1159 		if (getRandomNumber(1, 100) > faithScore) // always true?
1160 			displayAloneText();
1161 		else
1162 			setPresencePurpleRoom();
1163 	}
1164 }
1165 
1166 /**
1167  * Engine function - Set Random Presence - Dining Room
1168  * @remarks	Originally called 'pl10'
1169  */
setRandomPresenceDiningRoom(int faithScore)1170 void MortevielleEngine::setRandomPresenceDiningRoom(int faithScore) {
1171 	int h;
1172 	int p = getPresenceStatsDiningRoom(h);
1173 	p += faithScore;
1174 	if (getRandomNumber(1, 100) > p)
1175 		displayAloneText();
1176 	else
1177 		setPresenceDiningRoom(h);
1178 }
1179 
1180 /**
1181  * Engine function - Set Random Presence - Bureau
1182  * @remarks	Originally called 'pl11'
1183  */
setRandomPresenceBureau(int faithScore)1184 void MortevielleEngine::setRandomPresenceBureau(int faithScore) {
1185 	int h;
1186 
1187 	int p = getPresenceStatsBureau(h);
1188 	p += faithScore;
1189 	if (getRandomNumber(1, 100) > p)
1190 		displayAloneText();
1191 	else
1192 		setPresenceBureau(h);
1193 }
1194 
1195 /**
1196  * Engine function - Set Random Presence - Kitchen
1197  * @remarks	Originally called 'pl12'
1198  */
setRandomPresenceKitchen(int faithScore)1199 void MortevielleEngine::setRandomPresenceKitchen(int faithScore) {
1200 
1201 	int p = getPresenceStatsKitchen();
1202 	p += faithScore;
1203 	if (getRandomNumber(1, 100) > p)
1204 		displayAloneText();
1205 	else
1206 		setPresenceKitchen();
1207 }
1208 
1209 /**
1210  * Engine function - Set Random Presence - Attic / Cellar
1211  * @remarks	Originally called 'pl13'
1212  */
setRandomPresenceAttic(int faithScore)1213 void MortevielleEngine::setRandomPresenceAttic(int faithScore) {
1214 	int p = getPresenceStatsAttic();
1215 	p += faithScore;
1216 	if (getRandomNumber(1, 100) > p)
1217 		displayAloneText();
1218 	else
1219 		setPresenceKitchen();
1220 }
1221 
1222 /**
1223  * Engine function - Set Random Presence - Landing
1224  * @remarks	Originally called 'pl15'
1225  */
setRandomPresenceLanding(int faithScore)1226 void MortevielleEngine::setRandomPresenceLanding(int faithScore) {
1227 	int p = getPresenceStatsLanding();
1228 	p += faithScore;
1229 	if (getRandomNumber(1, 100) > p)
1230 		displayAloneText();
1231 	else
1232 		setPresenceLanding();
1233 }
1234 
1235 /**
1236  * Engine function - Set Random Presence - Chapel
1237  * @remarks	Originally called 'pl20'
1238  */
setRandomPresenceChapel(int faithScore)1239 void MortevielleEngine::setRandomPresenceChapel(int faithScore) {
1240 	int h;
1241 
1242 	int p = getPresenceStatsChapel(h);
1243 	p += faithScore;
1244 	if (getRandomNumber(1, 100) > p)
1245 		displayAloneText();
1246 	else
1247 		setPresenceChapel(h);
1248 }
1249 
1250 /**
1251  * Start music or speech
1252  * @remarks	Originally called 'musique'
1253  */
startMusicOrSpeech(int so)1254 void MortevielleEngine::startMusicOrSpeech(int so) {
1255 	if (so == 0) {
1256 		/* musik(0) */
1257 		;
1258 	} else if ((!_introSpeechPlayed) && (!_coreVar._alreadyEnteredManor)) {
1259 		// Type 1: Speech
1260 		_soundManager->startSpeech(10, 1, 1);
1261 		_introSpeechPlayed = true;
1262 	} else {
1263 		if (((_coreVar._currPlace == MOUNTAIN) || (_coreVar._currPlace == MANOR_FRONT) || (_coreVar._currPlace == MANOR_BACK)) && (getRandomNumber(1, 3) == 2))
1264 			// Type 1: Speech
1265 			_soundManager->startSpeech(9, getRandomNumber(2, 4), 1);
1266 		else if ((_coreVar._currPlace == CHAPEL) && (getRandomNumber(1, 2) == 1))
1267 			// Type 1: Speech
1268 			_soundManager->startSpeech(8, 1, 1);
1269 		else if ((_coreVar._currPlace == WELL) && (getRandomNumber(1, 2) == 2))
1270 			// Type 1: Speech
1271 			_soundManager->startSpeech(12, 1, 1);
1272 		else if (_coreVar._currPlace == INSIDE_WELL)
1273 			// Type 1: Speech
1274 			_soundManager->startSpeech(13, 1, 1);
1275 		else
1276 			// Type 2 : music
1277 			_soundManager->startSpeech(getRandomNumber(1, 17), 1, 2);
1278 	}
1279 }
1280 
1281 /**
1282  * Engine function - You lose!
1283  * @remarks	Originally called 'tperd'
1284  */
loseGame()1285 void MortevielleEngine::loseGame() {
1286 	resetOpenObjects();
1287 	_roomDoorId = OWN_ROOM;
1288 	_curSearchObjId = 0;
1289 	_menu->unsetSearchMenu();
1290 	if (!_outsideOnlyFl)
1291 		getPresence(MANOR_FRONT);
1292 
1293 	_loseGame = true;
1294 	clearUpperLeftPart();
1295 	_screenSurface->drawBox(60, 35, 400, 50, 15);
1296 	handleDescriptionText(9, _crep);
1297 	clearDescriptionBar();
1298 	clearVerbBar();
1299 	_col = false;
1300 	_syn = false;
1301 	_destinationOk = false;
1302 }
1303 
1304 /**
1305  * Engine function - Check inventory for a given object
1306  * @remarks	Originally called 'cherjer'
1307  */
checkInventory(int objectId)1308 bool MortevielleEngine::checkInventory(int objectId) {
1309 	bool retVal = false;
1310 	for (int i = 1; i <= 6; ++i)
1311 		retVal = (retVal || (_coreVar._inventory[i] == objectId));
1312 
1313 	if (_coreVar._selectedObjectId == objectId)
1314 		retVal = true;
1315 
1316 	return retVal;
1317 }
1318 
1319 /**
1320  * Engine function - Display Dining Room
1321  * @remarks	Originally called 'st1sama'
1322  */
displayDiningRoom()1323 void MortevielleEngine::displayDiningRoom() {
1324 	_coreVar._currPlace = DINING_ROOM;
1325 	prepareDisplayText();
1326 }
1327 
1328 /**
1329  * Engine function - Start non interactive Dialog
1330  * @remarks	Originally called 'sparl'
1331  */
startDialog(int16 rep)1332 void MortevielleEngine::startDialog(int16 rep) {
1333 	int key;
1334 
1335 	assert(rep >= 0);
1336 
1337 	_mouse->hideMouse();
1338 	Common::String dialogStr = getString(rep + kDialogStringIndex);
1339 	_text->displayStr(dialogStr, 230, 4, 65, 26, 5);
1340 	_dialogManager->drawF3F8();
1341 
1342 	key = 0;
1343 	do {
1344 		_soundManager->startSpeech(rep, _caff - 69, 0);
1345 		key = _dialogManager->waitForF3F8();
1346 		if (shouldQuit())
1347 			return;
1348 	} while (key != 66);
1349 	clearScreen();
1350 	_mouse->showMouse();
1351 }
1352 
1353 /**
1354  * Engine function - End of Search: reset globals
1355  * @remarks	Originally called 'finfouill'
1356  */
endSearch()1357 void MortevielleEngine::endSearch() {
1358 	_heroSearching = false;
1359 	_obpart = false;
1360 	_searchCount = 0;
1361 	_is = 0;
1362 	_menu->unsetSearchMenu();
1363 }
1364 
1365 /**
1366  * Engine function - Go to Dining room
1367  * @remarks	Originally called 't1sama'
1368  */
gotoDiningRoom()1369 void MortevielleEngine::gotoDiningRoom() {
1370 	int day, hour, minute;
1371 
1372 	updateHour(day, hour, minute);
1373 	if ((hour < 5) && (_coreVar._currPlace > ROOM18)) {
1374 		if (!checkInventory(137)) {        //You don't have the keys, and it's late
1375 			_crep = 1511;
1376 			loseGame();
1377 		} else
1378 			displayDiningRoom();
1379 	} else if (!_coreVar._alreadyEnteredManor) {     //Is it your first time?
1380 		_currBitIndex = 255; // Everybody is present
1381 		showPeoplePresent(_currBitIndex);
1382 		_caff = 77;
1383 		drawPictureWithText();
1384 		_screenSurface->drawBox(223, 47, 155, 91, 15);
1385 		handleDescriptionText(2, 33);
1386 		testKey(false);
1387 		menuUp();
1388 		_mouse->hideMouse();
1389 		clearScreen();
1390 		drawDiscussionBox();
1391 		startDialog(140);
1392 		drawRightFrame();
1393 		drawClock();
1394 		_mouse->showMouse();
1395 		_coreVar._currPlace = OWN_ROOM;
1396 		prepareDisplayText();
1397 		resetPresenceInRooms(DINING_ROOM);
1398 		if (!_outsideOnlyFl)
1399 			getPresence(OWN_ROOM);
1400 		_currBitIndex = 0;
1401 		_savedBitIndex = 0;
1402 		_coreVar._alreadyEnteredManor = true;
1403 	} else
1404 		displayDiningRoom();
1405 }
1406 
1407 /**
1408  * Engine function - Check Manor distance (in the mountains)
1409  * @remarks	Originally called 't1neig'
1410  */
checkManorDistance()1411 void MortevielleEngine::checkManorDistance() {
1412 	++_manorDistance;
1413 	if (_manorDistance > 2) {
1414 		_crep = 1506;
1415 		loseGame();
1416 	} else {
1417 		_destinationOk = true;
1418 		_coreVar._currPlace = MOUNTAIN;
1419 		prepareDisplayText();
1420 	}
1421 }
1422 
1423 /**
1424  * Engine function - Go to Manor front
1425  * @remarks	Originally called 't1deva'
1426  */
gotoManorFront()1427 void MortevielleEngine::gotoManorFront() {
1428 	_manorDistance = 0;
1429 	_coreVar._currPlace = MANOR_FRONT;
1430 	prepareDisplayText();
1431 }
1432 
1433 /**
1434  * Engine function - Go to Manor back
1435  * @remarks	Originally called 't1derr'
1436  */
gotoManorBack()1437 void MortevielleEngine::gotoManorBack() {
1438 	_coreVar._currPlace = MANOR_BACK;
1439 	prepareDisplayText();
1440 }
1441 
1442 /**
1443  * Engine function - Dead : Flooded in Well
1444  * @remarks	Originally called 't1deau'
1445  */
floodedInWell()1446 void MortevielleEngine::floodedInWell() {
1447 	_crep = 1503;
1448 	loseGame();
1449 }
1450 
1451 /**
1452  * Called when a savegame has been loaded.
1453  * @remarks	Originally called 'antegame'
1454  */
gameLoaded()1455 void MortevielleEngine::gameLoaded() {
1456 	_mouse->hideMouse();
1457 	_menu->_menuDisplayed = false;
1458 	_loseGame = true;
1459 	_anyone = false;
1460 	_destinationOk = true;
1461 	_col = false;
1462 	_hiddenHero = false;
1463 	_uptodatePresence = false;
1464 	_maff = 68;
1465 	_menuOpcode = OPCODE_NONE;
1466 	_introSpeechPlayed = false;
1467 	_x = 0;
1468 	_y = 0;
1469 	_num = 0;
1470 	_startTime = 0;
1471 	_endTime = 0;
1472 	_is = 0;
1473 	_searchCount = 0;
1474 	_roomDoorId = OWN_ROOM;
1475 	_syn = true;
1476 	_heroSearching = true;
1477 	_curSearchObjId = 0;
1478 	_manorDistance = 0;
1479 	resetOpenObjects();
1480 	_takeObjCount = 0;
1481 	prepareDisplayText();
1482 	_hintPctMessage = getString(580);
1483 
1484 	_destinationOk = false;
1485 	_endGame = true;
1486 	_loseGame = false;
1487 	_heroSearching = false;
1488 
1489 	displayAloneText();
1490 	prepareRoom();
1491 	drawClock();
1492 	drawPictureWithText();
1493 	handleDescriptionText(2, _crep);
1494 	clearVerbBar();
1495 	_endGame = false;
1496 	_menu->setDestinationText(_coreVar._currPlace);
1497 	_menu->setInventoryText();
1498 	if (_coreVar._selectedObjectId != 0)
1499 		displayItemInHand(_coreVar._selectedObjectId + 400);
1500 	_mouse->showMouse();
1501 }
1502 
1503 /**
1504  * Engine function - Handle OpCodes
1505  * @remarks	Originally called 'tsitu'
1506  */
handleOpcode()1507 void MortevielleEngine::handleOpcode() {
1508 	if (!_col)
1509 		clearDescriptionBar();
1510 	_syn = false;
1511 	_keyPressedEsc = false;
1512 	if (!_anyone) {
1513 		if (_uptodatePresence) {
1514 			if ((_currMenu == MENU_MOVE) || (_currAction == _menu->_opcodeLeave) || (_currAction == _menu->_opcodeSleep) || (_currAction == _menu->_opcodeEat)) {
1515 				_controlMenu = 4;
1516 				menuUp();
1517 				return;
1518 			}
1519 		}
1520 
1521 		if (_currMenu == MENU_MOVE)
1522 			fctMove();
1523 		else if (_currMenu == MENU_DISCUSS)
1524 			fctDiscuss();
1525 		else if (_currMenu == MENU_INVENTORY)
1526 			fctInventoryTake();
1527 		else if (_currAction == _menu->_opcodeAttach)
1528 			fctAttach();
1529 		else if (_currAction == _menu->_opcodeWait)
1530 			fctWait();
1531 		else if (_currAction == _menu->_opcodeForce)
1532 			fctForce();
1533 		else if (_currAction == _menu->_opcodeSleep)
1534 			fctSleep();
1535 		else if (_currAction == _menu->_opcodeListen)
1536 			fctListen();
1537 		else if (_currAction == _menu->_opcodeEnter)
1538 			fctEnter();
1539 		else if (_currAction == _menu->_opcodeClose)
1540 			fctClose();
1541 		else if (_currAction == _menu->_opcodeSearch)
1542 			fctSearch();
1543 		else if (_currAction == _menu->_opcodeKnock)
1544 			fctKnock();
1545 		else if (_currAction == _menu->_opcodeScratch)
1546 			fctScratch();
1547 		else if (_currAction == _menu->_opcodeRead)
1548 			fctRead();
1549 		else if (_currAction == _menu->_opcodeEat)
1550 			fctEat();
1551 		else if (_currAction == _menu->_opcodePlace)
1552 			fctPlace();
1553 		else if (_currAction == _menu->_opcodeOpen)
1554 			fctOpen();
1555 		else if (_currAction == _menu->_opcodeTake)
1556 			fctTake();
1557 		else if (_currAction == _menu->_opcodeLook)
1558 			fctLook();
1559 		else if (_currAction == _menu->_opcodeSmell)
1560 			fctSmell();
1561 		else if (_currAction == _menu->_opcodeSound)
1562 			fctSound();
1563 		else if (_currAction == _menu->_opcodeLeave)
1564 			fctLeave();
1565 		else if (_currAction == _menu->_opcodeLift)
1566 			fctLift();
1567 		else if (_currAction == _menu->_opcodeTurn)
1568 			fctTurn();
1569 		else if (_currAction == _menu->_opcodeSSearch)
1570 			fctSelfSearch();
1571 		else if (_currAction == _menu->_opcodeSRead)
1572 			fctSelfRead();
1573 		else if (_currAction == _menu->_opcodeSPut)
1574 			fctSelfPut();
1575 		else if (_currAction == _menu->_opcodeSLook)
1576 			fctSelftLook();
1577 
1578 		_hiddenHero = false;
1579 
1580 		if (_currAction == _menu->_opcodeSHide)
1581 			fctSelfHide();
1582 	} else if (_anyone) {
1583 		interactNPC();
1584 		_anyone = false;
1585 		menuUp();
1586 		return;
1587 	}
1588 	int hour, day, minute;
1589 	updateHour(day, hour, minute);
1590 	if ((((hour == 12) || (hour == 13) || (hour == 19)) && (_coreVar._currPlace != DINING_ROOM)) ||
1591 	        ((hour > 0) && (hour < 6) && (_coreVar._currPlace != OWN_ROOM)))
1592 		++_coreVar._faithScore;
1593 	if (((_coreVar._currPlace < CRYPT) || (_coreVar._currPlace > MOUNTAIN)) && (_coreVar._currPlace != INSIDE_WELL)
1594 	        && (_coreVar._currPlace != OWN_ROOM) && (_coreVar._selectedObjectId != 152) && (!_loseGame)) {
1595 		if ((_coreVar._faithScore > 99) && (hour > 8) && (hour < 16)) {
1596 			_crep = 1501;
1597 			loseGame();
1598 		} else if ((_coreVar._faithScore > 99) && (hour > 0) && (hour < 9)) {
1599 			_crep = 1508;
1600 			loseGame();
1601 		} else if ((day > 1) && (hour > 8) && (!_loseGame)) {
1602 			_crep = 1502;
1603 			loseGame();
1604 		}
1605 	}
1606 	menuUp();
1607 }
1608 
1609 /**
1610  * Engine function - Transform time into a char
1611  * @remarks	Originally called 'tmaj3'
1612  */
hourToChar()1613 void MortevielleEngine::hourToChar() {
1614 	int day, hour, minute;
1615 
1616 	updateHour(day, hour, minute);
1617 	if (minute == 30)
1618 		minute = 1;
1619 	hour += day * 24;
1620 	minute += hour * 2;
1621 	_coreVar._fullHour = (unsigned char)minute;
1622 }
1623 
1624 /**
1625  * Engine function - extract time from a char
1626  * @remarks	Originally called 'theure'
1627  */
charToHour()1628 void MortevielleEngine::charToHour() {
1629 	int fullHour = _coreVar._fullHour;
1630 	int tmpHour = fullHour % 48;
1631 	_currDay = fullHour / 48;
1632 	_currHalfHour = tmpHour % 2;
1633 	_currHour = tmpHour / 2;
1634 	_hour = _currHour;
1635 	if (_currHalfHour == 1)
1636 		_minute = 30;
1637 	else
1638 		_minute = 0;
1639 }
1640 
1641 /**
1642  * Engine function - Clear upper left part of Screen - Type 1
1643  * @remarks	Originally called 'clsf1'
1644  */
clearUpperLeftPart()1645 void MortevielleEngine::clearUpperLeftPart() {
1646 	_mouse->hideMouse();
1647 	_screenSurface->fillRect(0, Common::Rect(0, 11, 514, 175));
1648 	_mouse->showMouse();
1649 }
1650 
1651 /**
1652  * Engine function - Clear low bar used by description
1653  * @remarks	Originally called 'clsf2'
1654  */
clearDescriptionBar()1655 void MortevielleEngine::clearDescriptionBar() {
1656 	_mouse->hideMouse();
1657 	if (_largestClearScreen) {
1658 		_screenSurface->fillRect(0, Common::Rect(1, 176, 633, 199));
1659 		_screenSurface->drawBox(0, 175, 634, 24, 15);
1660 		_largestClearScreen = false;
1661 	} else {
1662 		_screenSurface->fillRect(0, Common::Rect(1, 176, 633, 190));
1663 		_screenSurface->drawBox(0, 175, 634, 15, 15);
1664 	}
1665 	_mouse->showMouse();
1666 }
1667 
1668 /**
1669  * Engine function - Clear lowest bar used by verbs
1670  * @remarks	Originally called 'clsf3'
1671  */
clearVerbBar()1672 void MortevielleEngine::clearVerbBar() {
1673 	_mouse->hideMouse();
1674 	_screenSurface->fillRect(0, Common::Rect(1, 192, 633, 199));
1675 	_screenSurface->drawBox(0, 191, 634, 8, 15);
1676 	_mouse->showMouse();
1677 }
1678 
1679 /**
1680  * Engine function - Clear upper right part of the screen
1681  * @remarks	Originally called 'clsf10'
1682  */
clearUpperRightPart()1683 void MortevielleEngine::clearUpperRightPart() {
1684 	Common::String st;
1685 
1686 	_mouse->hideMouse();
1687 
1688 	// Clear ambiance description
1689 	_screenSurface->fillRect(15, Common::Rect(544, 93, 600, 98));
1690 	if (_coreVar._faithScore < 33)
1691 		st = getEngineString(S_COOL);
1692 	else if (_coreVar._faithScore < 66)
1693 		st = getEngineString(S_LOURDE);
1694 	else if (_coreVar._faithScore > 65)
1695 		st = getEngineString(S_MALSAINE);
1696 
1697 	int x1 = 574 - (_screenSurface->getStringWidth(st) / 2);
1698 	_screenSurface->putxy(x1, 92);
1699 	_screenSurface->drawString(st, 4);
1700 
1701 	// Clear person list
1702 	_screenSurface->fillRect(15, Common::Rect(560, 24, 610, 86));
1703 	_mouse->showMouse();
1704 }
1705 
1706 /**
1707  * Engine function - Get a random number between two values
1708  * @remarks	Originally called 'get_random_number' and 'hazard'
1709  */
getRandomNumber(int minval,int maxval)1710 int MortevielleEngine::getRandomNumber(int minval, int maxval) {
1711 	return _randomSource.getRandomNumber(maxval - minval) + minval;
1712 }
1713 
1714 /**
1715  * Engine function - Show alert "use move menu"
1716  * @remarks	Originally called 'aldepl'
1717  */
showMoveMenuAlert()1718 void MortevielleEngine::showMoveMenuAlert() {
1719 	_dialogManager->show(getEngineString(S_USE_DEP_MENU));
1720 }
1721 
1722 /**
1723  * The original engine used this method to display a starting text screen letting the player
1724  * select the graphics mode to use
1725  * @remarks	Originally called 'dialpre'
1726  */
showConfigScreen()1727 void MortevielleEngine::showConfigScreen() {
1728 	// FIXME: need a DOS palette, index 9 (light blue). Also we should show DOS font here
1729 	Common::String tmpStr;
1730 	int cy = 0;
1731 	clearScreen();
1732  	do {
1733  		++cy;
1734  		tmpStr = getString(cy + kStartingScreenStringIndex);
1735  		int width = _screenSurface->getStringWidth(tmpStr);
1736  		_text->displayStr(tmpStr, 320 - width / 2, cy * 8, 80, 1, 2);
1737  	} while (cy != 20);
1738 
1739  	int ix = 0;
1740  	do {
1741  		++ix;
1742  	} while (!(keyPressed() || ix == 0x5e5));
1743 
1744 	_crep = 998;
1745 }
1746 
1747 /**
1748  * Decodes a number of 64 byte blocks
1749  * @param pStart	Start of data
1750  * @param count		Number of 64 byte blocks
1751  * @remarks	Originally called 'zzuul'
1752  */
decodeNumber(byte * pStart,int count)1753 void MortevielleEngine::decodeNumber(byte *pStart, int count) {
1754 	while (count-- > 0) {
1755 		for (int idx = 0; idx < 64; ++pStart, ++idx) {
1756 			uint16 v = ((*pStart - 0x80) << 1) + 0x80;
1757 
1758 			if (v & 0x8000)
1759 				*pStart = 0;
1760 			else if (v & 0xff00)
1761 				*pStart = 0xff;
1762 			else
1763 				*pStart = (byte)v;
1764 		}
1765 	}
1766 }
1767 
1768 const byte cryptoArrDefaultFr[32] = {
1769 	32, 101, 115,  97, 114, 105, 110,
1770 	117, 116, 111, 108,  13, 100,  99,
1771 	112, 109,  46, 118, 130,  39, 102,
1772 	98,  44, 113, 104, 103,  33,  76,
1773 	85, 106,  30,  31
1774 };
1775 
1776 const byte cryptoArr30Fr[32] = {
1777 	69,  67,  74, 138, 133, 120,  77, 122,
1778 	121,  68,  65,  63,  73,  80,  83,  82,
1779 	156,  45,  58,  79,  49,  86,  78,  84,
1780 	71,  81,  64,  66, 135,  34, 136,  91
1781 };
1782 
1783 const byte cryptoArr31Fr[32]= {
1784 	93,  47,  48,  53,  50,  70, 124,  75,
1785 	72, 147, 140, 150, 151,  57,  56,  51,
1786 	107, 139,  55,  89, 131,  37,  54,  88,
1787 	119,   0,   0,   0,   0,   0,   0,   0
1788 };
1789 
1790 const byte cryptoArrDefaultDe[32] = {
1791 	0x20, 0x65, 0x6E, 0x69, 0x73, 0x72, 0x74,
1792 	0x68, 0x61, 0x75, 0x0D, 0x63, 0x6C, 0x64,
1793 	0x6D, 0x6F, 0x67, 0x2E, 0x62, 0x66, 0x53,
1794 	0x2C, 0x77, 0x45, 0x7A, 0x6B, 0x44, 0x76,
1795 	0x9C, 0x47, 0x1E, 0x1F
1796 };
1797 
1798 const byte cryptoArr30De[32] = {
1799 	0x49, 0x4D, 0x21, 0x42, 0x4C, 0x70, 0x41, 0x52,
1800 	0x57, 0x4E, 0x48, 0x3F, 0x46, 0x50, 0x55, 0x4B,
1801 	0x5A, 0x4A, 0x54, 0x31, 0x4F, 0x56, 0x79, 0x3A,
1802 	0x6A, 0x5B, 0x5D, 0x40, 0x22, 0x2F, 0x30, 0x35
1803 };
1804 
1805 const byte cryptoArr31De[32]= {
1806 	0x78, 0x2D, 0x32, 0x82, 0x43, 0x39, 0x33, 0x38,
1807 	0x7C, 0x27, 0x37, 0x3B, 0x25, 0x28, 0x29, 0x36,
1808 	0x51, 0x59, 0x71, 0x81, 0x87, 0x88, 0x93, 0,
1809 	0,    0,    0,    0,    0,    0,    0,    0
1810 };
1811 
1812 const byte *cryptoArrDefault, *cryptoArr30, *cryptoArr31;
1813 uint16 ctrlChar;
1814 
1815 /**
1816  * Decrypt the next character
1817  * @param c		OUT, next decrypted char
1818  * @param idx	IN/OUT, current buffer index
1819  * @param pt	IN/OUT, current encryption point
1820  * @return a boolean specifying if a stop character has been encountered
1821  * @remarks	Originally called 'cinq_huit'
1822  */
decryptNextChar(char & c,int & idx,byte & pt)1823 bool MortevielleEngine::decryptNextChar(char &c, int &idx, byte &pt) {
1824 	uint16 oct, ocd;
1825 
1826 	/* 5-8 */
1827 	oct = _dialogIndexArray[idx];
1828 	oct = ((uint16)(oct << (16 - pt))) >> (16 - pt);
1829 	if (pt < 6) {
1830 		++idx;
1831 		oct = oct << (5 - pt);
1832 		pt += 11;
1833 		oct = oct | ((uint)_dialogIndexArray[idx] >> pt);
1834 	} else {
1835 		pt -= 5;
1836 		oct = (uint)oct >> pt;
1837 	}
1838 
1839 	if (oct == ctrlChar) {
1840 		c = '$';
1841 		return true;
1842 	} else if (oct == 30 || oct == 31) {
1843 		ocd = _dialogIndexArray[idx];
1844 		ocd = (uint16)(ocd << (16 - pt)) >> (16 - pt);
1845 		if (pt < 6) {
1846 			++idx;
1847 			ocd = ocd << (5 - pt);
1848 			pt += 11;
1849 			ocd = ocd | ((uint)_dialogIndexArray[idx] >> pt);
1850 		} else {
1851 			pt -= 5;
1852 			ocd = (uint)ocd >> pt;
1853 		}
1854 
1855 		if (oct == 30)
1856 			c = (unsigned char)cryptoArr30[ocd];
1857 		else
1858 			c = (unsigned char)cryptoArr31[ocd];
1859 
1860 		if (c == '\0') {
1861 			c = '#';
1862 			return true;
1863 		}
1864 	} else {
1865 		c = (unsigned char)cryptoArrDefault[oct];
1866 	}
1867 	return false;
1868 }
1869 
1870 /**
1871  * Decode and extract the line with the given Id
1872  * @remarks	Originally called 'deline'
1873  */
getString(int num)1874 Common::String MortevielleEngine::getString(int num) {
1875 	Common::String wrkStr = "";
1876 
1877 	if (num < 0) {
1878 		warning("getString(%d): num < 0! Skipping", num);
1879 	} else if (!_txxFileFl) {
1880 		wrkStr = getGameString(num);
1881 	} else {
1882 		int hint = _dialogHintArray[num]._hintId;
1883 		byte point = _dialogHintArray[num]._point;
1884 		int length = 0;
1885 		bool endFl = false;
1886 		char let;
1887 		do {
1888 			endFl = decryptNextChar(let, hint, point);
1889 			wrkStr += let;
1890 			++length;
1891 		} while (!endFl);
1892 	}
1893 
1894 	while (wrkStr.lastChar() == '$')
1895 		// Remove trailing '$'s
1896 		wrkStr.deleteLastChar();
1897 
1898 	return wrkStr;
1899 }
1900 
1901 /**
1902  * Reset object place
1903  * @remarks	Originally called 'copcha'
1904  */
resetObjectPlace()1905 void MortevielleEngine::resetObjectPlace() {
1906 	for (int i = kAcha; i < kAcha + 390; i++)
1907 		_tabdon[i] = _tabdon[i + 390];
1908 }
1909 
resetCoreVar()1910 void MortevielleEngine::resetCoreVar() {
1911 	_saveStruct._alreadyEnteredManor = _coreVar._alreadyEnteredManor = false;
1912 	_saveStruct._selectedObjectId = _coreVar._selectedObjectId = 0;
1913 	_saveStruct._cellarObjectId = _coreVar._cellarObjectId = 0;
1914 	_saveStruct._atticBallHoleObjectId = _coreVar._atticBallHoleObjectId = 0;
1915 	_saveStruct._atticRodHoleObjectId = _coreVar._atticRodHoleObjectId = 0;
1916 	_saveStruct._wellObjectId = _coreVar._wellObjectId = 0;
1917 	_saveStruct._secretPassageObjectId = _coreVar._secretPassageObjectId = 0;
1918 	_saveStruct._purpleRoomObjectId = _coreVar._purpleRoomObjectId = 136;
1919 	_saveStruct._cryptObjectId = _coreVar._cryptObjectId = 141;
1920 	_saveStruct._faithScore = _coreVar._faithScore = getRandomNumber(4, 10);
1921 	_saveStruct._currPlace = _coreVar._currPlace = MANOR_FRONT;
1922 
1923 	for (int i = 2; i <= 6; ++i)
1924 		_coreVar._inventory[i] = 0;
1925 
1926 	// Only object in inventory: a gun
1927 	_coreVar._inventory[1] = 113;
1928 
1929 	_saveStruct._fullHour = _coreVar._fullHour = (unsigned char)20;
1930 
1931 	for (int i = 1; i <= 10; ++i)
1932 		_coreVar._pctHintFound[i] = ' ';
1933 
1934 	for (int i = 1; i <= 6; ++i)
1935 		_coreVar._availableQuestion[i] = '*';
1936 
1937 	for (int i = 7; i <= 9; ++i)
1938 		_coreVar._availableQuestion[i] = ' ';
1939 
1940 	for (int i = 10; i <= 28; ++i)
1941 		_coreVar._availableQuestion[i] = '*';
1942 
1943 	for (int i = 29; i <= 42; ++i)
1944 		_coreVar._availableQuestion[i] = ' ';
1945 
1946 	_coreVar._availableQuestion[33] = '*';
1947 }
1948 /**
1949  * Engine function - When restarting the game, reset the main variables used by the engine
1950  * @remarks	Originally called 'inzon'
1951  */
resetVariables()1952 void MortevielleEngine::resetVariables() {
1953 	resetObjectPlace();
1954 	resetCoreVar();
1955 
1956 	for (int i = 1; i <= 8; ++i)
1957 		_charAnswerCount[i] = 0;
1958 
1959 	initMaxAnswer();
1960 }
1961 
1962 /**
1963  * Engine function - Set the palette
1964  * @remarks	Originally called 'writepal'
1965  */
setPal(int n)1966 void MortevielleEngine::setPal(int n) {
1967 	for (int i = 1; i <= 16; ++i) {
1968 		_curPict[(2 * i)] = _stdPal[n][i].x;
1969 		_curPict[(2 * i) + 1] = _stdPal[n][i].y;
1970 	}
1971 }
1972 
1973 /**
1974  * Engine function - Load Palette from File
1975  * @remarks	Originally called 'charpal'
1976  */
loadPalette()1977 void MortevielleEngine::loadPalette() {
1978 	Common::File f;
1979 
1980 	if (!f.open("fxx.mor")) {
1981 		if (f.open("mfxx.mor"))
1982 			f.seek(7 * 25);
1983 		else
1984 			error("Missing file - fxx.mor");
1985 	}
1986 
1987 	for (int i = 0; i < 108; ++i)
1988 		_drawingSizeArr[i] = f.readSint16LE();
1989 	f.close();
1990 
1991 	if (!f.open("plxx.mor"))
1992 		error("Missing file - plxx.mor");
1993 	for (int i = 0; i <= 90; ++i) {
1994 		for (int j = 1; j <= 16; ++j) {
1995 			_stdPal[i][j].x = f.readByte();
1996 			_stdPal[i][j].y = f.readByte();
1997 		}
1998 	}
1999 	f.close();
2000 
2001 	if (!f.open("cxx.mor"))
2002 		error("Missing file - cxx.mor");
2003 
2004 	// Skip CGA Palette and Patterns
2005 
2006 	f.close();
2007 }
2008 
2009 /**
2010  * Engine function - Load Texts from File
2011  * @remarks	Originally called 'chartex'
2012  */
loadTexts()2013 void MortevielleEngine::loadTexts() {
2014 	Common::File inpFile;
2015 	Common::File ntpFile;
2016 
2017 	_txxFileFl = false;
2018 	if (!useOriginalData()) {
2019 		warning("Using improved translation from DAT file");
2020 		return;
2021 	}
2022 
2023 	if (!inpFile.open("TXX.INP")) {
2024 		if (!inpFile.open("TXX.MOR")) {
2025 			warning("Missing file - TXX.INP or .MOR - Switching to DAT file");
2026 			return;
2027 		}
2028 	}
2029 	if (ntpFile.open("TXX.NTP")) {
2030 		cryptoArr30 = cryptoArr30Fr;
2031 		cryptoArr31 = cryptoArr31Fr;
2032 		cryptoArrDefault = cryptoArrDefaultFr;
2033 		ctrlChar = 11;
2034 	} else if (ntpFile.open("TXX.IND")) {
2035 		cryptoArr30 = cryptoArr30De;
2036 		cryptoArr31 = cryptoArr31De;
2037 		cryptoArrDefault = cryptoArrDefaultDe;
2038 		ctrlChar = 10;
2039 	} else {
2040 		warning("Missing file - TXX.NTP or .IND - Switching to DAT file");
2041 		return;
2042 	}
2043 
2044 	if ((inpFile.size() > (kMaxDialogIndex * 2)) || (ntpFile.size() > (kMaxDialogHint * 3))) {
2045 		warning("TXX file - Unexpected format - Switching to DAT file");
2046 		return;
2047 	}
2048 
2049 	for (int i = 0; i < inpFile.size() / 2; ++i)
2050 		_dialogIndexArray[i] = inpFile.readUint16LE();
2051 
2052 	inpFile.close();
2053 	_txxFileFl = true;
2054 
2055 	for (int i = 0; i < (ntpFile.size() / 3); ++i) {
2056 		_dialogHintArray[i]._hintId = ntpFile.readSint16LE();
2057 		_dialogHintArray[i]._point = ntpFile.readByte();
2058 	}
2059 
2060 	ntpFile.close();
2061 
2062 }
2063 
loadCFIEC()2064 void MortevielleEngine::loadCFIEC() {
2065 	Common::File f;
2066 
2067 	if (!f.open("cfiec.mor")) {
2068 		if (!f.open("alcfiec.mor"))
2069 			error("Missing file - *cfiec.mor");
2070 	}
2071 
2072 	_cfiecBufferSize = ((f.size() / 128) + 1) * 128;
2073 	int32 fileSize = f.size();
2074 
2075 	if (!_reloadCFIEC)
2076 		_cfiecBuffer = (byte *)malloc(sizeof(byte) * _cfiecBufferSize);
2077 
2078 	for (int32 i = 0; i < fileSize; ++i)
2079 		_cfiecBuffer[i] = f.readByte();
2080 
2081 	for (int i = fileSize; i < _cfiecBufferSize; i++)
2082 		_cfiecBuffer[i] = 0;
2083 
2084 	f.close();
2085 
2086 	_reloadCFIEC = false;
2087 }
2088 
2089 
loadCFIPH()2090 void MortevielleEngine::loadCFIPH() {
2091 	Common::File f;
2092 
2093 	if (!f.open("cfiph.mor")) {
2094 		if (!f.open("alcfiph.mor"))
2095 			error("Missing file - *cfiph.mor");
2096 	}
2097 
2098 	_soundManager->_cfiphBuffer = (uint16 *)malloc(sizeof(uint16) * (f.size() / 2));
2099 
2100 	for (int i = 0; i < (f.size() / 2); ++i)
2101 		_soundManager->_cfiphBuffer[i] = f.readUint16BE();
2102 
2103 	f.close();
2104 }
2105 
2106 /**
2107  * Engine function - Play Music
2108  * @remarks	Originally called 'music'
2109  */
music()2110 void MortevielleEngine::music() {
2111 	if (_soundOff)
2112 		return;
2113 
2114 	_reloadCFIEC = true;
2115 
2116 	Common::File f;
2117 	if (!f.open("mort.img")) {
2118 		// Some DOS versions use MORTP2 instead of MORT.IMG
2119 		// Some have both and they are identical
2120 		if (!f.open("mortp2"))
2121 			error("Missing file - mort.img");
2122 	}
2123 
2124 	int size = f.size();
2125 	byte *compMusicBuf = (byte *)malloc(sizeof(byte) * size);
2126 	byte *musicBuf = (byte *)malloc(sizeof(byte) * size * 2);
2127 	f.read(compMusicBuf, size);
2128 	f.close();
2129 
2130 	int musicSize = _soundManager->decodeMusic(compMusicBuf, musicBuf, size);
2131 	free(compMusicBuf);
2132 
2133 	_soundManager->playSong(musicBuf, musicSize, 5);
2134 	while (keyPressed())
2135 		getChar();
2136 
2137 	free(musicBuf);
2138 }
2139 
2140 /**
2141  * Engine function - Show title screen
2142  * @remarks	Originally called 'suite'
2143  */
showTitleScreen()2144 void MortevielleEngine::showTitleScreen() {
2145 	clearScreen();
2146 	handleDescriptionText(7, 2035);
2147 	_caff = 51;
2148 	_text->taffich();
2149 	testKeyboard();
2150 	delay(DISK_ACCESS_DELAY);
2151 	clearScreen();
2152 	draw(0, 0);
2153 
2154 	// FIXME: should be a DOS font here
2155 	Common::String cpr = "COPYRIGHT 1989 : LANKHOR";
2156 	_screenSurface->putxy(104 + 72 * kResolutionScaler, 185);
2157 	_screenSurface->drawString(cpr, 0);
2158 }
2159 
2160 /**
2161  * Draw picture
2162  * @remarks	Originally called 'dessine'
2163  */
draw(int x,int y)2164 void MortevielleEngine::draw(int x, int y) {
2165 	_mouse->hideMouse();
2166 	setPal(_numpal);
2167 	displayPicture(_curPict, x, y);
2168 	_mouse->showMouse();
2169 }
2170 
2171 /**
2172  * Draw right frame
2173  * @remarks	Originally called 'dessine_rouleau'
2174  */
drawRightFrame()2175 void MortevielleEngine::drawRightFrame() {
2176 	setPal(89);
2177 	_mouse->hideMouse();
2178 	displayPicture(_rightFramePict, 0, 0);
2179 	_mouse->showMouse();
2180 }
2181 
2182 /**
2183  * Read the current system time
2184  */
readclock()2185 int MortevielleEngine::readclock() {
2186 	return (int)(g_system->getMillis() / 1000);
2187 }
2188 
2189 /**
2190  * Engine function - Prepare room and hint string
2191  * @remarks	Originally called 'tinke'
2192  */
prepareRoom()2193 void MortevielleEngine::prepareRoom() {
2194 	int day, hour, minute;
2195 
2196 	_anyone = false;
2197 	updateHour(day, hour, minute);
2198 	if (day != _day) {
2199 		_day = day;
2200 		for (int i = 0; i < 9; i++) {
2201 			if (_charAnswerMax[i] > 0)
2202 				--_charAnswerMax[i];
2203 			_charAnswerCount[i] = 0;
2204 		}
2205 	}
2206 	if ((hour > _hour) || ((hour == 0) && (_hour == 23))) {
2207 		_hour = hour;
2208 		_minute = 0;
2209 		drawClock();
2210 		int hintCount = 0;
2211 		for (int i = 1; i <= 10; ++i) {
2212 			if (_coreVar._pctHintFound[i] == '*')
2213 				++hintCount;
2214 		}
2215 
2216 		Common::String pctStr;
2217 		if (hintCount == 10)
2218 			pctStr = "10";
2219 		else
2220 			pctStr = (unsigned char)(hintCount + 48);
2221 
2222 		_hintPctMessage = "[1][";
2223 		_hintPctMessage += getEngineString(S_SHOULD_HAVE_NOTICED);
2224 		_hintPctMessage += pctStr;
2225 		_hintPctMessage += '0';
2226 		_hintPctMessage += getEngineString(S_NUMBER_OF_HINTS);
2227 		_hintPctMessage += "][";
2228 		_hintPctMessage += getEngineString(S_OKAY);
2229 		_hintPctMessage += ']';
2230 	}
2231 	if (minute > _minute) {
2232 		_minute = 30;
2233 		drawClock();
2234 	}
2235 	if (_mouse->_pos.y < 12)
2236 		return;
2237 
2238 	if (!_outsideOnlyFl) {
2239 		if ((hour == 12) || ((hour > 18) && (hour < 21)) || ((hour >= 0) && (hour < 7)))
2240 			_inGameHourDuration = kTime2;
2241 		else
2242 			_inGameHourDuration = kTime1;
2243 		if ((_coreVar._faithScore > 33) && (_coreVar._faithScore < 66))
2244 			_inGameHourDuration -= (_inGameHourDuration / 3);
2245 
2246 		if (_coreVar._faithScore > 65)
2247 			_inGameHourDuration -= ((_inGameHourDuration / 3) * 2);
2248 
2249 		int newTime = readclock();
2250 		if ((newTime - _currentTime) > _inGameHourDuration) {
2251 			bool activeMenu = _menu->_menuActive;
2252 			_menu->eraseMenu();
2253 			_currentHourCount += ((newTime - _currentTime) / _inGameHourDuration);
2254 			_currentTime = newTime;
2255 			switch (_place) {
2256 			case GREEN_ROOM:
2257 			case DARKBLUE_ROOM:
2258 				setRandomPresenceGreenRoom(_coreVar._faithScore);
2259 				break;
2260 			case PURPLE_ROOM:
2261 				setRandomPresencePurpleRoom(_coreVar._faithScore);
2262 				break;
2263 			case BLUE_ROOM:
2264 				setRandomPresenceBlueRoom(_coreVar._faithScore);
2265 				break;
2266 			case RED_ROOM:
2267 			case GREEN_ROOM2:
2268 				setRandomPresenceRedRoom(_coreVar._faithScore);
2269 				break;
2270 			case JULIA_ROOM:
2271 				setRandomPresenceJuliaRoom(_coreVar._faithScore);
2272 				break;
2273 			case DINING_ROOM:
2274 				setRandomPresenceDiningRoom(_coreVar._faithScore);
2275 				break;
2276 			case BUREAU:
2277 				setRandomPresenceBureau(_coreVar._faithScore);
2278 				break;
2279 			case KITCHEN:
2280 				setRandomPresenceKitchen(_coreVar._faithScore);
2281 				break;
2282 			case ATTIC:
2283 			case CELLAR:
2284 				setRandomPresenceAttic(_coreVar._faithScore);
2285 				break;
2286 			case LANDING:
2287 			case ROOM26:
2288 				setRandomPresenceLanding(_coreVar._faithScore);
2289 				break;
2290 			case CHAPEL:
2291 				setRandomPresenceChapel(_coreVar._faithScore);
2292 				break;
2293 			}
2294 			if ((_savedBitIndex != 0) && (_currBitIndex != 10))
2295 				_savedBitIndex = _currBitIndex;
2296 
2297 			if ((_savedBitIndex == 0) && (_currBitIndex > 0)) {
2298 				if ((_coreVar._currPlace == ATTIC) || (_coreVar._currPlace == CELLAR)) {
2299 					initCaveOrCellar();
2300 				} else if (_currBitIndex == 10) {
2301 					_currBitIndex = 0;
2302 					if (!_uptodatePresence) {
2303 						_uptodatePresence = true;
2304 						_startTime = readclock();
2305 						if (getRandomNumber(1, 5) < 5) {
2306 							clearVerbBar();
2307 							prepareScreenType2();
2308 							displayTextInVerbBar(getEngineString(S_HEAR_NOISE));
2309 							int rand = (getRandomNumber(0, 4)) - 2;
2310 							_soundManager->startSpeech(1, rand, 1);
2311 							_soundManager->waitSpeech();
2312 							clearVerbBar();
2313 						}
2314 					}
2315 				}
2316 			}
2317 
2318 			if (activeMenu)
2319 				_menu->drawMenu();
2320 		}
2321 	}
2322 	_endTime = readclock();
2323 	if ((_uptodatePresence) && ((_endTime - _startTime) > 17)) {
2324 		getPresenceBitIndex(_place);
2325 		_uptodatePresence = false;
2326 		_startTime = 0;
2327 		if ((_coreVar._currPlace > OWN_ROOM) && (_coreVar._currPlace < DINING_ROOM))
2328 			_anyone = true;
2329 	}
2330 }
2331 
2332 /**
2333  * Engine function - Draw Clock
2334  * @remarks	Originally called 'pendule'
2335  */
drawClock()2336 void MortevielleEngine::drawClock() {
2337 	const int cv[2][12] = {
2338 		{  5,  8, 10,  8,  5,  0, -5, -8, -10, -8, -5,  0 },
2339 		{ -5, -3,  0,  3,  5,  6,  5,  3,   0, -3, -5, -6 }
2340 	};
2341 	const int x = 580;
2342 	const int y = 123;
2343 	const int rg = 9;
2344 
2345 	_mouse->hideMouse();
2346 
2347 	_screenSurface->drawRectangle(570, 118, 20, 10);
2348 	_screenSurface->drawRectangle(578, 114, 6, 18);
2349 
2350 	if (_minute == 0)
2351 		_screenSurface->drawLine(((uint)x >> 1) * kResolutionScaler, y, ((uint)x >> 1) * kResolutionScaler, (y - rg), 1);
2352 	else
2353 		_screenSurface->drawLine(((uint)x >> 1) * kResolutionScaler, y, ((uint)x >> 1) * kResolutionScaler, (y + rg), 1);
2354 
2355 	int hour12 = _hour;
2356 	if (hour12 > 12)
2357 		hour12 -= 12;
2358 	if (hour12 == 0)
2359 		hour12 = 12;
2360 
2361 	_screenSurface->drawLine(((uint)x >> 1) * kResolutionScaler, y, ((uint)(x + cv[0][hour12 - 1]) >> 1) * kResolutionScaler, y + cv[1][hour12 - 1], 1);
2362 	_mouse->showMouse();
2363 	_screenSurface->putxy(568, 154);
2364 
2365 	if (_hour > 11)
2366 		_screenSurface->drawString("PM ", 1);
2367 	else
2368 		_screenSurface->drawString("AM ", 1);
2369 
2370 	_screenSurface->putxy(550, 160);
2371 	if ((_day >= 0) && (_day <= 8)) {
2372 		Common::String tmp = getEngineString(S_DAY);
2373 		tmp.insertChar((char)(_day + 49), 0);
2374 		_screenSurface->drawString(tmp, 1);
2375 	}
2376 }
2377 
palette(int v1)2378 void MortevielleEngine::palette(int v1) {
2379 	warning("TODO: palette");
2380 }
2381 
2382 /**
2383  * Returns a substring of the given string
2384  * @param s		Source string
2385  * @param idx	Starting index (1 based)
2386  * @param size	Number of characters to return
2387  */
2388 
copy(const Common::String & s,int idx,size_t size)2389 Common::String MortevielleEngine::copy(const Common::String &s, int idx, size_t size) {
2390 	assert(idx + size < s.size());
2391 
2392 	// Copy the substring into a temporary buffer
2393 	char *tmp = new char[size + 1];
2394 	Common::strlcpy(tmp, s.c_str() + idx - 1, size + 1);
2395 
2396 	Common::String result(tmp);
2397 	delete[] tmp;
2398 	return result;
2399 }
2400 
2401 /**
2402  * Clear Screen
2403  * @remarks	Originally called 'hirs'
2404  */
clearScreen()2405 void MortevielleEngine::clearScreen() {
2406 	_screenSurface->clearScreen();
2407 }
2408 
2409 /**
2410  * Init room : Cave or Cellar
2411  * @remarks	Originally called 'cavegre'
2412  */
initCaveOrCellar()2413 void MortevielleEngine::initCaveOrCellar() {
2414 	_coreVar._faithScore += 2;
2415 	if (_coreVar._faithScore > 69)
2416 		_coreVar._faithScore += (_coreVar._faithScore / 10);
2417 	clearVerbBar();
2418 	prepareScreenType2();
2419 	displayTextInVerbBar(getEngineString(S_SOMEONE_ENTERS));
2420 	int rand = (getRandomNumber(0, 4)) - 2;
2421 	_soundManager->startSpeech(2, rand, 1);
2422 	_soundManager->waitSpeech();
2423 	// The original was doing here a useless loop.
2424 	// It has been removed
2425 
2426 	clearVerbBar();
2427 	displayAloneText();
2428 }
2429 
2430 /**
2431  * Display control menu string
2432  * @remarks	Originally called 'tctrm'
2433  */
displayControlMenu()2434 void MortevielleEngine::displayControlMenu() {
2435 	handleDescriptionText(2, (3000 + _controlMenu));
2436 	_controlMenu = 0;
2437 }
2438 
2439 /**
2440  * Display picture at a given coordinate
2441  * @remarks	Originally called 'pictout'
2442  */
displayPicture(const byte * pic,int x,int y)2443 void MortevielleEngine::displayPicture(const byte *pic, int x, int y) {
2444 	GfxSurface surface;
2445 	surface.decode(pic);
2446 	_screenSurface->drawPicture(surface, x, y);
2447 }
2448 
adzon()2449 void MortevielleEngine::adzon() {
2450 	Common::File f;
2451 
2452 	if (!f.open("don.mor"))
2453 		error("Missing file - don.mor");
2454 
2455 	f.read(_tabdon, 7 * 256);
2456 	f.close();
2457 
2458 	if (!f.open("bmor.mor"))
2459 		error("Missing file - bmor.mor");
2460 
2461 	f.read(&_tabdon[kFleche], 1916);
2462 	f.close();
2463 
2464 	// Read Right Frame Drawing
2465 	if (!f.open("dec.mor"))
2466 		error("Missing file - dec.mor");
2467 
2468 	free(_rightFramePict);
2469 	_rightFramePict = (byte *)malloc(sizeof(byte) * f.size());
2470 	f.read(_rightFramePict, f.size());
2471 	f.close();
2472 }
2473 
2474 /**
2475  * Returns the offset within the compressed image data resource of the desired image
2476  * @remarks	Originally called 'animof'
2477  */
getAnimOffset(int frameNum,int animNum)2478 int MortevielleEngine::getAnimOffset(int frameNum, int animNum) {
2479 	int animCount = _curAnim[1];
2480 	int aux = animNum;
2481 	if (frameNum != 1)
2482 		aux += animCount;
2483 
2484 	return (animCount << 2) + 2 + READ_BE_UINT16(&_curAnim[aux << 1]);
2485 }
2486 
2487 /**
2488  * Display text in description bar
2489  * @remarks	Originally called 'text1'
2490  */
displayTextInDescriptionBar(int x,int y,int nb,int mesgId)2491 void MortevielleEngine::displayTextInDescriptionBar(int x, int y, int nb, int mesgId) {
2492 	Common::String tmpStr = getString(mesgId);
2493 	if ((y == 182) && ((int)tmpStr.size() > nb))
2494 		y = 176;
2495 	_text->displayStr(tmpStr, x, y, nb, 20, _textColor);
2496 }
2497 
2498 /**
2499  * Display description text
2500  * @remarks	Originally called 'repon'
2501  */
handleDescriptionText(int f,int mesgId)2502 void MortevielleEngine::handleDescriptionText(int f, int mesgId) {
2503 	if ((mesgId > 499) && (mesgId < 563)) {
2504 		Common::String tmpStr = getString(mesgId - 501 + kInventoryStringIndex);
2505 
2506 		if ((int)tmpStr.size() > ((58 + (kResolutionScaler - 1) * 37) << 1))
2507 			_largestClearScreen = true;
2508 		else
2509 			_largestClearScreen = false;
2510 
2511 		clearDescriptionBar();
2512 		_text->displayStr(tmpStr, 8, 176, 85, 3, 5);
2513 	} else {
2514 		mapMessageId(mesgId);
2515 		switch (f) {
2516 		case 2:
2517 		case 8:
2518 			clearDescriptionBar();
2519 			prepareScreenType2();
2520 			displayTextInDescriptionBar(8, 182, 103, mesgId);
2521 			if ((mesgId == 68) || (mesgId == 69))
2522 				_coreVar._availableQuestion[40] = '*';
2523 			else if ((mesgId == 104) && (_caff == CELLAR)) {
2524 				_coreVar._availableQuestion[36] = '*';
2525 				if (_coreVar._availableQuestion[39] == '*') {
2526 					_coreVar._pctHintFound[3] = '*';
2527 					_coreVar._availableQuestion[38] = '*';
2528 				}
2529 			}
2530 			break;
2531 		case 1:
2532 		case 6:
2533 		case 9: {
2534 			int i;
2535 			if ((f == 1) || (f == 6))
2536 				i = 4;
2537 			else
2538 				i = 5;
2539 
2540 			Common::String tmpStr = getString(mesgId);
2541 			_text->displayStr(tmpStr, 80, 40, 60, 25, i);
2542 
2543 			if (mesgId == 180)
2544 				_coreVar._pctHintFound[6] = '*';
2545 			else if (mesgId == 179)
2546 				_coreVar._pctHintFound[10] = '*';
2547 			}
2548 			break;
2549 		case 7: {
2550 			prepareScreenType3();
2551 			Common::String tmpStr = getString(mesgId);
2552 			// CHECKME: original code seems to consider one extra character
2553 			// See text position in the 3rd intro screen
2554 			int size = tmpStr.size() + 1;
2555 			if (size < 40)
2556 				_text->displayStr(tmpStr, 252 - size * 3, 86, 50, 3, 5);
2557 			else
2558 				_text->displayStr(tmpStr, 144, 86, 50, 3, 5);
2559 			}
2560 			break;
2561 		default:
2562 			break;
2563 		}
2564 	}
2565 }
2566 
2567 /**
2568  * Recompute message Id
2569  * @remarks	Originally called 'modif'
2570  */
mapMessageId(int & mesgId)2571 void MortevielleEngine::mapMessageId(int &mesgId) {
2572 	if (mesgId == 26)
2573 		mesgId = 25;
2574 	else if ((mesgId > 29) && (mesgId < 36))
2575 		mesgId -= 4;
2576 	else if ((mesgId > 69) && (mesgId < 78))
2577 		mesgId -= 37;
2578 	else if ((mesgId > 99) && (mesgId < 194))
2579 		mesgId -= 59;
2580 	else if ((mesgId > 996) && (mesgId < 1000))
2581 		mesgId -= 862;
2582 	else if ((mesgId > 1500) && (mesgId < 1507))
2583 		mesgId -= 1363;
2584 	else if ((mesgId > 1507) && (mesgId < 1513))
2585 		mesgId -= 1364;
2586 	else if ((mesgId > 1999) && (mesgId < 2002))
2587 		mesgId -= 1851;
2588 	else if (mesgId == 2010)
2589 		mesgId = 151;
2590 	else if ((mesgId > 2011) && (mesgId < 2025))
2591 		mesgId -= 1860;
2592 	else if (mesgId == 2026)
2593 		mesgId = 165;
2594 	else if ((mesgId > 2029) && (mesgId < 2037))
2595 		mesgId -= 1864;
2596 	else if ((mesgId > 3000) && (mesgId < 3005))
2597 		mesgId -= 2828;
2598 	else if (mesgId == 4100)
2599 		mesgId = 177;
2600 	else if (mesgId == 4150)
2601 		mesgId = 178;
2602 	else if ((mesgId > 4151) && (mesgId < 4156))
2603 		mesgId -= 3973;
2604 	else if (mesgId == 4157)
2605 		mesgId = 183;
2606 	else if ((mesgId == 4160) || (mesgId == 4161))
2607 		mesgId -= 3976;
2608 }
2609 
2610 /**
2611  * Initialize open objects array
2612  * @remarks	Originally called 'initouv'
2613  */
resetOpenObjects()2614 void MortevielleEngine::resetOpenObjects() {
2615 	for (int i = 1; i <= 6; ++i)
2616 		_openObjects[i] = 0;
2617 	_openObjCount = 0;
2618 }
2619 
2620 /**
2621  * Display Text Block
2622  * @remarks	Originally called 'ecr2'
2623  */
displayTextBlock(Common::String text)2624 void MortevielleEngine::displayTextBlock(Common::String text) {
2625 	// Some dead code was present in the original: removed
2626 	_screenSurface->putxy(8, 177);
2627 	int tlig = 59 + (kResolutionScaler - 1) * 36;
2628 
2629 	if ((int)text.size() < tlig)
2630 		_screenSurface->drawString(text, 5);
2631 	else if ((int)text.size() < (tlig << 1)) {
2632 		_screenSurface->putxy(8, 176);
2633 		_screenSurface->drawString(copy(text, 1, (tlig - 1)), 5);
2634 		_screenSurface->putxy(8, 182);
2635 		_screenSurface->drawString(copy(text, tlig, tlig << 1), 5);
2636 	} else {
2637 		_largestClearScreen = true;
2638 		clearDescriptionBar();
2639 		_screenSurface->putxy(8, 176);
2640 		_screenSurface->drawString(copy(text, 1, (tlig - 1)), 5);
2641 		_screenSurface->putxy(8, 182);
2642 		_screenSurface->drawString(copy(text, tlig, ((tlig << 1) - 1)), 5);
2643 		_screenSurface->putxy(8, 190);
2644 		_screenSurface->drawString(copy(text, tlig << 1, tlig * 3), 5);
2645 	}
2646 }
2647 
displayTextInVerbBar(Common::String text)2648 void MortevielleEngine::displayTextInVerbBar(Common::String text) {
2649 	clearVerbBar();
2650 	_screenSurface->putxy(8, 192);
2651 	_screenSurface->drawString(text, 5);
2652 }
2653 
2654 /**
2655  * Display item in hand
2656  * @remarks	Originally called 'modobj'
2657  */
displayItemInHand(int objId)2658 void MortevielleEngine::displayItemInHand(int objId) {
2659 	Common::String strp = Common::String(' ');
2660 
2661 	if (objId != 500)
2662 		strp = getString(objId - 501 + kInventoryStringIndex);
2663 
2664 	_menu->setText(_menu->_inventoryMenu[8], strp);
2665 	_menu->disableMenuItem(_menu->_inventoryMenu[8]);
2666 }
2667 
2668 /**
2669  * Display empty hand
2670  * @remarks	Originally called 'maivid'
2671  */
displayEmptyHand()2672 void MortevielleEngine::displayEmptyHand() {
2673 	_coreVar._selectedObjectId = 0;
2674 	displayItemInHand(500);
2675 }
2676 
2677 /**
2678  * Set a random presence: Leo or Max
2679  * @remarks	Originally called 'chlm'
2680  */
checkLeoMaxRandomPresence()2681 int MortevielleEngine::checkLeoMaxRandomPresence() {
2682 	int retval = getRandomNumber(1, 2);
2683 	if (retval == 2)
2684 		retval = 128;
2685 
2686 	return retval;
2687 }
2688 
2689 /**
2690  * Reset room variables
2691  * @remarks	Originally called 'debloc'
2692  */
resetRoomVariables(int roomId)2693 void MortevielleEngine::resetRoomVariables(int roomId) {
2694 	_num = 0;
2695 	_x = 0;
2696 	_y = 0;
2697 	if ((roomId != ROOM26) && (roomId != LANDING))
2698 		resetPresenceInRooms(roomId);
2699 	_savedBitIndex = _currBitIndex;
2700 }
2701 
2702 /**
2703  * Compute presence stats
2704  * @remarks	Originally called 'ecfren'
2705  */
getPresenceStats(int & rand,int faithScore,int roomId)2706 int MortevielleEngine::getPresenceStats(int &rand, int faithScore, int roomId) {
2707 	if (roomId == OWN_ROOM)
2708 		displayAloneText();
2709 	int retVal = -500;
2710 	rand = 0;
2711 	if ( ((roomId == GREEN_ROOM) && (!_roomPresenceLuc) && (!_roomPresenceIda))
2712 	  || ((roomId == DARKBLUE_ROOM) && (!_roomPresenceGuy) && (!_roomPresenceEva)) )
2713 		retVal = getPresenceStatsGreenRoom();
2714 	if ((roomId == PURPLE_ROOM) && (!_purpleRoomPresenceLeo) && (!_juliaRoomPresenceLeo))
2715 		retVal = getPresenceStatsPurpleRoom();
2716 	if ( ((roomId == TOILETS) && (!_toiletsPresenceBobMax))
2717 	  || ((roomId == BATHROOM) && (!_bathRoomPresenceBobMax)) )
2718 		retVal = getPresenceStatsToilets();
2719 	if ((roomId == BLUE_ROOM) && (!_roomPresenceMax))
2720 		retVal = getPresenceStatsBlueRoom();
2721 	if ( ((roomId == RED_ROOM) && (!_roomPresenceBob))
2722 	  || ((roomId == GREEN_ROOM2) && (!_roomPresencePat)))
2723 		retVal = getPresenceStatsRedRoom();
2724 	if ((roomId == JULIA_ROOM) && (!_juliaRoomPresenceLeo) && (!_purpleRoomPresenceLeo))
2725 		retVal = 10;
2726 	if ( ((roomId == PURPLE_ROOM) && (_juliaRoomPresenceLeo))
2727 	  || ((roomId == JULIA_ROOM) && (_purpleRoomPresenceLeo)))
2728 		retVal = -400;
2729 	if (retVal != -500) {
2730 		retVal += faithScore;
2731 		rand = getRandomNumber(1, 100);
2732 	}
2733 
2734 	return retVal;
2735 }
2736 
2737 /**
2738  * Set presence flags
2739  * @remarks	Originally called 'becfren'
2740  */
setPresenceFlags(int roomId)2741 void MortevielleEngine::setPresenceFlags(int roomId) {
2742 	if ((roomId == GREEN_ROOM) || (roomId == DARKBLUE_ROOM)) {
2743 		int rand = getRandomNumber(1, 2);
2744 		if (roomId == GREEN_ROOM) {
2745 			if (rand == 1)
2746 				_roomPresenceLuc = true;
2747 			else
2748 				_roomPresenceIda = true;
2749 		} else { // roomId == DARKBLUE_ROOM
2750 			if (rand == 1)
2751 				_roomPresenceGuy = true;
2752 			else
2753 				_roomPresenceEva = true;
2754 		}
2755 	} else if (roomId == PURPLE_ROOM)
2756 		_purpleRoomPresenceLeo = true;
2757 	else if (roomId == TOILETS)
2758 		_toiletsPresenceBobMax = true;
2759 	else if (roomId == BLUE_ROOM)
2760 		_roomPresenceMax = true;
2761 	else if (roomId == RED_ROOM)
2762 		_roomPresenceBob = true;
2763 	else if (roomId == BATHROOM)
2764 		_bathRoomPresenceBobMax = true;
2765 	else if (roomId == GREEN_ROOM2)
2766 		_roomPresencePat = true;
2767 	else if (roomId == JULIA_ROOM)
2768 		_juliaRoomPresenceLeo = true;
2769 }
2770 
2771 /**
2772  * Initialize max answers per character
2773  * @remarks	Originally called 'init_nbrepm'
2774  */
initMaxAnswer()2775 void MortevielleEngine::initMaxAnswer() {
2776 	static const byte maxAnswer[9] = { 0, 4, 5, 6, 7, 5, 6, 5, 8 };
2777 
2778 	for (int idx = 0; idx < 9; ++idx) {
2779 		_charAnswerMax[idx] = maxAnswer[idx];
2780 		_charAnswerCount[idx] = 0;
2781 	}
2782 }
2783 
2784 /**
2785  * Get Presence
2786  * @remarks	Originally called 't11'
2787  */
getPresence(int roomId)2788 int MortevielleEngine::getPresence(int roomId) {
2789 	int retVal = 0;
2790 	int rand;
2791 
2792 	int pres = getPresenceStats(rand, _coreVar._faithScore, roomId);
2793 	_place = roomId;
2794 	if ((roomId > OWN_ROOM) && (roomId < DINING_ROOM)) {
2795 		if (pres != -500) {
2796 			if (rand > pres) {
2797 				displayAloneText();
2798 				retVal = 0;
2799 			} else {
2800 				setPresenceFlags(_place);
2801 				retVal = getPresenceBitIndex(_place);
2802 			}
2803 		} else
2804 			retVal = getPresenceBitIndex(_place);
2805 	}
2806 
2807 	if (roomId > JULIA_ROOM) {
2808 		if ((roomId > LANDING) && (roomId != CHAPEL) && (roomId != ROOM26))
2809 			displayAloneText();
2810 		else {
2811 			int h = 0;
2812 			switch (roomId) {
2813 			case DINING_ROOM:
2814 				pres = getPresenceStatsDiningRoom(h);
2815 				break;
2816 			case BUREAU:
2817 				pres = getPresenceStatsBureau(h);
2818 				break;
2819 			case KITCHEN:
2820 				pres = getPresenceStatsKitchen();
2821 				break;
2822 			case ATTIC:
2823 			case CELLAR:
2824 				pres = getPresenceStatsAttic();
2825 				break;
2826 			case LANDING:
2827 			case ROOM26:
2828 				pres = getPresenceStatsLanding();
2829 				break;
2830 			case CHAPEL:
2831 				pres = getPresenceStatsChapel(h);
2832 				break;
2833 			}
2834 			pres += _coreVar._faithScore;
2835 			rand = getRandomNumber(1, 100);
2836 			if (rand > pres) {
2837 				displayAloneText();
2838 				retVal = 0;
2839 			} else {
2840 				switch (roomId) {
2841 				case DINING_ROOM:
2842 					pres = setPresenceDiningRoom(h);
2843 					break;
2844 				case BUREAU:
2845 					pres = setPresenceBureau(h);
2846 					break;
2847 				case KITCHEN:
2848 				case ATTIC:
2849 				case CELLAR:
2850 					pres = setPresenceKitchen();
2851 					break;
2852 				case LANDING:
2853 				case ROOM26:
2854 					pres = setPresenceLanding();
2855 					break;
2856 				case CHAPEL:
2857 					pres = setPresenceChapel(h);
2858 					break;
2859 				}
2860 				retVal = pres;
2861 			}
2862 		}
2863 	}
2864 
2865 	return retVal;
2866 }
2867 
2868 /**
2869  * Display Question String
2870  * @remarks	Originally called 'writetp'
2871  */
displayQuestionText(Common::String s,int cmd)2872 void MortevielleEngine::displayQuestionText(Common::String s, int cmd) {
2873 	_screenSurface->drawString(s, cmd);
2874 }
2875 
2876 /**
2877  * Display animation frame
2878  * @remarks	Originally called 'aniof'
2879  */
displayAnimFrame(int frameNum,int animId)2880 void MortevielleEngine::displayAnimFrame(int frameNum, int animId) {
2881 	if ((_caff == BATHROOM) && ((animId == 4) || (animId == 5)))
2882 		return;
2883 
2884 	if ((_caff == DINING_ROOM) && (animId == 7))
2885 		animId = 6;
2886 	else if (_caff == KITCHEN) {
2887 		if (animId == 3)
2888 			animId = 4;
2889 		else if (animId == 4)
2890 			animId = 3;
2891 	}
2892 
2893 	int offset = getAnimOffset(frameNum, animId);
2894 
2895 	GfxSurface surface;
2896 	surface.decode(&_curAnim[offset]);
2897 	_screenSurface->drawPicture(surface, 0, 12);
2898 
2899 	prepareScreenType1();
2900 }
2901 
2902 /**
2903  * Draw Picture
2904  * @remarks	Originally called 'dessin'
2905  */
drawPicture()2906 void MortevielleEngine::drawPicture() {
2907 	clearUpperLeftPart();
2908 	if (_caff > 99) {
2909 		draw(60, 33);
2910 		_screenSurface->drawBox(118, 32, 291, 121, 15);         // Medium box
2911 	} else if (_caff > 69) {
2912 		draw(112, 48);           // Heads
2913 		_screenSurface->drawBox(222, 47, 155, 91, 15);
2914 	} else {
2915 		draw(0, 12);
2916 		prepareScreenType1();
2917 		if ((_caff < 30) || (_caff > 32)) {
2918 			for (int i = 1; i <= 6; ++i) {
2919 				if (_openObjects[i] != 0)
2920 					displayAnimFrame(1, _openObjects[i]);
2921 			}
2922 
2923 			switch (_caff) {
2924 			case ATTIC:
2925 				if (_coreVar._atticBallHoleObjectId == 141)
2926 					displayAnimFrame(1, 7);
2927 
2928 				if (_coreVar._atticRodHoleObjectId == 159)
2929 					displayAnimFrame(1, 6);
2930 				break;
2931 			case CELLAR:
2932 				if (_coreVar._cellarObjectId == 151)
2933 					displayAnimFrame(1, 2);
2934 				break;
2935 			case SECRET_PASSAGE:
2936 				if (_coreVar._secretPassageObjectId == 143)
2937 					displayAnimFrame(1, 1);
2938 				break;
2939 			case WELL:
2940 				if (_coreVar._wellObjectId != 0)
2941 					displayAnimFrame(1, 1);
2942 				break;
2943 			}
2944 		}
2945 
2946 		if (_caff < ROOM26)
2947 			startMusicOrSpeech(1);
2948 	}
2949 }
2950 
2951 /**
2952  * @remarks	Originally called 'afdes'
2953  */
drawPictureWithText()2954 void MortevielleEngine::drawPictureWithText() {
2955 	_text->taffich();
2956 	drawPicture();
2957 	_destinationOk = false;
2958 }
2959 
2960 /**
2961  * Engine function - Place
2962  * @remarks	Originally called 'tkey1'
2963  */
testKey(bool d)2964 void MortevielleEngine::testKey(bool d) {
2965 	bool quest = false;
2966 	int x, y;
2967 	bool click;
2968 
2969 	_mouse->hideMouse();
2970 	displayStatusInDescriptionBar('K');
2971 
2972 	// Wait for release from any key or mouse button
2973 	while (keyPressed())
2974 		_key = gettKeyPressed();
2975 
2976 	do {
2977 		_mouse->getMousePosition(x, y, click);
2978 		quest = keyPressed();
2979 		if (quest && shouldQuit())
2980 			return;
2981 	} while (click);
2982 
2983 	// Event loop
2984 	do {
2985 		if (d)
2986 			prepareRoom();
2987 		quest = keyPressed();
2988 		_mouse->getMousePosition(x, y, click);
2989 		if (shouldQuit())
2990 			return;
2991 	} while (!(quest || (click) || (d && _anyone)));
2992 	if (quest)
2993 		gettKeyPressed();
2994 	setMouseClick(false);
2995 	_mouse->showMouse();
2996 }
2997 
2998 /**
2999  * Display Narrative Picture
3000  * @remarks	Originally called 'tlu'
3001  */
displayNarrativePicture(int af,int ob)3002 void MortevielleEngine::displayNarrativePicture(int af, int ob) {
3003 	_caff = 32;
3004 	drawPictureWithText();
3005 	handleDescriptionText(6, ob + 4000);
3006 	handleDescriptionText(2, 999);
3007 	testKey(true);
3008 	_caff = af;
3009 	_currMenu = OPCODE_NONE;
3010 	_crep = 998;
3011 }
3012 
3013 /**
3014  * Display a message switching from a screen to another.
3015  * @remarks	Originally called 'messint'
3016  */
displayInterScreenMessage(int mesgId)3017 void MortevielleEngine::displayInterScreenMessage(int mesgId) {
3018 	clearUpperLeftPart();
3019 	clearDescriptionBar();
3020 	clearVerbBar();
3021 
3022 	GfxSurface surface;
3023 	surface.decode(_rightFramePict + 1008);
3024 	surface._offset.x = 80;
3025 	surface._offset.y = 40;
3026 	setPal(90);
3027 	_screenSurface->drawPicture(surface, 0, 0);
3028 	_screenSurface->drawPicture(surface, 0, 70);
3029 	handleDescriptionText(7, mesgId);
3030 	delay(DISK_ACCESS_DELAY);
3031 }
3032 
3033 /**
3034  * Prepare Display Text
3035  * @remarks	Originally called 'affrep'
3036  */
prepareDisplayText()3037 void MortevielleEngine::prepareDisplayText() {
3038 	_caff = _coreVar._currPlace;
3039 	_crep = _coreVar._currPlace;
3040 }
3041 
3042 /**
3043  * Exit room
3044  * @remarks	Originally called 'tsort'
3045  */
exitRoom()3046 void MortevielleEngine::exitRoom() {
3047 	if ((_openObjCount > 0) && (_coreVar._currPlace != OWN_ROOM)) {
3048 		if (_coreVar._faithScore < 50)
3049 			_coreVar._faithScore += 2;
3050 		else
3051 			_coreVar._faithScore += (_coreVar._faithScore / 10);
3052 	}
3053 
3054 	resetOpenObjects();
3055 
3056 	_roomDoorId = OWN_ROOM;
3057 	_curSearchObjId = 0;
3058 	resetRoomVariables(_coreVar._currPlace);
3059 }
3060 
3061 /**
3062  * get 'read' description
3063  * @remarks	Originally called 'st4'
3064  */
getReadDescription(int objId)3065 void MortevielleEngine::getReadDescription(int objId) {
3066 	_crep = 997;
3067 
3068 	switch (objId) {
3069 	case 114 :
3070 		_crep = 109;
3071 		break;
3072 	case 110 :
3073 		_crep = 107;
3074 		break;
3075 	case 158 :
3076 		_crep = 113;
3077 		break;
3078 	case 152:
3079 	case 153:
3080 	case 154:
3081 	case 155:
3082 	case 156:
3083 	case 150:
3084 	case 100:
3085 	case 157:
3086 	case 160:
3087 	case 161 :
3088 		displayNarrativePicture(_caff, objId);
3089 		break;
3090 	default:
3091 		break;
3092 	}
3093 }
3094 
3095 /**
3096  * get 'search' description
3097  * @remarks	Originally called 'st7'
3098  */
getSearchDescription(int objId)3099 void MortevielleEngine::getSearchDescription(int objId) {
3100 	switch (objId) {
3101 	case 116:
3102 	case 144:
3103 		_crep = 104;
3104 		break;
3105 	case 126:
3106 	case 111:
3107 		_crep = 108;
3108 		break;
3109 	case 132:
3110 		_crep = 111;
3111 		break;
3112 	case 142:
3113 		_crep = 112;
3114 		break;
3115 	default:
3116 		_crep = 183;
3117 		getReadDescription(objId);
3118 	}
3119 }
3120 
3121 /**
3122  * Menu up
3123  * @remarks	Originally called 'mennor'
3124  */
menuUp()3125 void MortevielleEngine::menuUp() {
3126 	_menu->menuUp(_currMenu);
3127 }
3128 
3129 /**
3130  * Draw discussion box
3131  * @remarks	Originally called 'premtet'
3132  */
drawDiscussionBox()3133 void MortevielleEngine::drawDiscussionBox() {
3134 	draw(10, 80);
3135 	_screenSurface->drawBox(18, 79, 155, 91, 15);
3136 }
3137 
3138 /**
3139  * Try to put an object somewhere
3140  * @remarks	Originally called 'ajchai'
3141  */
putObject()3142 void MortevielleEngine::putObject() {
3143 	int putId = kAcha + ((_curSearchObjId - 1) * 10) - 1;
3144 	int i;
3145 	for (i = 1; (i <= 9) && (_tabdon[putId + i] != 0); i++)
3146 		;
3147 
3148 	if (_tabdon[putId + i] == 0)
3149 		_tabdon[putId + i] = _coreVar._selectedObjectId;
3150 	else
3151 		_crep = 192;
3152 }
3153 
3154 /**
3155  * Check if inventory is full and, if not, add object in it.
3156  * @remarks	Originally called 'ajjer'
3157  */
addObjectToInventory(int objectId)3158 void MortevielleEngine::addObjectToInventory(int objectId) {
3159 	int i;
3160 
3161 	for (i = 1; (i <= 5) && (_coreVar._inventory[i] != 0); i++)
3162 		;
3163 
3164 	if (_coreVar._inventory[i] == 0) {
3165 		_coreVar._inventory[i] = objectId;
3166 		_menu->setInventoryText();
3167 	} else
3168 		// Inventory is full
3169 		_crep = 139;
3170 }
3171 
3172 /**
3173  * Interact with NPC
3174  * @remarks	Originally called 'quelquun'
3175  */
interactNPC()3176 void MortevielleEngine::interactNPC() {
3177 	if (_menu->_menuDisplayed)
3178 		_menu->eraseMenu();
3179 
3180 	endSearch();
3181 	_crep = 997;
3182 L1:
3183 	if (!_hiddenHero) {
3184 		if (_crep == 997)
3185 			_crep = 138;
3186 		handleDescriptionText(2, _crep);
3187 		if (_crep == 138)
3188 			_soundManager->startSpeech(5, 2, 1);
3189 		else
3190 			_soundManager->startSpeech(4, 4, 1);
3191 
3192 		if (_openObjCount == 0)
3193 			_coreVar._faithScore += 2;
3194 		else if (_coreVar._faithScore < 50)
3195 			_coreVar._faithScore += 4;
3196 		else
3197 			_coreVar._faithScore += 3 * (_coreVar._faithScore / 10);
3198 		exitRoom();
3199 		_menu->setDestinationText(LANDING);
3200 		int charIdx = convertBitIndexToCharacterIndex(_currBitIndex);
3201 		_caff = 69 + charIdx;
3202 		_crep = _caff;
3203 		_currMenu = MENU_DISCUSS;
3204 		_currAction = (_menu->_discussMenu[charIdx]._menuId << 8) | _menu->_discussMenu[charIdx]._actionId;
3205 		_syn = true;
3206 		_col = true;
3207 	} else {
3208 		if (getRandomNumber(1, 3) == 2) {
3209 			_hiddenHero = false;
3210 			_crep = 137;
3211 			goto L1;
3212 		} else {
3213 			handleDescriptionText(2, 136);
3214 			int rand = (getRandomNumber(0, 4)) - 2;
3215 			_soundManager->startSpeech(3, rand, 1);
3216 			clearDescriptionBar();
3217 			displayAloneText();
3218 			resetRoomVariables(MANOR_FRONT);
3219 			prepareDisplayText();
3220 		}
3221 	}
3222 	if (_menu->_menuDisplayed)
3223 		_menu->drawMenu();
3224 }
3225 
3226 /**
3227  * Search - Prepare next object
3228  * @remarks	Originally called 'tsuiv'
3229  */
prepareNextObject()3230 void MortevielleEngine::prepareNextObject() {
3231 	int objId;
3232 	int tabIdx = kAcha + ((_curSearchObjId - 1) * 10) - 1;
3233 	int localSeearchCount = 0;
3234 	do {
3235 		++localSeearchCount;
3236 		++_searchCount;
3237 		objId = _tabdon[tabIdx + _searchCount];
3238 	} while ((objId == 0) && (_searchCount <= 9));
3239 
3240 	if ((objId != 0) && (_searchCount < 11)) {
3241 		_is++;
3242 		_caff = objId;
3243 		_crep = _caff + 400;
3244 		if (_currBitIndex != 0)
3245 			// Someone is present in the room
3246 			_coreVar._faithScore += 2;
3247 	} else {
3248 		prepareDisplayText();
3249 		endSearch();
3250 		if (localSeearchCount > 9)
3251 			_crep = 131;
3252 	}
3253 }
3254 
3255 /**
3256  * Display Arrow status
3257  * @remarks	Originally called 'tfleche'
3258  */
displayStatusArrow()3259 void MortevielleEngine::displayStatusArrow() {
3260 	bool qust;
3261 	char touch;
3262 
3263 	if (_num == 9999)
3264 		return;
3265 
3266 	displayStatusInDescriptionBar((unsigned char)152);
3267 	bool inRect = false;
3268 	do {
3269 		touch = '\0';
3270 
3271 		do {
3272 			_mouse->moveMouse(qust, touch);
3273 			if (shouldQuit())
3274 				return;
3275 
3276 			if (getMouseClick())
3277 				inRect = (_mouse->_pos.x < 256 * kResolutionScaler) && (_mouse->_pos.y < 176) && (_mouse->_pos.y > 12);
3278 			prepareRoom();
3279 		} while (!(qust || inRect || _anyone));
3280 
3281 		if (qust && (touch == '\103'))
3282 			_dialogManager->show(_hintPctMessage);
3283 	} while (!((touch == '\73') || ((touch == '\104') && (_x != 0) && (_y != 0)) || (_anyone) || (inRect)));
3284 
3285 	if (touch == '\73')
3286 		_keyPressedEsc = true;
3287 
3288 	if (inRect) {
3289 		_x = _mouse->_pos.x;
3290 		_y = _mouse->_pos.y;
3291 	}
3292 }
3293 
3294 /**
3295  * Set coordinates
3296  * @remarks	Originally called 'tcoord'
3297  */
setCoordinates(int sx)3298 void MortevielleEngine::setCoordinates(int sx) {
3299 	int sy, ix, iy;
3300 	int ib;
3301 
3302 
3303 	_num = 0;
3304 	_crep = 999;
3305 	int a = 0;
3306 	int atdon = kAmzon + 3;
3307 	int cy = 0;
3308 	while (cy < _caff) {
3309 		a += _tabdon[atdon];
3310 		atdon += 4;
3311 		++cy;
3312 	}
3313 
3314 	if (_tabdon[atdon] == 0) {
3315 		_crep = 997;
3316 		return;
3317 	}
3318 
3319 	a += kFleche;
3320 	int cb = 0;
3321 	for (cy = 0; cy <= (sx - 2); ++cy) {
3322 		ib = (_tabdon[a + cb] << 8) + _tabdon[(a + cb + 1)];
3323 		cb += (ib * 4) + 2;
3324 	}
3325 	ib = (_tabdon[a + cb] << 8) + _tabdon[(a + cb + 1)];
3326 	if (ib == 0) {
3327 		_crep = 997;
3328 		return;
3329 	}
3330 
3331 	cy = 1;
3332 	do {
3333 		cb += 2;
3334 		sx = _tabdon[a + cb] * kResolutionScaler;
3335 		sy = _tabdon[(a + cb + 1)];
3336 		cb += 2;
3337 		ix = _tabdon[a + cb] * kResolutionScaler;
3338 		iy = _tabdon[(a + cb + 1)];
3339 		++cy;
3340 	} while (!(((_x >= sx) && (_x <= ix) && (_y >= sy) && (_y <= iy)) || (cy > ib)));
3341 
3342 	if ((_x >= sx) && (_x <= ix) && (_y >= sy) && (_y <= iy)) {
3343 		_num = cy - 1;
3344 		return;
3345 	}
3346 
3347 	_crep = 997;
3348 }
3349 
3350 /**
3351  * Display LOOK Screen
3352  * @remarks	Originally called 'treg'
3353  */
displayLookScreen(int objId)3354 void MortevielleEngine::displayLookScreen(int objId) {
3355 	int mdes = _caff;
3356 	_caff = objId;
3357 
3358 	if (((_caff > 29) && (_caff < 33)) || (_caff == 144) || (_caff == 147) || (_caff == 149) || (_currAction == _menu->_opcodeSLook)) {
3359 		drawPictureWithText();
3360 		if ((_caff > 29) && (_caff < 33))
3361 			handleDescriptionText(2, _caff);
3362 		else
3363 			handleDescriptionText(2, _caff + 400);
3364 		testKey(true);
3365 		_caff = mdes;
3366 		_currMenu = MENU_NONE;
3367 		_crep = 998;
3368 	} else {
3369 		_obpart = true;
3370 		_crep = _caff + 400;
3371 		_menu->setSearchMenu();
3372 	}
3373 }
3374 
3375 /**
3376  * Engine function - Put in hand
3377  * @remarks	Originally called 'avpoing'
3378  */
putInHand(int & objId)3379 void MortevielleEngine::putInHand(int &objId) {
3380 	_crep = 999;
3381 	if (_coreVar._selectedObjectId != 0)
3382 		addObjectToInventory(_coreVar._selectedObjectId);
3383 
3384 	// If inventory wasn't full
3385 	if (_crep != 139) {
3386 		displayItemInHand(objId + 400);
3387 		_coreVar._selectedObjectId = objId;
3388 		objId = 0;
3389 	}
3390 }
3391 
3392 /**
3393  * Search - Get the first object
3394  * @remarks	Originally called 'rechai'
3395  */
getFirstObject()3396 int MortevielleEngine::getFirstObject() {
3397 	int tmpPlace = _coreVar._currPlace;
3398 
3399 	if (_coreVar._currPlace == CRYPT)
3400 		tmpPlace = CELLAR;
3401 
3402 	return _tabdon[kAsearch + (tmpPlace * 7) + _num - 1];
3403 }
3404 
3405 /**
3406  * Check before leaving the secret passage
3407  * @remarks	Originally called 't23coul'
3408  */
checkLeaveSecretPassage()3409 int MortevielleEngine::checkLeaveSecretPassage() {
3410 	if (!checkInventory(143)) {
3411 		_crep = 1512;
3412 		loseGame();
3413 	}
3414 
3415 	return CELLAR;
3416 }
3417 
3418 /**
3419  * Display status character in description bar
3420  * @remarks	Originally called 'fenat'
3421  */
displayStatusInDescriptionBar(char stat)3422 void MortevielleEngine::displayStatusInDescriptionBar(char stat) {
3423 	_mouse->hideMouse();
3424 	_screenSurface->writeCharacter(Common::Point(306, 193), stat, 12);
3425 	_screenSurface->drawBox(300, 191, 16, 8, 15);
3426 	_mouse->showMouse();
3427 }
3428 
3429 /**
3430  * Test Keyboard
3431  * @remarks	Originally called 'teskbd'
3432  */
testKeyboard()3433 void MortevielleEngine::testKeyboard() {
3434 	if (keyPressed())
3435 		gettKeyPressed();
3436 }
3437 
3438 /**
3439  * Test Key Pressed
3440  * @remarks	Originally called 'testou'
3441  */
gettKeyPressed()3442 int MortevielleEngine::gettKeyPressed() {
3443 	char ch = getChar();
3444 
3445 	switch (ch)  {
3446 	case '\23' :
3447 		_soundOff = !_soundOff;
3448 		break;
3449 	case '\26' :
3450 		if ((_x26KeyCount == 1) || (_x26KeyCount == 2)) {
3451 			decodeNumber(&_cfiecBuffer[161 * 16], (_cfiecBufferSize - (161 * 16)) / 64);
3452 			++_x26KeyCount;
3453 
3454 			return 61;
3455 		}
3456 		break;
3457 	case '\33' :
3458 		if (keyPressed())
3459 			ch = getChar();
3460 		break;
3461 	default:
3462 		break;
3463 	}
3464 
3465 	return (int)ch;
3466 }
3467 
3468 } // End of namespace Mortevielle
3469