scummvm/engines/mortevielle/utils.cpp
2014-02-18 02:39:36 +01:00

3408 lines
81 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/*
* This code is based on original Mortville Manor DOS source code
* Copyright (c) 1987-1989 Lankhor
*/
#include "mortevielle/mortevielle.h"
#include "mortevielle/dialogs.h"
#include "mortevielle/menu.h"
#include "mortevielle/mouse.h"
#include "mortevielle/outtext.h"
#include "common/scummsys.h"
#include "graphics/cursorman.h"
namespace Mortevielle {
/**
* Check is a key was pressed
* It also delays the engine and check if the screen has to be updated
* @remarks Originally called 'keypressed'
*/
bool MortevielleEngine::keyPressed() {
// Check for any pending key presses
handleEvents();
// Check if it's time to draw the next frame
if (g_system->getMillis() > (_lastGameFrame + GAME_FRAME_DELAY)) {
_lastGameFrame = g_system->getMillis();
_screenSurface->updateScreen();
_debugger->onFrame();
}
// Delay briefly to keep CPU usage down
g_system->delayMillis(5);
// Return if there are any pending key presses
return !_keypresses.empty();
}
/**
* Wait for a keypress
* @remarks Originally called 'get_ch'
*/
int MortevielleEngine::getChar() {
bool end = false;
// If there isn't any pending keypress, wait until there is
while (!shouldQuit() && !end) {
end = keyPressed();
}
// Return the top keypress
return shouldQuit() ? 0 : _keypresses.pop();
}
/**
* Handle pending events
* @remarks Since the ScummVM screen surface is double height to handle 640x200 using 640x400,
* the mouse Y position is divided by 2 to keep the game thinking the Y goes from 0 - 199
*/
bool MortevielleEngine::handleEvents() {
Common::Event event;
if (!g_system->getEventManager()->pollEvent(event))
return false;
switch (event.type) {
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_LBUTTONUP:
case Common::EVENT_MOUSEMOVE:
_mousePos = Common::Point(event.mouse.x, event.mouse.y / 2);
_mouse->_pos.x = event.mouse.x;
_mouse->_pos.y = event.mouse.y / 2;
if (event.type == Common::EVENT_LBUTTONDOWN)
_mouseClick = true;
else if (event.type == Common::EVENT_LBUTTONUP)
_mouseClick = false;
break;
case Common::EVENT_KEYDOWN:
addKeypress(event);
break;
default:
break;
}
return true;
}
/**
* Add the specified key to the pending keypress stack
*/
void MortevielleEngine::addKeypress(Common::Event &evt) {
// Character to add
char ch = evt.kbd.ascii;
// Check for debugger
if ((evt.kbd.keycode == Common::KEYCODE_d) && (evt.kbd.flags & Common::KBD_CTRL)) {
// Attach to the debugger
_debugger->attach();
_debugger->onFrame();
} else if ((evt.kbd.keycode >= Common::KEYCODE_a) && (evt.kbd.keycode <= Common::KEYCODE_z)) {
// Handle alphabetic keys
if (evt.kbd.hasFlags(Common::KBD_CTRL))
ch = evt.kbd.keycode - Common::KEYCODE_a + 1;
else
ch = evt.kbd.keycode - Common::KEYCODE_a + 'A';
} else if ((evt.kbd.keycode >= Common::KEYCODE_F1) && (evt.kbd.keycode <= Common::KEYCODE_F12)) {
// Handle function keys
ch = 59 + evt.kbd.keycode - Common::KEYCODE_F1;
} else {
// Series of special cases
switch (evt.kbd.keycode) {
case Common::KEYCODE_KP4:
case Common::KEYCODE_LEFT:
ch = '4';
break;
case Common::KEYCODE_KP2:
case Common::KEYCODE_DOWN:
ch = '2';
break;
case Common::KEYCODE_KP6:
case Common::KEYCODE_RIGHT:
ch = '6';
break;
case Common::KEYCODE_KP8:
case Common::KEYCODE_UP:
ch = '8';
break;
case Common::KEYCODE_KP7:
ch = '7';
break;
case Common::KEYCODE_KP1:
ch = '1';
break;
case Common::KEYCODE_KP9:
ch = '9';
break;
case Common::KEYCODE_KP3:
ch = '3';
break;
case Common::KEYCODE_KP5:
ch = '5';
break;
case Common::KEYCODE_RETURN:
ch = '\13';
break;
case Common::KEYCODE_ESCAPE:
ch = '\33';
break;
default:
break;
}
}
if (ch != 0)
_keypresses.push(ch);
}
static const byte CURSOR_ARROW_DATA[16 * 16] = {
0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x0f, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/**
* Initialize the mouse
*/
void MortevielleEngine::initMouse() {
CursorMan.replaceCursor(CURSOR_ARROW_DATA, 16, 16, 0, 0, 0xff);
CursorMan.showMouse(true);
_mouse->initMouse();
}
/**
* Sets the mouse position
* @remarks Since the ScummVM screen surface is double height to handle 640x200 using 640x400,
* the mouse Y position is doubled to convert from 0-199 to 0-399
*/
void MortevielleEngine::setMousePos(const Common::Point &pt) {
// Adjust the passed position from simulated 640x200 to 640x400 co-ordinates
Common::Point newPoint(pt.x, (pt.y == 199) ? 399 : pt.y * 2);
if (newPoint != _mousePos)
// Warp the mouse to the new position
g_system->warpMouse(newPoint.x, newPoint.y);
// Save the new position
_mousePos = newPoint;
}
/**
* Delay by a given amount
*/
void MortevielleEngine::delay(int amount) {
uint32 endTime = g_system->getMillis() + amount;
while (g_system->getMillis() < endTime) {
if (g_system->getMillis() > (_lastGameFrame + GAME_FRAME_DELAY)) {
_lastGameFrame = g_system->getMillis();
_screenSurface->updateScreen();
_debugger->onFrame();
}
g_system->delayMillis(10);
}
}
/**
* Waits for the user to select an action, and then handles it
* @remarks Originally called tecran
*/
void MortevielleEngine::handleAction() {
const int lim = 20000;
int temps = 0;
char inkey = '\0';
bool funct = false;
clearVerbBar();
_controlMenu = 0;
if (!_keyPressedEsc) {
_menu->drawMenu();
_menu->_menuDisplayed = true;
temps = 0;
_key = 0;
funct = false;
inkey = '.';
_inMainGameLoop = true;
do {
_menu->updateMenu();
prepareRoom();
_mouse->moveMouse(funct, inkey);
if (shouldQuit())
return;
++temps;
if (keyPressed() || _mouseClick) {
_soundManager->_mixer->stopHandle(_soundManager->_soundHandle);
}
} while (!((_menu->_menuSelected) || (temps > lim) || (funct) || (_anyone)));
_inMainGameLoop = false;
_menu->eraseMenu();
_menu->_menuDisplayed = false;
if (_menu->_menuSelected && (_currMenu == MENU_SAVE)) {
Common::String saveName = Common::String::format("Savegame #%d", _currAction & 15);
_savegameManager->saveGame(_currAction & 15, saveName);
}
if (_menu->_menuSelected && (_currMenu == MENU_LOAD))
_savegameManager->loadGame((_currAction & 15) - 1);
if (inkey == '\103') { /* F9 */
temps = _dialogManager->show(_hintPctMessage);
return;
} else if (inkey == '\77') {
if ((_menuOpcode != OPCODE_NONE) && ((_currMenu == MENU_ACTION) || (_currMenu == MENU_SELF))) {
_currAction = _menuOpcode;
displayTextInVerbBar(getEngineString(S_IDEM));
} else
return;
} else if (inkey == '\104') {
if ((_x != 0) && (_y != 0))
_num = 9999;
return;
}
}
if (inkey == '\73') {
_quitGame = true;
hourToChar();
} else {
if ((funct) && (inkey != '\77'))
return;
if (temps > lim) {
handleDescriptionText(2, 141);
if (_num == 9999)
_num = 0;
} else {
_menuOpcode = _currMenu;
if ((_currMenu == MENU_ACTION) || (_currMenu == MENU_SELF))
_menuOpcode = _currAction;
bool handledOpcodeFl = false;
if (!_anyone) {
if ((_heroSearching) || (_obpart)) {
if (_mouse->_pos.y < 12)
return;
if ((_currAction == _menu->_opcodeSound) || (_currAction == _menu->_opcodeLift)) {
handledOpcodeFl = true;
if ((_currAction == _menu->_opcodeLift) || (_obpart)) {
endSearch();
_caff = _coreVar._currPlace;
_crep = 998;
} else
prepareNextObject();
menuUp();
}
}
}
do {
if (!handledOpcodeFl)
handleOpcode();
if ((_controlMenu == 0) && (! _loseGame) && (! _endGame)) {
_text->taffich();
if (_destinationOk) {
_destinationOk = false;
drawPicture();
}
if ((!_syn) || (_col))
handleDescriptionText(2, _crep);
}
} while (_syn);
if (_controlMenu != 0)
displayControlMenu();
}
}
}
/**
* Engine function - Init Places
* @remarks Originally called 'init_lieu'
*/
void MortevielleEngine::loadPlaces() {
Common::File f;
if (!f.open("MXX.mor"))
if (!f.open("MFXX.mor"))
error("Missing file - MXX.mor");
for (int i = 0; i < 7; ++i) {
for (int j = 0; j < 25; ++j)
_destinationArray[i][j] = f.readByte();
}
f.close();
}
/**
* Set Text Color
* @remarks Originally called 'text_color'
*/
void MortevielleEngine::setTextColor(int col) {
_textColor = col;
}
/**
* Prepare screen - Type 1!
* @remarks Originally called 'ecrf1'
*/
void MortevielleEngine::prepareScreenType1() {
// Large drawing
_screenSurface->drawBox(0, 11, 512, 164, 15);
}
/**
* Prepare room - Type 2!
* @remarks Originally called 'ecrf2'
*/
void MortevielleEngine::prepareScreenType2() {
setTextColor(5);
}
/**
* Prepare room - Type 3!
* @remarks Originally called 'ecrf7'
*/
void MortevielleEngine::prepareScreenType3() {
setTextColor(4);
}
/**
* Engine function - Update hour
* @remarks Originally called 'calch'
*/
void MortevielleEngine::updateHour(int &day, int &hour, int &minute) {
int newTime = readclock();
int th = _currentHourCount + ((newTime - _currentTime) / _inGameHourDuration);
minute = ((th % 2) + _currHalfHour) * 30;
hour = ((uint)th >> 1) + _currHour;
if (minute == 60) {
minute = 0;
++hour;
}
day = (hour / 24) + _currDay;
hour = hour - ((day - _currDay) * 24);
}
/**
* Engine function - Convert character index to bit index
* @remarks Originally called 'conv'
*/
int MortevielleEngine::convertCharacterIndexToBitIndex(int characterIndex) {
return 128 >> (characterIndex - 1);
}
/**
* Engine function - Convert bit index to character index
* @remarks Originally called 'tip'
*/
int MortevielleEngine::convertBitIndexToCharacterIndex(int bitIndex) {
int retVal = 0;
if (bitIndex == 128)
retVal = 1;
else if (bitIndex == 64)
retVal = 2;
else if (bitIndex == 32)
retVal = 3;
else if (bitIndex == 16)
retVal = 4;
else if (bitIndex == 8)
retVal = 5;
else if (bitIndex == 4)
retVal = 6;
else if (bitIndex == 2)
retVal = 7;
else if (bitIndex == 1)
retVal = 8;
return retVal;
}
/**
* Engine function - Reset presence in other rooms
* @remarks Originally called 't5'
*/
void MortevielleEngine::resetPresenceInRooms(int roomId) {
if (roomId == DINING_ROOM)
_outsideOnlyFl = false;
if (roomId != GREEN_ROOM) {
_roomPresenceLuc = false;
_roomPresenceIda = false;
}
if (roomId != PURPLE_ROOM)
_purpleRoomPresenceLeo = false;
if (roomId != DARKBLUE_ROOM) {
_roomPresenceGuy = false;
_roomPresenceEva = false;
}
if (roomId != BLUE_ROOM)
_roomPresenceMax = false;
if (roomId != RED_ROOM)
_roomPresenceBob = false;
if (roomId != GREEN_ROOM2)
_roomPresencePat = false;
if (roomId != TOILETS)
_toiletsPresenceBobMax = false;
if (roomId != BATHROOM)
_bathRoomPresenceBobMax = false;
if (roomId != JULIA_ROOM)
_juliaRoomPresenceLeo = false;
}
/**
* Engine function - Show the people present in the given room
* @remarks Originally called 'affper'
*/
void MortevielleEngine::showPeoplePresent(int bitIndex) {
int xp = 580 - (_screenSurface->getStringWidth("LEO") / 2);
for (int i = 1; i <= 8; ++i)
_menu->disableMenuItem(_menu->_discussMenu[i]);
clearUpperRightPart();
if ((bitIndex & 128) == 128) {
_screenSurface->putxy(xp, 24);
_screenSurface->drawString("LEO", 4);
_menu->enableMenuItem(_menu->_discussMenu[1]);
}
if ((bitIndex & 64) == 64) {
_screenSurface->putxy(xp, 32);
_screenSurface->drawString("PAT", 4);
_menu->enableMenuItem(_menu->_discussMenu[2]);
}
if ((bitIndex & 32) == 32) {
_screenSurface->putxy(xp, 40);
_screenSurface->drawString("GUY", 4);
_menu->enableMenuItem(_menu->_discussMenu[3]);
}
if ((bitIndex & 16) == 16) {
_screenSurface->putxy(xp, 48);
_screenSurface->drawString("EVA", 4);
_menu->enableMenuItem(_menu->_discussMenu[4]);
}
if ((bitIndex & 8) == 8) {
_screenSurface->putxy(xp, 56);
_screenSurface->drawString("BOB", 4);
_menu->enableMenuItem(_menu->_discussMenu[5]);
}
if ((bitIndex & 4) == 4) {
_screenSurface->putxy(xp, 64);
_screenSurface->drawString("LUC", 4);
_menu->enableMenuItem(_menu->_discussMenu[6]);
}
if ((bitIndex & 2) == 2) {
_screenSurface->putxy(xp, 72);
_screenSurface->drawString("IDA", 4);
_menu->enableMenuItem(_menu->_discussMenu[7]);
}
if ((bitIndex & 1) == 1) {
_screenSurface->putxy(xp, 80);
_screenSurface->drawString("MAX", 4);
_menu->enableMenuItem(_menu->_discussMenu[8]);
}
_currBitIndex = bitIndex;
}
/**
* Engine function - Select random characters
* @remarks Originally called 'choix'
*/
int MortevielleEngine::selectCharacters(int min, int max) {
bool invertSelection = false;
int rand = getRandomNumber(min, max);
if (rand > 4) {
rand = 8 - rand;
invertSelection = true;
}
int i = 0;
int retVal = 0;
while (i < rand) {
int charIndex = getRandomNumber(1, 8);
int charBitIndex = convertCharacterIndexToBitIndex(charIndex);
if ((retVal & charBitIndex) != charBitIndex) {
++i;
retVal |= charBitIndex;
}
}
if (invertSelection)
retVal = 255 - retVal;
return retVal;
}
/**
* Engine function - Get Presence Statistics - Green Room
* @remarks Originally called 'cpl1'
*/
int MortevielleEngine::getPresenceStatsGreenRoom() {
int day, hour, minute;
int retVal = 0;
updateHour(day, hour, minute);
// The original uses an || instead of an &&, resulting
// in an always true condition. Based on the other tests,
// and on other scenes, we use an && instead.
if ((hour > 7) && (hour < 11))
retVal = 25;
else if ((hour > 10) && (hour < 14))
retVal = 35;
else if ((hour > 13) && (hour < 16))
retVal = 50;
else if ((hour > 15) && (hour < 18))
retVal = 5;
else if ((hour > 17) && (hour < 22))
retVal = 35;
else if ((hour > 21) && (hour < 24))
retVal = 50;
else if ((hour >= 0) && (hour < 8))
retVal = 70;
_menu->updateMenu();
return retVal;
}
/**
* Engine function - Get Presence Statistics - Purple Room
* @remarks Originally called 'cpl2'
*/
int MortevielleEngine::getPresenceStatsPurpleRoom() {
int day, hour, minute;
int retVal = 0;
updateHour(day, hour, minute);
if ((hour > 7) && (hour < 11))
retVal = -2;
else if (hour == 11)
retVal = 100;
else if ((hour > 11) && (hour < 23))
retVal = 10;
else if (hour == 23)
retVal = 20;
else if ((hour >= 0) && (hour < 8))
retVal = 50;
return retVal;
}
/**
* Engine function - Get Presence Statistics - Toilets
* @remarks Originally called 'cpl3'
*/
int MortevielleEngine::getPresenceStatsToilets() {
int day, hour, minute;
int retVal = 0;
updateHour(day, hour, minute);
if (((hour > 8) && (hour < 10)) || ((hour > 19) && (hour < 24)))
retVal = 34;
else if (((hour > 9) && (hour < 20)) || ((hour >= 0) && (hour < 9)))
retVal = 0;
return retVal;
}
/**
* Engine function - Get Presence Statistics - Blue Room
* @remarks Originally called 'cpl5'
*/
int MortevielleEngine::getPresenceStatsBlueRoom() {
int day, hour, minute;
int retVal = 0;
updateHour(day, hour, minute);
if ((hour > 6) && (hour < 10))
retVal = 0;
else if (hour == 10)
retVal = 100;
else if ((hour > 10) && (hour < 24))
retVal = 15;
else if ((hour >= 0) && (hour < 7))
retVal = 50;
return retVal;
}
/**
* Engine function - Get Presence Statistics - Red Room
* @remarks Originally called 'cpl6'
*/
int MortevielleEngine::getPresenceStatsRedRoom() {
int day, hour, minute;
int retVal = 0;
updateHour(day, hour, minute);
if (((hour > 7) && (hour < 13)) || ((hour > 17) && (hour < 20)))
retVal = -2;
else if (((hour > 12) && (hour < 17)) || ((hour > 19) && (hour < 24)))
retVal = 35;
else if (hour == 17)
retVal = 100;
else if ((hour >= 0) && (hour < 8))
retVal = 60;
return retVal;
}
/**
* Shows the "you are alone" message in the status area
* on the right hand side of the screen
* @remarks Originally called 'person'
*/
void MortevielleEngine::displayAloneText() {
for (int i = 1; i <= 8; ++i)
_menu->disableMenuItem(_menu->_discussMenu[i]);
Common::String sYou = getEngineString(S_YOU);
Common::String sAre = getEngineString(S_ARE);
Common::String sAlone = getEngineString(S_ALONE);
clearUpperRightPart();
_screenSurface->putxy(580 - (_screenSurface->getStringWidth(sYou) / 2), 30);
_screenSurface->drawString(sYou, 4);
_screenSurface->putxy(580 - (_screenSurface->getStringWidth(sAre) / 2), 50);
_screenSurface->drawString(sAre, 4);
_screenSurface->putxy(580 - (_screenSurface->getStringWidth(sAlone) / 2), 70);
_screenSurface->drawString(sAlone, 4);
_currBitIndex = 0;
}
/**
* Engine function - Get Presence Statistics - Room Bureau
* @remarks Originally called 'cpl10'
*/
int MortevielleEngine::getPresenceStatsDiningRoom(int &hour) {
int day, minute;
int retVal = 0;
updateHour(day, hour, minute);
if (((hour > 7) && (hour < 11)) || ((hour > 11) && (hour < 14)) || ((hour > 18) && (hour < 21)))
retVal = 100;
else if ((hour == 11) || ((hour > 20) && (hour < 24)))
retVal = 45;
else if (((hour > 13) && (hour < 17)) || (hour == 18))
retVal = 35;
else if (hour == 17)
retVal = 60;
else if ((hour >= 0) && (hour < 8))
retVal = 5;
return retVal;
}
/**
* Engine function - Get Presence Statistics - Room Bureau
* @remarks Originally called 'cpl11'
*/
int MortevielleEngine::getPresenceStatsBureau(int &hour) {
int day, minute;
int retVal = 0;
updateHour(day, hour, minute);
if (((hour > 8) && (hour < 12)) || ((hour > 20) && (hour < 24)))
retVal = 25;
else if (((hour > 11) && (hour < 14)) || ((hour > 18) && (hour < 21)))
retVal = 5;
else if ((hour > 13) && (hour < 17))
retVal = 55;
else if ((hour > 16) && (hour < 19))
retVal = 45;
else if ((hour >= 0) && (hour < 9))
retVal = 0;
return retVal;
}
/**
* Engine function - Get Presence Statistics - Room Kitchen
* @remarks Originally called 'cpl12'
*/
int MortevielleEngine::getPresenceStatsKitchen() {
int day, hour, minute;
int retVal = 0;
updateHour(day, hour, minute);
if (((hour > 8) && (hour < 15)) || ((hour > 16) && (hour < 22)))
retVal = 55;
else if (((hour > 14) && (hour < 17)) || ((hour > 21) && (hour < 24)))
retVal = 25;
else if ((hour >= 0) && (hour < 5))
retVal = 0;
else if ((hour > 4) && (hour < 9))
retVal = 15;
return retVal;
}
/**
* Engine function - Get Presence Statistics - Room Attic
* @remarks Originally called 'cpl13'
*/
int MortevielleEngine::getPresenceStatsAttic() {
return 0;
}
/**
* Engine function - Get Presence Statistics - Room Landing
* @remarks Originally called 'cpl15'
*/
int MortevielleEngine::getPresenceStatsLanding() {
int day, hour, minute;
int retVal = 0;
updateHour(day, hour, minute);
if ((hour > 7) && (hour < 12))
retVal = 25;
else if ((hour > 11) && (hour < 14))
retVal = 0;
else if ((hour > 13) && (hour < 18))
retVal = 10;
else if ((hour > 17) && (hour < 20))
retVal = 55;
else if ((hour > 19) && (hour < 22))
retVal = 5;
else if ((hour > 21) && (hour < 24))
retVal = 15;
else if ((hour >= 0) && (hour < 8))
retVal = -15;
return retVal;
}
/**
* Engine function - Get Presence Statistics - Room Chapel
* @remarks Originally called 'cpl20'
*/
int MortevielleEngine::getPresenceStatsChapel(int &hour) {
int day, minute;
int retVal = 0;
updateHour(day, hour, minute);
if (hour == 10)
retVal = 65;
else if ((hour > 10) && (hour < 21))
retVal = 5;
else if ((hour > 20) && (hour < 24))
retVal = -15;
else if ((hour >= 0) && (hour < 5))
retVal = -300;
else if ((hour > 4) && (hour < 10))
retVal = -5;
return retVal;
}
/**
* Engine function - Check who is in the Green Room
* @remarks Originally called 'quelq1'
*/
void MortevielleEngine::setPresenceGreenRoom(int roomId) {
int rand = getRandomNumber(1, 2);
if (roomId == GREEN_ROOM) {
if (rand == 1)
_roomPresenceLuc = true;
else
_roomPresenceIda = true;
} else if (roomId == DARKBLUE_ROOM) {
if (rand == 1)
_roomPresenceGuy = true;
else
_roomPresenceEva = true;
}
_currBitIndex = 10;
}
/**
* Engine function - Check who is in the Purple Room
* @remarks Originally called 'quelq2'
*/
void MortevielleEngine::setPresencePurpleRoom() {
if (_place == PURPLE_ROOM)
_purpleRoomPresenceLeo = true;
else
_juliaRoomPresenceLeo = true;
_currBitIndex = 10;
}
/**
* Engine function - Check who is in the Blue Room
* @remarks Originally called 'quelq5'
*/
void MortevielleEngine::setPresenceBlueRoom() {
_roomPresenceMax = true;
_currBitIndex = 10;
}
/**
* Engine function - Check who is in the Red Room
* @remarks Originally called 'quelq6'
*/
void MortevielleEngine::setPresenceRedRoom(int roomId) {
if (roomId == RED_ROOM)
_roomPresenceBob = true;
else if (roomId == GREEN_ROOM2)
_roomPresencePat = true;
_currBitIndex = 10;
}
/**
* Engine function - Check who is in the Dining Room
* @remarks Originally called 'quelq10'
*/
int MortevielleEngine::setPresenceDiningRoom(int hour) {
int retVal = 0;
if ((hour >= 0) && (hour < 8))
retVal = checkLeoMaxRandomPresence();
else {
int min = 0, max = 0;
if ((hour > 7) && (hour < 10)) {
min = 5;
max = 7;
} else if ((hour > 9) && (hour < 12)) {
min = 1;
max = 4;
} else if (((hour > 11) && (hour < 15)) || ((hour > 18) && (hour < 21))) {
min = 6;
max = 8;
} else if (((hour > 14) && (hour < 19)) || ((hour > 20) && (hour < 24))) {
min = 1;
max = 5;
}
retVal = selectCharacters(min, max);
}
showPeoplePresent(retVal);
return retVal;
}
/**
* Engine function - Check who is in the Bureau
* @remarks Originally called 'quelq11'
*/
int MortevielleEngine::setPresenceBureau(int hour) {
int retVal = 0;
if ((hour >= 0) && (hour < 8))
retVal = checkLeoMaxRandomPresence();
else {
int min = 0, max = 0;
if (((hour > 7) && (hour < 10)) || ((hour > 20) && (hour < 24))) {
min = 1;
max = 3;
} else if (((hour > 9) && (hour < 12)) || ((hour > 13) && (hour < 19))) {
min = 1;
max = 4;
} else if (((hour > 11) && (hour < 14)) || ((hour > 18) && (hour < 21))) {
min = 1;
max = 2;
}
retVal = selectCharacters(min, max);
}
showPeoplePresent(retVal);
return retVal;
}
/**
* Engine function - Check who is in the Kitchen
* @remarks Originally called 'quelq12'
*/
int MortevielleEngine::setPresenceKitchen() {
int retVal = checkLeoMaxRandomPresence();
showPeoplePresent(retVal);
return retVal;
}
/**
* Engine function - Check who is in the Landing
* @remarks Originally called 'quelq15'
*/
int MortevielleEngine::setPresenceLanding() {
bool test = false;
int rand = 0;
do {
rand = getRandomNumber(1, 8);
test = (((rand == 1) && (_purpleRoomPresenceLeo || _juliaRoomPresenceLeo)) ||
((rand == 2) && _roomPresencePat) ||
((rand == 3) && _roomPresenceGuy) ||
((rand == 4) && _roomPresenceEva) ||
((rand == 5) && _roomPresenceBob) ||
((rand == 6) && _roomPresenceLuc) ||
((rand == 7) && _roomPresenceIda) ||
((rand == 8) && _roomPresenceMax));
} while (test);
int retVal = convertCharacterIndexToBitIndex(rand);
showPeoplePresent(retVal);
return retVal;
}
/**
* Engine function - Check who is in the chapel
* @remarks Originally called 'quelq20'
*/
int MortevielleEngine::setPresenceChapel(int hour) {
int retVal = 0;
if (((hour >= 0) && (hour < 10)) || ((hour > 18) && (hour < 24)))
retVal = checkLeoMaxRandomPresence();
else {
int min = 0, max = 0;
if ((hour > 9) && (hour < 12)) {
min = 3;
max = 7;
} else if ((hour > 11) && (hour < 18)) {
min = 1;
max = 2;
} else if (hour == 18) {
min = 2;
max = 4;
}
retVal = selectCharacters(min, max);
}
showPeoplePresent(retVal);
return retVal;
}
/**
* Engine function - Get the answer after you known a door
* @remarks Originally called 'frap'
*/
void MortevielleEngine::getKnockAnswer() {
int day, hour, minute;
updateHour(day, hour, minute);
if ((hour >= 0) && (hour < 8))
_crep = 190;
else {
if (getRandomNumber(1, 100) > 70)
_crep = 190;
else
_crep = 147;
}
}
/**
* Engine function - Get Room Presence Bit Index
* @remarks Originally called 'nouvp'
*/
int MortevielleEngine::getPresenceBitIndex(int roomId) {
int bitIndex = 0;
if (roomId == GREEN_ROOM) {
if (_roomPresenceLuc)
bitIndex = 4; // LUC
if (_roomPresenceIda)
bitIndex = 2; // IDA
} else if ( ((roomId == PURPLE_ROOM) && (_purpleRoomPresenceLeo))
|| ((roomId == JULIA_ROOM) && (_juliaRoomPresenceLeo)))
bitIndex = 128; // LEO
else if (roomId == DARKBLUE_ROOM) {
if (_roomPresenceGuy)
bitIndex = 32; // GUY
if (_roomPresenceEva)
bitIndex = 16; // EVA
} else if ((roomId == BLUE_ROOM) && (_roomPresenceMax))
bitIndex = 1; // MAX
else if ((roomId == RED_ROOM) && (_roomPresenceBob))
bitIndex = 8; // BOB
else if ((roomId == GREEN_ROOM2) && (_roomPresencePat))
bitIndex = 64; // PAT
else if ( ((roomId == TOILETS) && (_toiletsPresenceBobMax))
|| ((roomId == BATHROOM) && (_bathRoomPresenceBobMax)) )
bitIndex = 9; // BOB + MAX
if (bitIndex != 9)
showPeoplePresent(bitIndex);
return bitIndex;
}
/**
* Engine function - initGame
* @remarks Originally called 'dprog'
*/
void MortevielleEngine::initGame() {
_place = MANOR_FRONT;
_currentHourCount = 0;
if (!_coreVar._alreadyEnteredManor)
_outsideOnlyFl = true;
_inGameHourDuration = kTime1;
_currentTime = readclock();
}
/**
* Engine function - Set Random Presence - Green Room
* @remarks Originally called 'pl1'
*/
void MortevielleEngine::setRandomPresenceGreenRoom(int faithScore) {
if ( ((_place == GREEN_ROOM) && (!_roomPresenceLuc) && (!_roomPresenceIda))
|| ((_place == DARKBLUE_ROOM) && (!_roomPresenceGuy) && (!_roomPresenceEva)) ) {
int p = getPresenceStatsGreenRoom();
p += faithScore;
if (getRandomNumber(1, 100) > p)
displayAloneText();
else
setPresenceGreenRoom(_place);
}
}
/**
* Engine function - Set Random Presence - Purple Room
* @remarks Originally called 'pl2'
*/
void MortevielleEngine::setRandomPresencePurpleRoom(int faithScore) {
if (!_purpleRoomPresenceLeo) {
int p = getPresenceStatsPurpleRoom();
p += faithScore;
if (getRandomNumber(1, 100) > p)
displayAloneText();
else
setPresencePurpleRoom();
}
}
/**
* Engine function - Set Random Presence - Blue Room
* @remarks Originally called 'pl5'
*/
void MortevielleEngine::setRandomPresenceBlueRoom(int faithScore) {
if (!_roomPresenceMax) {
int p = getPresenceStatsBlueRoom();
p += faithScore;
if (getRandomNumber(1, 100) > p)
displayAloneText();
else
setPresenceBlueRoom();
}
}
/**
* Engine function - Set Random Presence - Red Room
* @remarks Originally called 'pl6'
*/
void MortevielleEngine::setRandomPresenceRedRoom(int faithScore) {
if ( ((_place == RED_ROOM) && (!_roomPresenceBob))
|| ((_place == GREEN_ROOM2) && (!_roomPresencePat)) ) {
int p = getPresenceStatsRedRoom();
p += faithScore;
if (getRandomNumber(1, 100) > p)
displayAloneText();
else
setPresenceRedRoom(_place);
}
}
/**
* Engine function - Set Random Presence - Room 9
* @remarks Originally called 'pl9'
*/
void MortevielleEngine::setRandomPresenceJuliaRoom(int faithScore) {
if (!_juliaRoomPresenceLeo) {
faithScore = -10;
if (getRandomNumber(1, 100) > faithScore) // always true?
displayAloneText();
else
setPresencePurpleRoom();
}
}
/**
* Engine function - Set Random Presence - Dining Room
* @remarks Originally called 'pl10'
*/
void MortevielleEngine::setRandomPresenceDiningRoom(int faithScore) {
int h;
int p = getPresenceStatsDiningRoom(h);
p += faithScore;
if (getRandomNumber(1, 100) > p)
displayAloneText();
else
setPresenceDiningRoom(h);
}
/**
* Engine function - Set Random Presence - Bureau
* @remarks Originally called 'pl11'
*/
void MortevielleEngine::setRandomPresenceBureau(int faithScore) {
int h;
int p = getPresenceStatsBureau(h);
p += faithScore;
if (getRandomNumber(1, 100) > p)
displayAloneText();
else
setPresenceBureau(h);
}
/**
* Engine function - Set Random Presence - Kitchen
* @remarks Originally called 'pl12'
*/
void MortevielleEngine::setRandomPresenceKitchen(int faithScore) {
int p = getPresenceStatsKitchen();
p += faithScore;
if (getRandomNumber(1, 100) > p)
displayAloneText();
else
setPresenceKitchen();
}
/**
* Engine function - Set Random Presence - Attic / Cellar
* @remarks Originally called 'pl13'
*/
void MortevielleEngine::setRandomPresenceAttic(int faithScore) {
int p = getPresenceStatsAttic();
p += faithScore;
if (getRandomNumber(1, 100) > p)
displayAloneText();
else
setPresenceKitchen();
}
/**
* Engine function - Set Random Presence - Landing
* @remarks Originally called 'pl15'
*/
void MortevielleEngine::setRandomPresenceLanding(int faithScore) {
int p = getPresenceStatsLanding();
p += faithScore;
if (getRandomNumber(1, 100) > p)
displayAloneText();
else
setPresenceLanding();
}
/**
* Engine function - Set Random Presence - Chapel
* @remarks Originally called 'pl20'
*/
void MortevielleEngine::setRandomPresenceChapel(int faithScore) {
int h;
int p = getPresenceStatsChapel(h);
p += faithScore;
if (getRandomNumber(1, 100) > p)
displayAloneText();
else
setPresenceChapel(h);
}
/**
* Start music or speech
* @remarks Originally called 'musique'
*/
void MortevielleEngine::startMusicOrSpeech(int so) {
if (so == 0) {
/* musik(0) */
;
} else if ((!_introSpeechPlayed) && (!_coreVar._alreadyEnteredManor)) {
// Type 1: Speech
_soundManager->startSpeech(10, 1, 1);
_introSpeechPlayed = true;
} else {
if (((_coreVar._currPlace == MOUNTAIN) || (_coreVar._currPlace == MANOR_FRONT) || (_coreVar._currPlace == MANOR_BACK)) && (getRandomNumber(1, 3) == 2))
// Type 1: Speech
_soundManager->startSpeech(9, getRandomNumber(2, 4), 1);
else if ((_coreVar._currPlace == CHAPEL) && (getRandomNumber(1, 2) == 1))
// Type 1: Speech
_soundManager->startSpeech(8, 1, 1);
else if ((_coreVar._currPlace == WELL) && (getRandomNumber(1, 2) == 2))
// Type 1: Speech
_soundManager->startSpeech(12, 1, 1);
else if (_coreVar._currPlace == INSIDE_WELL)
// Type 1: Speech
_soundManager->startSpeech(13, 1, 1);
else
// Type 2 : music
_soundManager->startSpeech(getRandomNumber(1, 17), 1, 2);
}
}
/**
* Engine function - You lose!
* @remarks Originally called 'tperd'
*/
void MortevielleEngine::loseGame() {
resetOpenObjects();
_roomDoorId = OWN_ROOM;
_curSearchObjId = 0;
_menu->unsetSearchMenu();
if (!_outsideOnlyFl)
getPresence(MANOR_FRONT);
_loseGame = true;
clearUpperLeftPart();
_screenSurface->drawBox(60, 35, 400, 50, 15);
handleDescriptionText(9, _crep);
clearDescriptionBar();
clearVerbBar();
_col = false;
_syn = false;
_destinationOk = false;
}
/**
* Engine function - Check inventory for a given object
* @remarks Originally called 'cherjer'
*/
bool MortevielleEngine::checkInventory(int objectId) {
bool retVal = false;
for (int i = 1; i <= 6; ++i)
retVal = (retVal || (_coreVar._inventory[i] == objectId));
if (_coreVar._selectedObjectId == objectId)
retVal = true;
return retVal;
}
/**
* Engine function - Display Dining Room
* @remarks Originally called 'st1sama'
*/
void MortevielleEngine::displayDiningRoom() {
_coreVar._currPlace = DINING_ROOM;
prepareDisplayText();
}
/**
* Engine function - Start non interactive Dialog
* @remarks Originally called 'sparl'
*/
void MortevielleEngine::startDialog(int16 rep) {
const int haut[9] = { 0, 0, 1, -3, 6, -2, 2, 7, -1 };
int key;
assert(rep >= 0);
_mouse->hideMouse();
Common::String dialogStr = getString(rep + kDialogStringIndex);
_text->displayStr(dialogStr, 230, 4, 65, 26, 5);
_dialogManager->drawF3F8();
key = 0;
do {
_soundManager->startSpeech(rep, haut[_caff - 69], 0);
key = _dialogManager->waitForF3F8();
if (shouldQuit())
return;
} while (key != 66);
clearScreen();
_mouse->showMouse();
}
/**
* Engine function - End of Search: reset globals
* @remarks Originally called 'finfouill'
*/
void MortevielleEngine::endSearch() {
_heroSearching = false;
_obpart = false;
_searchCount = 0;
_menu->unsetSearchMenu();
}
/**
* Engine function - Go to Dining room
* @remarks Originally called 't1sama'
*/
void MortevielleEngine::gotoDiningRoom() {
int day, hour, minute;
updateHour(day, hour, minute);
if ((hour < 5) && (_coreVar._currPlace > ROOM18)) {
if (!checkInventory(137)) { //You don't have the keys, and it's late
_crep = 1511;
loseGame();
} else
displayDiningRoom();
} else if (!_coreVar._alreadyEnteredManor) { //Is it your first time?
_currBitIndex = 255; // Everybody is present
showPeoplePresent(_currBitIndex);
_caff = 77;
drawPictureWithText();
_screenSurface->drawBox(223, 47, 155, 92, 15);
handleDescriptionText(2, 33);
testKey(false);
menuUp();
_mouse->hideMouse();
clearScreen();
drawDiscussionBox();
startDialog(140);
drawRightFrame();
drawClock();
_mouse->showMouse();
_coreVar._currPlace = OWN_ROOM;
prepareDisplayText();
resetPresenceInRooms(DINING_ROOM);
if (!_outsideOnlyFl)
getPresence(OWN_ROOM);
_currBitIndex = 0;
_savedBitIndex = 0;
_coreVar._alreadyEnteredManor = true;
} else
displayDiningRoom();
}
/**
* Engine function - Check Manor distance (in the mountains)
* @remarks Originally called 't1neig'
*/
void MortevielleEngine::checkManorDistance() {
++_manorDistance;
if (_manorDistance > 2) {
_crep = 1506;
loseGame();
} else {
_destinationOk = true;
_coreVar._currPlace = MOUNTAIN;
prepareDisplayText();
}
}
/**
* Engine function - Go to Manor front
* @remarks Originally called 't1deva'
*/
void MortevielleEngine::gotoManorFront() {
_manorDistance = 0;
_coreVar._currPlace = MANOR_FRONT;
prepareDisplayText();
}
/**
* Engine function - Go to Manor back
* @remarks Originally called 't1derr'
*/
void MortevielleEngine::gotoManorBack() {
_coreVar._currPlace = MANOR_BACK;
prepareDisplayText();
}
/**
* Engine function - Dead : Flooded in Well
* @remarks Originally called 't1deau'
*/
void MortevielleEngine::floodedInWell() {
_crep = 1503;
loseGame();
}
/**
* Called when a savegame has been loaded.
* @remarks Originally called 'antegame'
*/
void MortevielleEngine::gameLoaded() {
_mouse->hideMouse();
_menu->_menuDisplayed = false;
_loseGame = true;
_anyone = false;
_destinationOk = true;
_col = false;
_hiddenHero = false;
_uptodatePresence = false;
_maff = 68;
_menuOpcode = OPCODE_NONE;
_introSpeechPlayed = false;
_x = 0;
_y = 0;
_num = 0;
_startTime = 0;
_endTime = 0;
_searchCount = 0;
_roomDoorId = OWN_ROOM;
_syn = true;
_heroSearching = true;
_curSearchObjId = 0;
_manorDistance = 0;
resetOpenObjects();
_takeObjCount = 0;
prepareDisplayText();
_hintPctMessage = getString(580);
_destinationOk = false;
_endGame = true;
_loseGame = false;
_heroSearching = false;
displayAloneText();
prepareRoom();
drawClock();
drawPictureWithText();
handleDescriptionText(2, _crep);
clearVerbBar();
_endGame = false;
_menu->setDestinationText(_coreVar._currPlace);
_menu->setInventoryText();
if (_coreVar._selectedObjectId != 0)
displayItemInHand(_coreVar._selectedObjectId + 400);
_mouse->showMouse();
}
/**
* Engine function - Handle OpCodes
* @remarks Originally called 'tsitu'
*/
void MortevielleEngine::handleOpcode() {
if (!_col)
clearDescriptionBar();
_syn = false;
_keyPressedEsc = false;
if (!_anyone) {
if (_uptodatePresence) {
if ((_currMenu == MENU_MOVE) || (_currAction == _menu->_opcodeLeave) || (_currAction == _menu->_opcodeSleep) || (_currAction == _menu->_opcodeEat)) {
_controlMenu = 4;
menuUp();
return;
}
}
if (_currMenu == MENU_MOVE)
fctMove();
else if (_currMenu == MENU_DISCUSS)
fctDiscuss();
else if (_currMenu == MENU_INVENTORY)
fctInventoryTake();
else if (_currAction == _menu->_opcodeAttach)
fctAttach();
else if (_currAction == _menu->_opcodeWait)
fctWait();
else if (_currAction == _menu->_opcodeForce)
fctForce();
else if (_currAction == _menu->_opcodeSleep)
fctSleep();
else if (_currAction == _menu->_opcodeListen)
fctListen();
else if (_currAction == _menu->_opcodeEnter)
fctEnter();
else if (_currAction == _menu->_opcodeClose)
fctClose();
else if (_currAction == _menu->_opcodeSearch)
fctSearch();
else if (_currAction == _menu->_opcodeKnock)
fctKnock();
else if (_currAction == _menu->_opcodeScratch)
fctScratch();
else if (_currAction == _menu->_opcodeRead)
fctRead();
else if (_currAction == _menu->_opcodeEat)
fctEat();
else if (_currAction == _menu->_opcodePlace)
fctPlace();
else if (_currAction == _menu->_opcodeOpen)
fctOpen();
else if (_currAction == _menu->_opcodeTake)
fctTake();
else if (_currAction == _menu->_opcodeLook)
fctLook();
else if (_currAction == _menu->_opcodeSmell)
fctSmell();
else if (_currAction == _menu->_opcodeSound)
fctSound();
else if (_currAction == _menu->_opcodeLeave)
fctLeave();
else if (_currAction == _menu->_opcodeLift)
fctLift();
else if (_currAction == _menu->_opcodeTurn)
fctTurn();
else if (_currAction == _menu->_opcodeSSearch)
fctSelfSearch();
else if (_currAction == _menu->_opcodeSRead)
fctSelfRead();
else if (_currAction == _menu->_opcodeSPut)
fctSelfPut();
else if (_currAction == _menu->_opcodeSLook)
fctSelftLook();
_hiddenHero = false;
if (_currAction == _menu->_opcodeSHide)
fctSelfHide();
} else if (_anyone) {
interactNPC();
_anyone = false;
menuUp();
return;
}
int hour, day, minute;
updateHour(day, hour, minute);
if ((((hour == 12) || (hour == 13) || (hour == 19)) && (_coreVar._currPlace != DINING_ROOM)) ||
((hour > 0) && (hour < 6) && (_coreVar._currPlace != OWN_ROOM)))
++_coreVar._faithScore;
if (((_coreVar._currPlace < CRYPT) || (_coreVar._currPlace > MOUNTAIN)) && (_coreVar._currPlace != INSIDE_WELL)
&& (_coreVar._currPlace != OWN_ROOM) && (_coreVar._selectedObjectId != 152) && (!_loseGame)) {
if ((_coreVar._faithScore > 99) && (hour > 8) && (hour < 16)) {
_crep = 1501;
loseGame();
} else if ((_coreVar._faithScore > 99) && (hour > 0) && (hour < 9)) {
_crep = 1508;
loseGame();
} else if ((day > 1) && (hour > 8) && (!_loseGame)) {
_crep = 1502;
loseGame();
}
}
menuUp();
}
/**
* Engine function - Transform time into a char
* @remarks Originally called 'tmaj3'
*/
void MortevielleEngine::hourToChar() {
int day, hour, minute;
updateHour(day, hour, minute);
if (minute == 30)
minute = 1;
hour += day * 24;
minute += hour * 2;
_coreVar._fullHour = (unsigned char)minute;
}
/**
* Engine function - extract time from a char
* @remarks Originally called 'theure'
*/
void MortevielleEngine::charToHour() {
int fullHour = _coreVar._fullHour;
int tmpHour = fullHour % 48;
_currDay = fullHour / 48;
_currHalfHour = tmpHour % 2;
_currHour = tmpHour / 2;
_hour = _currHour;
if (_currHalfHour == 1)
_minute = 30;
else
_minute = 0;
}
/**
* Engine function - Clear upper left part of Screen - Type 1
* @remarks Originally called 'clsf1'
*/
void MortevielleEngine::clearUpperLeftPart() {
_mouse->hideMouse();
_screenSurface->fillRect(0, Common::Rect(0, 11, 514, 175));
_mouse->showMouse();
}
/**
* Engine function - Clear low bar used by description
* @remarks Originally called 'clsf2'
*/
void MortevielleEngine::clearDescriptionBar() {
_mouse->hideMouse();
if (_largestClearScreen) {
_screenSurface->fillRect(0, Common::Rect(1, 176, 633, 199));
_screenSurface->drawBox(0, 176, 634, 23, 15);
_largestClearScreen = false;
} else {
_screenSurface->fillRect(0, Common::Rect(1, 176, 633, 190));
_screenSurface->drawBox(0, 176, 634, 14, 15);
}
_mouse->showMouse();
}
/**
* Engine function - Clear lowest bar used by verbs
* @remarks Originally called 'clsf3'
*/
void MortevielleEngine::clearVerbBar() {
_mouse->hideMouse();
_screenSurface->fillRect(0, Common::Rect(1, 192, 633, 199));
_screenSurface->drawBox(0, 191, 634, 8, 15);
_mouse->showMouse();
}
/**
* Engine function - Clear upper right part of the screen
* @remarks Originally called 'clsf10'
*/
void MortevielleEngine::clearUpperRightPart() {
Common::String st;
_mouse->hideMouse();
// Clear ambiance description
_screenSurface->fillRect(15, Common::Rect(544, 93, 600, 98));
if (_coreVar._faithScore < 33)
st = getEngineString(S_COOL);
else if (_coreVar._faithScore < 66)
st = getEngineString(S_LOURDE);
else if (_coreVar._faithScore > 65)
st = getEngineString(S_MALSAINE);
int x1 = 580 - (_screenSurface->getStringWidth(st) / 2);
_screenSurface->putxy(x1, 92);
_screenSurface->drawString(st, 4);
// Clear person list
_screenSurface->fillRect(15, Common::Rect(560, 24, 610, 86));
_mouse->showMouse();
}
/**
* Engine function - Get a random number between two values
* @remarks Originally called 'get_random_number' and 'hazard'
*/
int MortevielleEngine::getRandomNumber(int minval, int maxval) {
return _randomSource.getRandomNumber(maxval - minval) + minval;
}
/**
* Engine function - Show alert "use move menu"
* @remarks Originally called 'aldepl'
*/
void MortevielleEngine::showMoveMenuAlert() {
_dialogManager->show(getEngineString(S_USE_DEP_MENU));
}
/**
* The original engine used this method to display a starting text screen letting the player
* select the graphics mode to use
* @remarks Originally called 'dialpre'
*/
void MortevielleEngine::showConfigScreen() {
_crep = 998;
}
/**
* Decodes a number of 64 byte blocks
* @param pStart Start of data
* @param count Number of 64 byte blocks
* @remarks Originally called 'zzuul'
*/
void MortevielleEngine::decodeNumber(byte *pStart, int count) {
while (count-- > 0) {
for (int idx = 0; idx < 64; ++pStart, ++idx) {
uint16 v = ((*pStart - 0x80) << 1) + 0x80;
if (v & 0x8000)
*pStart = 0;
else if (v & 0xff00)
*pStart = 0xff;
else
*pStart = (byte)v;
}
}
}
const byte cryptoArrDefaultFr[32] = {
32, 101, 115, 97, 114, 105, 110,
117, 116, 111, 108, 13, 100, 99,
112, 109, 46, 118, 130, 39, 102,
98, 44, 113, 104, 103, 33, 76,
85, 106, 30, 31
};
const byte cryptoArr30Fr[32] = {
69, 67, 74, 138, 133, 120, 77, 122,
121, 68, 65, 63, 73, 80, 83, 82,
156, 45, 58, 79, 49, 86, 78, 84,
71, 81, 64, 66, 135, 34, 136, 91
};
const byte cryptoArr31Fr[32]= {
93, 47, 48, 53, 50, 70, 124, 75,
72, 147, 140, 150, 151, 57, 56, 51,
107, 139, 55, 89, 131, 37, 54, 88,
119, 0, 0, 0, 0, 0, 0, 0
};
const byte cryptoArrDefaultDe[32] = {
0x20, 0x65, 0x6E, 0x69, 0x73, 0x72, 0x74,
0x68, 0x61, 0x75, 0x0D, 0x63, 0x6C, 0x64,
0x6D, 0x6F, 0x67, 0x2E, 0x62, 0x66, 0x53,
0x2C, 0x77, 0x45, 0x7A, 0x6B, 0x44, 0x76,
0x9C, 0x47, 0x1E, 0x1F
};
const byte cryptoArr30De[32] = {
0x49, 0x4D, 0x21, 0x42, 0x4C, 0x70, 0x41, 0x52,
0x57, 0x4E, 0x48, 0x3F, 0x46, 0x50, 0x55, 0x4B,
0x5A, 0x4A, 0x54, 0x31, 0x4F, 0x56, 0x79, 0x3A,
0x6A, 0x5B, 0x5D, 0x40, 0x22, 0x2F, 0x30, 0x35
};
const byte cryptoArr31De[32]= {
0x78, 0x2D, 0x32, 0x82, 0x43, 0x39, 0x33, 0x38,
0x7C, 0x27, 0x37, 0x3B, 0x25, 0x28, 0x29, 0x36,
0x51, 0x59, 0x71, 0x81, 0x87, 0x88, 0x93, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
const byte *cryptoArrDefault, *cryptoArr30, *cryptoArr31;
uint16 ctrlChar;
/**
* Decrypt the next character
* @param c OUT, next decrypted char
* @param idx IN/OUT, current buffer index
* @param pt IN/OUT, current encryption point
* @return a boolean specifying if a stop character has been encountered
* @remarks Originally called 'cinq_huit'
*/
bool MortevielleEngine::decryptNextChar(char &c, int &idx, byte &pt) {
uint16 oct, ocd;
/* 5-8 */
oct = _dialogIndexArray[idx];
oct = ((uint16)(oct << (16 - pt))) >> (16 - pt);
if (pt < 6) {
++idx;
oct = oct << (5 - pt);
pt += 11;
oct = oct | ((uint)_dialogIndexArray[idx] >> pt);
} else {
pt -= 5;
oct = (uint)oct >> pt;
}
if (oct == ctrlChar) {
c = '$';
return true;
} else if (oct == 30 || oct == 31) {
ocd = _dialogIndexArray[idx];
ocd = (uint16)(ocd << (16 - pt)) >> (16 - pt);
if (pt < 6) {
++idx;
ocd = ocd << (5 - pt);
pt += 11;
ocd = ocd | ((uint)_dialogIndexArray[idx] >> pt);
} else {
pt -= 5;
ocd = (uint)ocd >> pt;
}
if (oct == 30)
c = (unsigned char)cryptoArr30[ocd];
else
c = (unsigned char)cryptoArr31[ocd];
if (c == '\0') {
c = '#';
return true;
}
} else {
c = (unsigned char)cryptoArrDefault[oct];
}
return false;
}
/**
* Decode and extract the line with the given Id
* @remarks Originally called 'deline'
*/
Common::String MortevielleEngine::getString(int num) {
Common::String wrkStr = "";
if (num < 0) {
warning("getString(%d): num < 0! Skipping", num);
} else if (!_txxFileFl) {
wrkStr = getGameString(num);
} else {
int hint = _dialogHintArray[num]._hintId;
byte point = _dialogHintArray[num]._point;
int length = 0;
bool endFl = false;
char let;
do {
endFl = decryptNextChar(let, hint, point);
wrkStr += let;
++length;
} while (!endFl);
}
while (wrkStr.lastChar() == '$')
// Remove trailing '$'s
wrkStr.deleteLastChar();
return wrkStr;
}
/**
* Reset object place
* @remarks Originally called 'copcha'
*/
void MortevielleEngine::resetObjectPlace() {
for (int i = kAcha; i < kAcha + 390; i++)
_tabdon[i] = _tabdon[i + 390];
}
void MortevielleEngine::resetCoreVar() {
_saveStruct._alreadyEnteredManor = _coreVar._alreadyEnteredManor = false;
_saveStruct._selectedObjectId = _coreVar._selectedObjectId = 0;
_saveStruct._cellarObjectId = _coreVar._cellarObjectId = 0;
_saveStruct._atticBallHoleObjectId = _coreVar._atticBallHoleObjectId = 0;
_saveStruct._atticRodHoleObjectId = _coreVar._atticRodHoleObjectId = 0;
_saveStruct._wellObjectId = _coreVar._wellObjectId = 0;
_saveStruct._secretPassageObjectId = _coreVar._secretPassageObjectId = 0;
_saveStruct._purpleRoomObjectId = _coreVar._purpleRoomObjectId = 136;
_saveStruct._cryptObjectId = _coreVar._cryptObjectId = 141;
_saveStruct._faithScore = _coreVar._faithScore = getRandomNumber(4, 10);
_saveStruct._currPlace = _coreVar._currPlace = MANOR_FRONT;
for (int i = 2; i <= 6; ++i)
_coreVar._inventory[i] = 0;
// Only object in inventory: a gun
_coreVar._inventory[1] = 113;
_saveStruct._fullHour = _coreVar._fullHour = (unsigned char)20;
for (int i = 1; i <= 10; ++i)
_coreVar._pctHintFound[i] = ' ';
for (int i = 1; i <= 6; ++i)
_coreVar._availableQuestion[i] = '*';
for (int i = 7; i <= 9; ++i)
_coreVar._availableQuestion[i] = ' ';
for (int i = 10; i <= 28; ++i)
_coreVar._availableQuestion[i] = '*';
for (int i = 29; i <= 42; ++i)
_coreVar._availableQuestion[i] = ' ';
_coreVar._availableQuestion[33] = '*';
}
/**
* Engine function - When restarting the game, reset the main variables used by the engine
* @remarks Originally called 'inzon'
*/
void MortevielleEngine::resetVariables() {
resetObjectPlace();
resetCoreVar();
for (int i = 1; i <= 8; ++i)
_charAnswerCount[i] = 0;
initMaxAnswer();
}
/**
* Engine function - Set the palette
* @remarks Originally called 'writepal'
*/
void MortevielleEngine::setPal(int n) {
for (int i = 1; i <= 16; ++i) {
_curPict[(2 * i)] = _stdPal[n][i].x;
_curPict[(2 * i) + 1] = _stdPal[n][i].y;
}
}
/**
* Engine function - Load Palette from File
* @remarks Originally called 'charpal'
*/
void MortevielleEngine::loadPalette() {
Common::File f;
if (!f.open("fxx.mor")) {
if (f.open("mfxx.mor"))
f.seek(7 * 25);
else
error("Missing file - fxx.mor");
}
for (int i = 0; i < 108; ++i)
_drawingSizeArr[i] = f.readSint16LE();
f.close();
if (!f.open("plxx.mor"))
error("Missing file - plxx.mor");
for (int i = 0; i <= 90; ++i) {
for (int j = 1; j <= 16; ++j) {
_stdPal[i][j].x = f.readByte();
_stdPal[i][j].y = f.readByte();
}
}
f.close();
if (!f.open("cxx.mor"))
error("Missing file - cxx.mor");
// Skip CGA Palette and Patterns
f.close();
}
/**
* Engine function - Load Texts from File
* @remarks Originally called 'chartex'
*/
void MortevielleEngine::loadTexts() {
Common::File inpFile;
Common::File ntpFile;
_txxFileFl = false;
if (!useOriginalData()) {
warning("Using improved translation from DAT file");
return;
}
if (!inpFile.open("TXX.INP")) {
if (!inpFile.open("TXX.MOR")) {
warning("Missing file - TXX.INP or .MOR - Switching to DAT file");
return;
}
}
if (ntpFile.open("TXX.NTP")) {
cryptoArr30 = cryptoArr30Fr;
cryptoArr31 = cryptoArr31Fr;
cryptoArrDefault = cryptoArrDefaultFr;
ctrlChar = 11;
} else if (ntpFile.open("TXX.IND")) {
cryptoArr30 = cryptoArr30De;
cryptoArr31 = cryptoArr31De;
cryptoArrDefault = cryptoArrDefaultDe;
ctrlChar = 10;
} else {
warning("Missing file - TXX.NTP or .IND - Switching to DAT file");
return;
}
if ((inpFile.size() > (kMaxDialogIndex * 2)) || (ntpFile.size() > (kMaxDialogHint * 3))) {
warning("TXX file - Unexpected format - Switching to DAT file");
return;
}
for (int i = 0; i < inpFile.size() / 2; ++i)
_dialogIndexArray[i] = inpFile.readUint16LE();
inpFile.close();
_txxFileFl = true;
for (int i = 0; i < (ntpFile.size() / 3); ++i) {
_dialogHintArray[i]._hintId = ntpFile.readSint16LE();
_dialogHintArray[i]._point = ntpFile.readByte();
}
ntpFile.close();
}
void MortevielleEngine::loadCFIEC() {
Common::File f;
if (!f.open("cfiec.mor")) {
if (!f.open("alcfiec.mor"))
error("Missing file - *cfiec.mor");
}
_cfiecBufferSize = ((f.size() / 128) + 1) * 128;
int32 fileSize = f.size();
if (!_reloadCFIEC)
_cfiecBuffer = (byte *)malloc(sizeof(byte) * _cfiecBufferSize);
for (int32 i = 0; i < fileSize; ++i)
_cfiecBuffer[i] = f.readByte();
for (int i = fileSize; i < _cfiecBufferSize; i++)
_cfiecBuffer[i] = 0;
f.close();
_reloadCFIEC = false;
}
void MortevielleEngine::loadCFIPH() {
Common::File f;
if (!f.open("cfiph.mor")) {
if (!f.open("alcfiph.mor"))
error("Missing file - *cfiph.mor");
}
_soundManager->_cfiphBuffer = (uint16 *)malloc(sizeof(uint16) * (f.size() / 2));
for (int i = 0; i < (f.size() / 2); ++i)
_soundManager->_cfiphBuffer[i] = f.readUint16BE();
f.close();
}
/**
* Engine function - Play Music
* @remarks Originally called 'music'
*/
void MortevielleEngine::music() {
if (_soundOff)
return;
_reloadCFIEC = true;
Common::File f;
if (!f.open("mort.img"))
error("Missing file - mort.img");
int size = f.size();
byte *compMusicBuf = (byte *)malloc(sizeof(byte) * size);
byte *musicBuf = (byte *)malloc(sizeof(byte) * size * 2);
f.read(compMusicBuf, size);
f.close();
int musicSize = _soundManager->decodeMusic(compMusicBuf, musicBuf, size);
free(compMusicBuf);
_soundManager->playSong(musicBuf, musicSize, 5);
while (keyPressed())
getChar();
free(musicBuf);
}
/**
* Engine function - Show title screen
* @remarks Originally called 'suite'
*/
void MortevielleEngine::showTitleScreen() {
clearScreen();
handleDescriptionText(7, 2035);
_caff = 51;
_text->taffich();
testKeyboard();
clearScreen();
draw(0, 0);
Common::String cpr = "COPYRIGHT 1989 : LANKHOR";
_screenSurface->putxy(104 + 72 * kResolutionScaler, 185);
_screenSurface->drawString(cpr, 0);
}
/**
* Draw picture
* @remarks Originally called 'dessine'
*/
void MortevielleEngine::draw(int x, int y) {
_mouse->hideMouse();
setPal(_numpal);
displayPicture(_curPict, x, y);
_mouse->showMouse();
}
/**
* Draw right frame
* @remarks Originally called 'dessine_rouleau'
*/
void MortevielleEngine::drawRightFrame() {
setPal(89);
_mouse->hideMouse();
displayPicture(_rightFramePict, 0, 0);
_mouse->showMouse();
}
/**
* Read the current system time
*/
int MortevielleEngine::readclock() {
return (int)(g_system->getMillis() / 1000);
}
/**
* Engine function - Prepare room and hint string
* @remarks Originally called 'tinke'
*/
void MortevielleEngine::prepareRoom() {
int day, hour, minute;
_anyone = false;
updateHour(day, hour, minute);
if (day != _day) {
_day = day;
for (int i = 0; i < 9; i++) {
if (_charAnswerMax[i] > 0)
--_charAnswerMax[i];
_charAnswerCount[i] = 0;
}
}
if ((hour > _hour) || ((hour == 0) && (_hour == 23))) {
_hour = hour;
_minute = 0;
drawClock();
int hintCount = 0;
for (int i = 1; i <= 10; ++i) {
if (_coreVar._pctHintFound[i] == '*')
++hintCount;
}
Common::String pctStr;
if (hintCount == 10)
pctStr = "10";
else
pctStr = (unsigned char)(hintCount + 48);
_hintPctMessage = "[1][";
_hintPctMessage += getEngineString(S_SHOULD_HAVE_NOTICED);
_hintPctMessage += pctStr;
_hintPctMessage += '0';
_hintPctMessage += getEngineString(S_NUMBER_OF_HINTS);
_hintPctMessage += "][";
_hintPctMessage += getEngineString(S_OKAY);
_hintPctMessage += ']';
}
if (minute > _minute) {
_minute = 30;
drawClock();
}
if (_mouse->_pos.y < 12)
return;
if (!_outsideOnlyFl) {
if ((hour == 12) || ((hour > 18) && (hour < 21)) || ((hour >= 0) && (hour < 7)))
_inGameHourDuration = kTime2;
else
_inGameHourDuration = kTime1;
if ((_coreVar._faithScore > 33) && (_coreVar._faithScore < 66))
_inGameHourDuration -= (_inGameHourDuration / 3);
if (_coreVar._faithScore > 65)
_inGameHourDuration -= ((_inGameHourDuration / 3) * 2);
int newTime = readclock();
if ((newTime - _currentTime) > _inGameHourDuration) {
bool activeMenu = _menu->_menuActive;
_menu->eraseMenu();
_currentHourCount += ((newTime - _currentTime) / _inGameHourDuration);
_currentTime = newTime;
switch (_place) {
case GREEN_ROOM:
case DARKBLUE_ROOM:
setRandomPresenceGreenRoom(_coreVar._faithScore);
break;
case PURPLE_ROOM:
setRandomPresencePurpleRoom(_coreVar._faithScore);
break;
case BLUE_ROOM:
setRandomPresenceBlueRoom(_coreVar._faithScore);
break;
case RED_ROOM:
case GREEN_ROOM2:
setRandomPresenceRedRoom(_coreVar._faithScore);
break;
case JULIA_ROOM:
setRandomPresenceJuliaRoom(_coreVar._faithScore);
break;
case DINING_ROOM:
setRandomPresenceDiningRoom(_coreVar._faithScore);
break;
case BUREAU:
setRandomPresenceBureau(_coreVar._faithScore);
break;
case KITCHEN:
setRandomPresenceKitchen(_coreVar._faithScore);
break;
case ATTIC:
case CELLAR:
setRandomPresenceAttic(_coreVar._faithScore);
break;
case LANDING:
case ROOM26:
setRandomPresenceLanding(_coreVar._faithScore);
break;
case CHAPEL:
setRandomPresenceChapel(_coreVar._faithScore);
break;
}
if ((_savedBitIndex != 0) && (_currBitIndex != 10))
_savedBitIndex = _currBitIndex;
if ((_savedBitIndex == 0) && (_currBitIndex > 0)) {
if ((_coreVar._currPlace == ATTIC) || (_coreVar._currPlace == CELLAR)) {
initCaveOrCellar();
} else if (_currBitIndex == 10) {
_currBitIndex = 0;
if (!_uptodatePresence) {
_uptodatePresence = true;
_startTime = readclock();
if (getRandomNumber(1, 5) < 5) {
clearVerbBar();
prepareScreenType2();
displayTextInVerbBar(getEngineString(S_HEAR_NOISE));
int rand = (getRandomNumber(0, 4)) - 2;
_soundManager->startSpeech(1, rand, 1);
_soundManager->waitSpeech();
clearVerbBar();
}
}
}
}
if (activeMenu)
_menu->drawMenu();
}
}
_endTime = readclock();
if ((_uptodatePresence) && ((_endTime - _startTime) > 17)) {
getPresenceBitIndex(_place);
_uptodatePresence = false;
_startTime = 0;
if ((_coreVar._currPlace > OWN_ROOM) && (_coreVar._currPlace < DINING_ROOM))
_anyone = true;
}
}
/**
* Engine function - Draw Clock
* @remarks Originally called 'pendule'
*/
void MortevielleEngine::drawClock() {
const int cv[2][12] = {
{ 5, 8, 10, 8, 5, 0, -5, -8, -10, -8, -5, 0 },
{ -5, -3, 0, 3, 5, 6, 5, 3, 0, -3, -5, -6 }
};
const int x = 580;
const int y = 123;
const int rg = 9;
_mouse->hideMouse();
_screenSurface->drawRectangle(570, 118, 20, 10);
_screenSurface->drawRectangle(578, 114, 6, 18);
if (_minute == 0)
_screenSurface->drawLine(((uint)x >> 1) * kResolutionScaler, y, ((uint)x >> 1) * kResolutionScaler, (y - rg), 1);
else
_screenSurface->drawLine(((uint)x >> 1) * kResolutionScaler, y, ((uint)x >> 1) * kResolutionScaler, (y + rg), 1);
int hour12 = _hour;
if (hour12 > 12)
hour12 -= 12;
if (hour12 == 0)
hour12 = 12;
_screenSurface->drawLine(((uint)x >> 1) * kResolutionScaler, y, ((uint)(x + cv[0][hour12 - 1]) >> 1) * kResolutionScaler, y + cv[1][hour12 - 1], 1);
_mouse->showMouse();
_screenSurface->putxy(568, 154);
if (_hour > 11)
_screenSurface->drawString("PM ", 1);
else
_screenSurface->drawString("AM ", 1);
_screenSurface->putxy(550, 160);
if ((_day >= 0) && (_day <= 8)) {
Common::String tmp = getEngineString(S_DAY);
tmp.insertChar((char)(_day + 49), 0);
_screenSurface->drawString(tmp, 1);
}
}
void MortevielleEngine::palette(int v1) {
warning("TODO: palette");
}
/**
* Returns a substring of the given string
* @param s Source string
* @param idx Starting index (1 based)
* @param size Number of characters to return
*/
Common::String MortevielleEngine::copy(const Common::String &s, int idx, size_t size) {
assert(idx + size < s.size());
// Copy the substring into a temporary buffer
char *tmp = new char[size + 1];
Common::strlcpy(tmp, s.c_str() + idx - 1, size + 1);
Common::String result(tmp);
delete[] tmp;
return result;
}
/**
* Clear Screen
* @remarks Originally called 'hirs'
*/
void MortevielleEngine::clearScreen() {
_screenSurface->clearScreen();
}
/**
* Init room : Cave or Cellar
* @remarks Originally called 'cavegre'
*/
void MortevielleEngine::initCaveOrCellar() {
_coreVar._faithScore += 2;
if (_coreVar._faithScore > 69)
_coreVar._faithScore += (_coreVar._faithScore / 10);
clearVerbBar();
prepareScreenType2();
displayTextInVerbBar(getEngineString(S_SOMEONE_ENTERS));
int rand = (getRandomNumber(0, 4)) - 2;
_soundManager->startSpeech(2, rand, 1);
_soundManager->waitSpeech();
// The original was doing here a useless loop.
// It has been removed
clearVerbBar();
displayAloneText();
}
/**
* Display control menu string
* @remarks Originally called 'tctrm'
*/
void MortevielleEngine::displayControlMenu() {
handleDescriptionText(2, (3000 + _controlMenu));
_controlMenu = 0;
}
/**
* Display picture at a given coordinate
* @remarks Originally called 'pictout'
*/
void MortevielleEngine::displayPicture(const byte *pic, int x, int y) {
GfxSurface surface;
surface.decode(pic);
_screenSurface->drawPicture(surface, x, y);
}
void MortevielleEngine::adzon() {
Common::File f;
if (!f.open("don.mor"))
error("Missing file - don.mor");
f.read(_tabdon, 7 * 256);
f.close();
if (!f.open("bmor.mor"))
error("Missing file - bmor.mor");
f.read(&_tabdon[kFleche], 1916);
f.close();
// Read Right Frame Drawing
if (!f.open("dec.mor"))
error("Missing file - dec.mor");
free(_rightFramePict);
_rightFramePict = (byte *)malloc(sizeof(byte) * f.size());
f.read(_rightFramePict, f.size());
f.close();
}
/**
* Returns the offset within the compressed image data resource of the desired image
* @remarks Originally called 'animof'
*/
int MortevielleEngine::getAnimOffset(int frameNum, int animNum) {
int animCount = _curAnim[1];
int aux = animNum;
if (frameNum != 1)
aux += animCount;
return (animCount << 2) + 2 + READ_BE_UINT16(&_curAnim[aux << 1]);
}
/**
* Display text in description bar
* @remarks Originally called 'text1'
*/
void MortevielleEngine::displayTextInDescriptionBar(int x, int y, int nb, int mesgId) {
Common::String tmpStr = getString(mesgId);
if ((y == 182) && ((int) tmpStr.size() > nb))
y = 176;
_text->displayStr(tmpStr, x, y, nb, 20, _textColor);
}
/**
* Display description text
* @remarks Originally called 'repon'
*/
void MortevielleEngine::handleDescriptionText(int f, int mesgId) {
if ((mesgId > 499) && (mesgId < 563)) {
Common::String tmpStr = getString(mesgId - 501 + kInventoryStringIndex);
if ((int) tmpStr.size() > ((58 + (kResolutionScaler - 1) * 37) << 1))
_largestClearScreen = true;
else
_largestClearScreen = false;
clearDescriptionBar();
_text->displayStr(tmpStr, 8, 176, 85, 3, 5);
} else {
mapMessageId(mesgId);
switch (f) {
case 2:
case 8:
clearDescriptionBar();
prepareScreenType2();
displayTextInDescriptionBar(8, 182, 103, mesgId);
if ((mesgId == 68) || (mesgId == 69))
_coreVar._availableQuestion[40] = '*';
else if ((mesgId == 104) && (_caff == CELLAR)) {
_coreVar._availableQuestion[36] = '*';
if (_coreVar._availableQuestion[39] == '*') {
_coreVar._pctHintFound[3] = '*';
_coreVar._availableQuestion[38] = '*';
}
}
break;
case 1:
case 6:
case 9: {
int i;
if ((f == 1) || (f == 6))
i = 4;
else
i = 5;
Common::String tmpStr = getString(mesgId);
_text->displayStr(tmpStr, 80, 40, 60, 25, i);
if (mesgId == 180)
_coreVar._pctHintFound[6] = '*';
else if (mesgId == 179)
_coreVar._pctHintFound[10] = '*';
}
break;
default:
break;
}
}
}
/**
* Recompute message Id
* @remarks Originally called 'modif'
*/
void MortevielleEngine::mapMessageId(int &mesgId) {
if (mesgId == 26)
mesgId = 25;
else if ((mesgId > 29) && (mesgId < 36))
mesgId -= 4;
else if ((mesgId > 69) && (mesgId < 78))
mesgId -= 37;
else if ((mesgId > 99) && (mesgId < 194))
mesgId -= 59;
else if ((mesgId > 996) && (mesgId < 1000))
mesgId -= 862;
else if ((mesgId > 1500) && (mesgId < 1507))
mesgId -= 1363;
else if ((mesgId > 1507) && (mesgId < 1513))
mesgId -= 1364;
else if ((mesgId > 1999) && (mesgId < 2002))
mesgId -= 1851;
else if (mesgId == 2010)
mesgId = 151;
else if ((mesgId > 2011) && (mesgId < 2025))
mesgId -= 1860;
else if (mesgId == 2026)
mesgId = 165;
else if ((mesgId > 2029) && (mesgId < 2037))
mesgId -= 1864;
else if ((mesgId > 3000) && (mesgId < 3005))
mesgId -= 2828;
else if (mesgId == 4100)
mesgId = 177;
else if (mesgId == 4150)
mesgId = 178;
else if ((mesgId > 4151) && (mesgId < 4156))
mesgId -= 3973;
else if (mesgId == 4157)
mesgId = 183;
else if ((mesgId == 4160) || (mesgId == 4161))
mesgId -= 3976;
}
/**
* Initialize open objects array
* @remarks Originally called 'initouv'
*/
void MortevielleEngine::resetOpenObjects() {
for (int i = 1; i <= 6; ++i)
_openObjects[i] = 0;
_openObjCount = 0;
}
/**
* Display Text Block
* @remarks Originally called 'ecr2'
*/
void MortevielleEngine::displayTextBlock(Common::String text) {
// Some dead code was present in the original: removed
_screenSurface->putxy(8, 177);
int tlig = 59 + (kResolutionScaler - 1) * 36;
if ((int)text.size() < tlig)
_screenSurface->drawString(text, 5);
else if ((int)text.size() < (tlig << 1)) {
_screenSurface->putxy(8, 176);
_screenSurface->drawString(copy(text, 1, (tlig - 1)), 5);
_screenSurface->putxy(8, 182);
_screenSurface->drawString(copy(text, tlig, tlig << 1), 5);
} else {
_largestClearScreen = true;
clearDescriptionBar();
_screenSurface->putxy(8, 176);
_screenSurface->drawString(copy(text, 1, (tlig - 1)), 5);
_screenSurface->putxy(8, 182);
_screenSurface->drawString(copy(text, tlig, ((tlig << 1) - 1)), 5);
_screenSurface->putxy(8, 190);
_screenSurface->drawString(copy(text, tlig << 1, tlig * 3), 5);
}
}
void MortevielleEngine::displayTextInVerbBar(Common::String text) {
clearVerbBar();
_screenSurface->putxy(8, 192);
_screenSurface->drawString(text, 5);
}
/**
* Display item in hand
* @remarks Originally called 'modobj'
*/
void MortevielleEngine::displayItemInHand(int objId) {
Common::String strp = Common::String(' ');
if (objId != 500)
strp = getString(objId - 501 + kInventoryStringIndex);
_menu->setText(_menu->_inventoryMenu[8], strp);
_menu->disableMenuItem(_menu->_inventoryMenu[8]);
}
/**
* Display empty hand
* @remarks Originally called 'maivid'
*/
void MortevielleEngine::displayEmptyHand() {
_coreVar._selectedObjectId = 0;
displayItemInHand(500);
}
/**
* Set a random presence: Leo or Max
* @remarks Originally called 'chlm'
*/
int MortevielleEngine::checkLeoMaxRandomPresence() {
int retval = getRandomNumber(1, 2);
if (retval == 2)
retval = 128;
return retval;
}
/**
* Reset room variables
* @remarks Originally called 'debloc'
*/
void MortevielleEngine::resetRoomVariables(int roomId) {
_num = 0;
_x = 0;
_y = 0;
if ((roomId != ROOM26) && (roomId != LANDING))
resetPresenceInRooms(roomId);
_savedBitIndex = _currBitIndex;
}
/**
* Compute presence stats
* @remarks Originally called 'ecfren'
*/
int MortevielleEngine::getPresenceStats(int &rand, int faithScore, int roomId) {
if (roomId == OWN_ROOM)
displayAloneText();
int retVal = -500;
rand = 0;
if ( ((roomId == GREEN_ROOM) && (!_roomPresenceLuc) && (!_roomPresenceIda))
|| ((roomId == DARKBLUE_ROOM) && (!_roomPresenceGuy) && (!_roomPresenceEva)) )
retVal = getPresenceStatsGreenRoom();
if ((roomId == PURPLE_ROOM) && (!_purpleRoomPresenceLeo) && (!_juliaRoomPresenceLeo))
retVal = getPresenceStatsPurpleRoom();
if ( ((roomId == TOILETS) && (!_toiletsPresenceBobMax))
|| ((roomId == BATHROOM) && (!_bathRoomPresenceBobMax)) )
retVal = getPresenceStatsToilets();
if ((roomId == BLUE_ROOM) && (!_roomPresenceMax))
retVal = getPresenceStatsBlueRoom();
if ( ((roomId == RED_ROOM) && (!_roomPresenceBob))
|| ((roomId == GREEN_ROOM2) && (!_roomPresencePat)))
retVal = getPresenceStatsRedRoom();
if ((roomId == JULIA_ROOM) && (!_juliaRoomPresenceLeo) && (!_purpleRoomPresenceLeo))
retVal = 10;
if ( ((roomId == PURPLE_ROOM) && (_juliaRoomPresenceLeo))
|| ((roomId == JULIA_ROOM) && (_purpleRoomPresenceLeo)))
retVal = -400;
if (retVal != -500) {
retVal += faithScore;
rand = getRandomNumber(1, 100);
}
return retVal;
}
/**
* Set presence flags
* @remarks Originally called 'becfren'
*/
void MortevielleEngine::setPresenceFlags(int roomId) {
if ((roomId == GREEN_ROOM) || (roomId == DARKBLUE_ROOM)) {
int rand = getRandomNumber(1, 2);
if (roomId == GREEN_ROOM) {
if (rand == 1)
_roomPresenceLuc = true;
else
_roomPresenceIda = true;
} else { // roomId == DARKBLUE_ROOM
if (rand == 1)
_roomPresenceGuy = true;
else
_roomPresenceEva = true;
}
} else if (roomId == PURPLE_ROOM)
_purpleRoomPresenceLeo = true;
else if (roomId == TOILETS)
_toiletsPresenceBobMax = true;
else if (roomId == BLUE_ROOM)
_roomPresenceMax = true;
else if (roomId == RED_ROOM)
_roomPresenceBob = true;
else if (roomId == BATHROOM)
_bathRoomPresenceBobMax = true;
else if (roomId == GREEN_ROOM2)
_roomPresencePat = true;
else if (roomId == JULIA_ROOM)
_juliaRoomPresenceLeo = true;
}
/**
* Initialize max answers per character
* @remarks Originally called 'init_nbrepm'
*/
void MortevielleEngine::initMaxAnswer() {
static const byte maxAnswer[9] = { 0, 4, 5, 6, 7, 5, 6, 5, 8 };
for (int idx = 0; idx < 9; ++idx) {
_charAnswerMax[idx] = maxAnswer[idx];
_charAnswerCount[idx] = 0;
}
}
/**
* Get Presence
* @remarks Originally called 't11'
*/
int MortevielleEngine::getPresence(int roomId) {
int retVal = 0;
int rand;
int pres = getPresenceStats(rand, _coreVar._faithScore, roomId);
_place = roomId;
if ((roomId > OWN_ROOM) && (roomId < DINING_ROOM)) {
if (pres != -500) {
if (rand > pres) {
displayAloneText();
retVal = 0;
} else {
setPresenceFlags(_place);
retVal = getPresenceBitIndex(_place);
}
} else
retVal = getPresenceBitIndex(_place);
}
if (roomId > JULIA_ROOM) {
if ((roomId > LANDING) && (roomId != CHAPEL) && (roomId != ROOM26))
displayAloneText();
else {
int h = 0;
switch (roomId) {
case DINING_ROOM:
pres = getPresenceStatsDiningRoom(h);
break;
case BUREAU:
pres = getPresenceStatsBureau(h);
break;
case KITCHEN:
pres = getPresenceStatsKitchen();
break;
case ATTIC:
case CELLAR:
pres = getPresenceStatsAttic();
break;
case LANDING:
case ROOM26:
pres = getPresenceStatsLanding();
break;
case CHAPEL:
pres = getPresenceStatsChapel(h);
break;
}
pres += _coreVar._faithScore;
rand = getRandomNumber(1, 100);
if (rand > pres) {
displayAloneText();
retVal = 0;
} else {
switch (roomId) {
case DINING_ROOM:
pres = setPresenceDiningRoom(h);
break;
case BUREAU:
pres = setPresenceBureau(h);
break;
case KITCHEN:
case ATTIC:
case CELLAR:
pres = setPresenceKitchen();
break;
case LANDING:
case ROOM26:
pres = setPresenceLanding();
break;
case CHAPEL:
pres = setPresenceChapel(h);
break;
}
retVal = pres;
}
}
}
return retVal;
}
/**
* Display Question String
* @remarks Originally called 'writetp'
*/
void MortevielleEngine::displayQuestionText(Common::String s, int cmd) {
_screenSurface->drawString(s, cmd);
}
/**
* Display animation frame
* @remarks Originally called 'aniof'
*/
void MortevielleEngine::displayAnimFrame(int frameNum, int animId) {
if ((_caff == BATHROOM) && ((animId == 4) || (animId == 5)))
return;
if ((_caff == DINING_ROOM) && (animId == 7))
animId = 6;
else if (_caff == KITCHEN) {
if (animId == 3)
animId = 4;
else if (animId == 4)
animId = 3;
}
int offset = getAnimOffset(frameNum, animId);
GfxSurface surface;
surface.decode(&_curAnim[offset]);
_screenSurface->drawPicture(surface, 0, 12);
prepareScreenType1();
}
/**
* Draw Picture
* @remarks Originally called 'dessin'
*/
void MortevielleEngine::drawPicture() {
clearUpperLeftPart();
if (_caff > 99) {
draw(60, 33);
_screenSurface->drawBox(118, 32, 291, 122, 15); // Medium box
} else if (_caff > 69) {
draw(112, 48); // Heads
_screenSurface->drawBox(222, 47, 155, 92, 15);
} else {
draw(0, 12);
prepareScreenType1();
if ((_caff < 30) || (_caff > 32)) {
for (int i = 1; i <= 6; ++i) {
if (_openObjects[i] != 0)
displayAnimFrame(1, _openObjects[i]);
}
switch (_caff) {
case ATTIC:
if (_coreVar._atticBallHoleObjectId == 141)
displayAnimFrame(1, 7);
if (_coreVar._atticRodHoleObjectId == 159)
displayAnimFrame(1, 6);
break;
case CELLAR:
if (_coreVar._cellarObjectId == 151)
displayAnimFrame(1, 2);
break;
case SECRET_PASSAGE:
if (_coreVar._secretPassageObjectId == 143)
displayAnimFrame(1, 1);
break;
case WELL:
if (_coreVar._wellObjectId != 0)
displayAnimFrame(1, 1);
break;
}
}
if (_caff < ROOM26)
startMusicOrSpeech(1);
}
}
void MortevielleEngine::drawPictureWithText() {
_text->taffich();
drawPicture();
_destinationOk = false;
}
/**
* Engine function - Place
* @remarks Originally called 'tkey1'
*/
void MortevielleEngine::testKey(bool d) {
bool quest = false;
int x, y;
bool click;
_mouse->hideMouse();
displayStatusInDescriptionBar('K');
// Wait for release from any key or mouse button
while (keyPressed())
_key = gettKeyPressed();
do {
_mouse->getMousePosition(x, y, click);
quest = keyPressed();
if (quest && shouldQuit())
return;
} while (click);
// Event loop
do {
if (d)
prepareRoom();
quest = keyPressed();
_mouse->getMousePosition(x, y, click);
if (shouldQuit())
return;
} while (!(quest || (click) || (d && _anyone)));
if (quest)
gettKeyPressed();
setMouseClick(false);
_mouse->showMouse();
}
/**
* Display Narrative Picture
* @remarks Originally called 'tlu'
*/
void MortevielleEngine::displayNarrativePicture(int af, int ob) {
_caff = 32;
drawPictureWithText();
handleDescriptionText(6, ob + 4000);
handleDescriptionText(2, 999);
testKey(true);
_caff = af;
_currMenu = OPCODE_NONE;
_crep = 998;
}
/**
* Prepare Display Text
* @remarks Originally called 'affrep'
*/
void MortevielleEngine::prepareDisplayText() {
_caff = _coreVar._currPlace;
_crep = _coreVar._currPlace;
}
/**
* Exit room
* @remarks Originally called 'tsort'
*/
void MortevielleEngine::exitRoom() {
if ((_openObjCount > 0) && (_coreVar._currPlace != OWN_ROOM)) {
if (_coreVar._faithScore < 50)
_coreVar._faithScore += 2;
else
_coreVar._faithScore += (_coreVar._faithScore / 10);
}
resetOpenObjects();
_roomDoorId = OWN_ROOM;
_curSearchObjId = 0;
resetRoomVariables(_coreVar._currPlace);
}
/**
* get 'read' description
* @remarks Originally called 'st4'
*/
void MortevielleEngine::getReadDescription(int objId) {
_crep = 997;
switch (objId) {
case 114 :
_crep = 109;
break;
case 110 :
_crep = 107;
break;
case 158 :
_crep = 113;
break;
case 152:
case 153:
case 154:
case 155:
case 156:
case 150:
case 100:
case 157:
case 160:
case 161 :
displayNarrativePicture(_caff, objId);
break;
default:
break;
}
}
/**
* get 'search' description
* @remarks Originally called 'st7'
*/
void MortevielleEngine::getSearchDescription(int objId) {
switch (objId) {
case 116:
case 144:
_crep = 104;
break;
case 126:
case 111:
_crep = 108;
break;
case 132:
_crep = 111;
break;
case 142:
_crep = 112;
break;
default:
_crep = 183;
getReadDescription(objId);
}
}
/**
* Menu up
* @remarks Originally called 'mennor'
*/
void MortevielleEngine::menuUp() {
_menu->menuUp(_currMenu);
}
/**
* Draw discussion box
* @remarks Originally called 'premtet'
*/
void MortevielleEngine::drawDiscussionBox() {
draw(10, 80);
_screenSurface->drawBox(18, 79, 155, 92, 15);
}
/**
* Try to put an object somewhere
* @remarks Originally called 'ajchai'
*/
void MortevielleEngine::putObject() {
int putId = kAcha + ((_curSearchObjId - 1) * 10) - 1;
int i;
for (i = 1; (i <= 9) && (_tabdon[putId + i] != 0); i++)
;
if (_tabdon[putId + i] == 0)
_tabdon[putId + i] = _coreVar._selectedObjectId;
else
_crep = 192;
}
/**
* Check if inventory is full and, if not, add object in it.
* @remarks Originally called 'ajjer'
*/
void MortevielleEngine::addObjectToInventory(int objectId) {
int i;
for (i = 1; (i <= 5) && (_coreVar._inventory[i] != 0); i++)
;
if (_coreVar._inventory[i] == 0) {
_coreVar._inventory[i] = objectId;
_menu->setInventoryText();
} else
// Inventory is full
_crep = 139;
}
/**
* Interact with NPC
* @remarks Originally called 'quelquun'
*/
void MortevielleEngine::interactNPC() {
if (_menu->_menuDisplayed)
_menu->eraseMenu();
endSearch();
_crep = 997;
L1:
if (!_hiddenHero) {
if (_crep == 997)
_crep = 138;
handleDescriptionText(2, _crep);
if (_crep == 138)
_soundManager->startSpeech(5, 2, 1);
else
_soundManager->startSpeech(4, 4, 1);
if (_openObjCount == 0)
_coreVar._faithScore += 2;
else if (_coreVar._faithScore < 50)
_coreVar._faithScore += 4;
else
_coreVar._faithScore += 3 * (_coreVar._faithScore / 10);
exitRoom();
_menu->setDestinationText(LANDING);
int charIdx = convertBitIndexToCharacterIndex(_currBitIndex);
_caff = 69 + charIdx;
_crep = _caff;
_currMenu = MENU_DISCUSS;
_currAction = (_menu->_discussMenu[charIdx]._menuId << 8) | _menu->_discussMenu[charIdx]._actionId;
_syn = true;
_col = true;
} else {
if (getRandomNumber(1, 3) == 2) {
_hiddenHero = false;
_crep = 137;
goto L1;
} else {
handleDescriptionText(2, 136);
int rand = (getRandomNumber(0, 4)) - 2;
_soundManager->startSpeech(3, rand, 1);
clearDescriptionBar();
displayAloneText();
resetRoomVariables(MANOR_FRONT);
prepareDisplayText();
}
}
if (_menu->_menuDisplayed)
_menu->drawMenu();
}
/**
* Search - Prepare next object
* @remarks Originally called 'tsuiv'
*/
void MortevielleEngine::prepareNextObject() {
int objId;
int tabIdx = kAcha + ((_curSearchObjId - 1) * 10) - 1;
int localSeearchCount = 0;
do {
++localSeearchCount;
++_searchCount;
objId = _tabdon[tabIdx + _searchCount];
} while ((objId == 0) && (_searchCount <= 9));
if ((objId != 0) && (_searchCount < 11)) {
_caff = objId;
_crep = _caff + 400;
if (_currBitIndex != 0)
// Someone is present in the room
_coreVar._faithScore += 2;
} else {
prepareDisplayText();
endSearch();
if (localSeearchCount > 9)
_crep = 131;
}
}
/**
* Display Arrow status
* @remarks Originally called 'tfleche'
*/
void MortevielleEngine::displayStatusArrow() {
bool qust;
char touch;
if (_num == 9999)
return;
displayStatusInDescriptionBar((unsigned char)152);
bool inRect = false;
do {
touch = '\0';
do {
_mouse->moveMouse(qust, touch);
if (shouldQuit())
return;
if (getMouseClick())
inRect = (_mouse->_pos.x < 256 * kResolutionScaler) && (_mouse->_pos.y < 176) && (_mouse->_pos.y > 12);
prepareRoom();
} while (!(qust || inRect || _anyone));
if (qust && (touch == '\103'))
_dialogManager->show(_hintPctMessage);
} while (!((touch == '\73') || ((touch == '\104') && (_x != 0) && (_y != 0)) || (_anyone) || (inRect)));
if (touch == '\73')
_keyPressedEsc = true;
if (inRect) {
_x = _mouse->_pos.x;
_y = _mouse->_pos.y;
}
}
/**
* Set coordinates
* @remarks Originally called 'tcoord'
*/
void MortevielleEngine::setCoordinates(int sx) {
int sy, ix, iy;
int ib;
_num = 0;
_crep = 999;
int a = 0;
int atdon = kAmzon + 3;
int cy = 0;
while (cy < _caff) {
a += _tabdon[atdon];
atdon += 4;
++cy;
}
if (_tabdon[atdon] == 0) {
_crep = 997;
return;
}
a += kFleche;
int cb = 0;
for (cy = 0; cy <= (sx - 2); ++cy) {
ib = (_tabdon[a + cb] << 8) + _tabdon[(a + cb + 1)];
cb += (ib * 4) + 2;
}
ib = (_tabdon[a + cb] << 8) + _tabdon[(a + cb + 1)];
if (ib == 0) {
_crep = 997;
return;
}
cy = 1;
do {
cb += 2;
sx = _tabdon[a + cb] * kResolutionScaler;
sy = _tabdon[(a + cb + 1)];
cb += 2;
ix = _tabdon[a + cb] * kResolutionScaler;
iy = _tabdon[(a + cb + 1)];
++cy;
} while (!(((_x >= sx) && (_x <= ix) && (_y >= sy) && (_y <= iy)) || (cy > ib)));
if ((_x >= sx) && (_x <= ix) && (_y >= sy) && (_y <= iy)) {
_num = cy - 1;
return;
}
_crep = 997;
}
/**
* Display LOOK Screen
* @remarks Originally called 'treg'
*/
void MortevielleEngine::displayLookScreen(int objId) {
int mdes = _caff;
_caff = objId;
if (((_caff > 29) && (_caff < 33)) || (_caff == 144) || (_caff == 147) || (_caff == 149) || (_currAction == _menu->_opcodeSLook)) {
drawPictureWithText();
if ((_caff > 29) && (_caff < 33))
handleDescriptionText(2, _caff);
else
handleDescriptionText(2, _caff + 400);
testKey(true);
_caff = mdes;
_currMenu = MENU_NONE;
_crep = 998;
} else {
_obpart = true;
_crep = _caff + 400;
_menu->setSearchMenu();
}
}
/**
* Engine function - Put in hand
* @remarks Originally called 'avpoing'
*/
void MortevielleEngine::putInHand(int &objId) {
_crep = 999;
if (_coreVar._selectedObjectId != 0)
addObjectToInventory(_coreVar._selectedObjectId);
// If inventory wasn't full
if (_crep != 139) {
displayItemInHand(objId + 400);
_coreVar._selectedObjectId = objId;
objId = 0;
}
}
/**
* Search - Get the first object
* @remarks Originally called 'rechai'
*/
int MortevielleEngine::getFirstObject() {
int tmpPlace = _coreVar._currPlace;
if (_coreVar._currPlace == CRYPT)
tmpPlace = CELLAR;
return _tabdon[kAsearch + (tmpPlace * 7) + _num - 1];
}
/**
* Check before leaving the secret passage
* @remarks Originally called 't23coul'
*/
int MortevielleEngine::checkLeaveSecretPassage() {
if (!checkInventory(143)) {
_crep = 1512;
loseGame();
}
return CELLAR;
}
/**
* Display status character in description bar
* @remarks Originally called 'fenat'
*/
void MortevielleEngine::displayStatusInDescriptionBar(char stat) {
_mouse->hideMouse();
_screenSurface->writeCharacter(Common::Point(306, 193), stat, 12);
_screenSurface->drawBox(300, 191, 16, 8, 15);
_mouse->showMouse();
}
/**
* Test Keyboard
* @remarks Originally called 'teskbd'
*/
void MortevielleEngine::testKeyboard() {
if (keyPressed())
gettKeyPressed();
}
/**
* Test Key Pressed
* @remarks Originally called 'testou'
*/
int MortevielleEngine::gettKeyPressed() {
char ch = getChar();
switch (ch) {
case '\23' :
_soundOff = !_soundOff;
break;
case '\26' :
if ((_x26KeyCount == 1) || (_x26KeyCount == 2)) {
decodeNumber(&_cfiecBuffer[161 * 16], (_cfiecBufferSize - (161 * 16)) / 64);
++_x26KeyCount;
return 61;
}
break;
case '\33' :
if (keyPressed())
ch = getChar();
break;
default:
break;
}
return (int)ch;
}
} // End of namespace Mortevielle