aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/engines.mk5
-rw-r--r--engines/mortevielle/actions.cpp1621
-rw-r--r--engines/mortevielle/detection.cpp100
-rw-r--r--engines/mortevielle/detection_tables.h88
-rw-r--r--engines/mortevielle/dialogs.cpp480
-rw-r--r--engines/mortevielle/dialogs.h69
-rw-r--r--engines/mortevielle/graphics.cpp1165
-rw-r--r--engines/mortevielle/graphics.h111
-rw-r--r--engines/mortevielle/menu.cpp600
-rw-r--r--engines/mortevielle/menu.h81
-rw-r--r--engines/mortevielle/module.mk22
-rw-r--r--engines/mortevielle/mortevielle.cpp3790
-rw-r--r--engines/mortevielle/mortevielle.h540
-rw-r--r--engines/mortevielle/mouse.cpp420
-rw-r--r--engines/mortevielle/mouse.h52
-rw-r--r--engines/mortevielle/outtext.cpp359
-rw-r--r--engines/mortevielle/outtext.h48
-rw-r--r--engines/mortevielle/saveload.cpp317
-rw-r--r--engines/mortevielle/saveload.h68
-rw-r--r--engines/mortevielle/sound.cpp201
-rw-r--r--engines/mortevielle/sound.h112
-rw-r--r--engines/mortevielle/speech.cpp598
-rw-r--r--engines/mortevielle/speech.h99
23 files changed, 10946 insertions, 0 deletions
diff --git a/engines/engines.mk b/engines/engines.mk
index 61004463fe..9c83433a73 100644
--- a/engines/engines.mk
+++ b/engines/engines.mk
@@ -125,6 +125,11 @@ DEFINES += -DENABLE_RIVEN
endif
endif
+ifdef ENABLE_MORTEVIELLE
+DEFINES += -DENABLE_MORTEVIELLE=$(ENABLE_MORTEVIELLE)
+MODULES += engines/mortevielle
+endif
+
ifdef ENABLE_PARALLACTION
DEFINES += -DENABLE_PARALLACTION=$(ENABLE_PARALLACTION)
MODULES += engines/parallaction
diff --git a/engines/mortevielle/actions.cpp b/engines/mortevielle/actions.cpp
new file mode 100644
index 0000000000..f53dd91497
--- /dev/null
+++ b/engines/mortevielle/actions.cpp
@@ -0,0 +1,1621 @@
+/* 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 "common/scummsys.h"
+#include "mortevielle/dialogs.h"
+#include "mortevielle/menu.h"
+#include "mortevielle/mortevielle.h"
+#include "mortevielle/mouse.h"
+#include "mortevielle/outtext.h"
+#include "mortevielle/speech.h"
+
+namespace Mortevielle {
+
+/**
+ * Engine function - Move
+ * @remarks Originally called 'taller'
+ */
+void MortevielleEngine::fctMove() {
+ if ((_coreVar._currPlace == ROOM26) && (_msg[4] == _menu._moveMenu[6])) {
+ _coreVar._currPlace = LANDING;
+ _caff = _coreVar._currPlace;
+ afdes();
+ repon(2, _coreVar._currPlace);
+ }
+ if ((_coreVar._currPlace == LANDING) && (_msg[4] == _menu._moveMenu[6])) {
+ if (!_syn)
+ ecr3(getEngineString(S_GO_TO));
+ tfleche();
+
+ if (_keyPressedEsc)
+ _okdes = false;
+
+ if ((_anyone) || (_keyPressedEsc))
+ return;
+
+ setCoordinates(1);
+
+ if (_num == 0)
+ return;
+
+ if (_num == 1) {
+ _coreVar._currPlace = OWN_ROOM;
+ _menu.setDestinationText(OWN_ROOM);
+ } else if (_num == 7) {
+ _coreVar._currPlace = ATTIC;
+ _menu.setDestinationText(ATTIC);
+ } else if (_num != 6)
+ _coreVar._currPlace = ROOM26;
+
+ if ((_num > 1) && (_num < 6))
+ _roomDoorId = _num - 1;
+ else if (_num > 7)
+ _roomDoorId = _num - 3;
+
+ if (_num != 6)
+ affrep();
+ else
+ showMoveMenuAlert();
+ return;
+ }
+ exitRoom();
+ int menuChoice = 1;
+
+ while (_menu._moveMenu[menuChoice] != _msg[4])
+ ++menuChoice;
+
+ if (_coreVar._currPlace == MOUNTAIN) {
+ if (menuChoice == 1)
+ gotoManorFront();
+ else if (menuChoice == 2)
+ checkManorDistance();
+ _menu.setDestinationText(_coreVar._currPlace);
+ return;
+ } else if (_coreVar._currPlace == INSIDE_WELL) {
+ if (menuChoice == 1)
+ floodedInWell();
+ else if (menuChoice == 2)
+ gotoManorBack();
+ _menu.setDestinationText(_coreVar._currPlace);
+ return;
+ } else if ((_coreVar._currPlace == BUREAU) && (menuChoice == 1))
+ menuChoice = 6;
+ else if (_coreVar._currPlace == KITCHEN) {
+ if (menuChoice == 2)
+ menuChoice = 6;
+ else if (menuChoice == 5)
+ menuChoice = 16;
+ } else if ((_coreVar._currPlace == CELLAR) && (menuChoice == 3))
+ menuChoice = 6;
+ else if (((_coreVar._currPlace == LANDING) || (_coreVar._currPlace == ROOM26)) && (menuChoice == 4))
+ menuChoice = 6;
+
+ if ((_coreVar._currPlace > MOUNTAIN) && (_coreVar._currPlace != ROOM26))
+ menuChoice += 10;
+
+ if ((_coreVar._currPlace == CHAPEL) && (menuChoice == 13))
+ menuChoice = 16;
+ else if (_coreVar._currPlace == MANOR_FRONT) {
+ if (menuChoice == 12)
+ menuChoice = 16;
+ else if (menuChoice > 13)
+ menuChoice = 15;
+ } else if ((_coreVar._currPlace == MANOR_BACK) && (menuChoice > 14))
+ menuChoice = 15;
+ else if ((_coreVar._currPlace == WELL) && (menuChoice > 13) && (menuChoice != 17))
+ menuChoice = 15;
+
+ if (menuChoice == 1)
+ _coreVar._currPlace = BUREAU;
+ else if (menuChoice == 2)
+ _coreVar._currPlace = KITCHEN;
+ else if (menuChoice == 3)
+ _coreVar._currPlace = CELLAR;
+ else if (menuChoice == 4)
+ _coreVar._currPlace = LANDING;
+ else if (menuChoice == 5)
+ menuChoice = 12;
+ else if (menuChoice == 6)
+ menuChoice = 11;
+
+ if (menuChoice == 11)
+ gotoDiningRoom();
+ else if (menuChoice == 12)
+ gotoManorFront();
+ else if (menuChoice == 13)
+ _coreVar._currPlace = CHAPEL;
+ else if (menuChoice == 14)
+ _coreVar._currPlace = WELL;
+ else if (menuChoice == 15)
+ checkManorDistance();
+ else if (menuChoice == 16)
+ gotoManorBack();
+ else if (menuChoice == 17) {
+ if ((_coreVar._wellObjectId != 120) && (_coreVar._wellObjectId != 140))
+ _crep = 997;
+ else if (_coreVar._wellObjectId == 120)
+ _crep = 181;
+ else if (_coreVar._faithScore > 80) {
+ _crep = 1505;
+ loseGame();
+ } else {
+ _coreVar._currPlace = INSIDE_WELL;
+ affrep();
+ }
+ }
+ if ((menuChoice < 5) || (menuChoice == 13) || (menuChoice == 14))
+ affrep();
+ resetRoomVariables(_coreVar._currPlace);
+ _menu.setDestinationText(_coreVar._currPlace);
+}
+
+/**
+ * Engine function - Take
+ * @remarks Originally called 'tprendre'
+ */
+void MortevielleEngine::fctTake() {
+ if (_caff > 99) {
+ int cx = _caff;
+ avpoing(cx);
+ if (_crep != 139) {
+ if (_currBitIndex > 0)
+ _coreVar._faithScore += 3;
+ if (_obpart) {
+ if (_coreVar._currPlace == PURPLE_ROOM)
+ _coreVar._purpleRoomObjectId = 0;
+ if (_coreVar._currPlace == ATTIC) {
+ if (_coreVar._atticBallHoleObjectId == _caff)
+ _coreVar._atticBallHoleObjectId = 0;
+ if (_coreVar._atticRodHoleObjectId == _caff)
+ _coreVar._atticRodHoleObjectId = 0;
+ }
+ if (_coreVar._currPlace == CELLAR)
+ _coreVar._cellarObjectId = 0;
+ if (_coreVar._currPlace == CRYPT)
+ _coreVar._cryptObjectId = 0;
+ if (_coreVar._currPlace == SECRET_PASSAGE)
+ _coreVar._secretPassageObjectId = 0;
+ if (_coreVar._currPlace == WELL)
+ _coreVar._wellObjectId = 0;
+ _menu.unsetSearchMenu();
+ _obpart = false;
+ affrep();
+ } else {
+ _tabdon[kAcha + ((_mchai - 1) * 10) + _cs - 1] = 0;
+ tsuiv();
+ ++_takeObjCount;
+ if (_takeObjCount > 6) {
+ _coreVar._faithScore += 2;
+ _takeObjCount = 0;
+ }
+ }
+ }
+ return;
+ }
+ if (!_syn)
+ ecr3(getEngineString(S_TAKE));
+ tfleche();
+ if ((_anyone) || (_keyPressedEsc))
+ return;
+ if (_caff == 3) {
+ setCoordinates(2);
+ if (_num == 1) {
+ _crep = 152;
+ return;
+ }
+ }
+ setCoordinates(5);
+ if ((_num == 0) || ((_num == 1) && (_coreVar._currPlace == CRYPT))) {
+ setCoordinates(8);
+ if (_num != 0) {
+ if (_currBitIndex > 0)
+ _coreVar._faithScore += 3;
+ _crep = 997;
+ if ((_coreVar._currPlace == PURPLE_ROOM) && (_coreVar._purpleRoomObjectId != 0))
+ avpoing(_coreVar._purpleRoomObjectId);
+ if ((_coreVar._currPlace == ATTIC) && (_num == 1) && (_coreVar._atticBallHoleObjectId != 0)) {
+ avpoing(_coreVar._atticBallHoleObjectId);
+ if ((_crep != 997) && (_crep != 139))
+ aniof(2, 7);
+ }
+ if ((_coreVar._currPlace == ATTIC) && (_num == 2) && (_coreVar._atticRodHoleObjectId != 0)) {
+ avpoing(_coreVar._atticRodHoleObjectId);
+ if ((_crep != 997) && (_crep != 139))
+ aniof(2, 6);
+ }
+ if ((_coreVar._currPlace == CELLAR) && (_coreVar._cellarObjectId != 0)) {
+ avpoing(_coreVar._cellarObjectId);
+ if ((_crep != 997) && (_crep != 139))
+ aniof(2, 2);
+ }
+ if ((_coreVar._currPlace == CRYPT) && (_coreVar._cryptObjectId != 0))
+ avpoing(_coreVar._cryptObjectId);
+
+ if ((_coreVar._currPlace == SECRET_PASSAGE) && (_coreVar._secretPassageObjectId != 0)) {
+ avpoing(_coreVar._secretPassageObjectId);
+ if ((_crep != 997) && (_crep != 139)) {
+ _crep = 182;
+ aniof(2, 1);
+ }
+ }
+ if ((_coreVar._currPlace == WELL) && (_coreVar._wellObjectId != 0)) {
+ avpoing(_coreVar._wellObjectId);
+ if ((_crep != 997) && (_crep != 139))
+ aniof(2, 1);
+ }
+ if ((_crep != 997) && (_crep != 182) && (_crep != 139))
+ _crep = 999;
+ }
+ } else {
+ if ( ((_coreVar._currPlace == OWN_ROOM) && (_num == 3))
+ || ((_coreVar._currPlace == GREEN_ROOM) && (_num == 4))
+ || ((_coreVar._currPlace == PURPLE_ROOM) && (_num == 1))
+ || ((_coreVar._currPlace == DARKBLUE_ROOM) && (_num == 3))
+ || ((_coreVar._currPlace == BLUE_ROOM) && (_num == 6))
+ || ((_coreVar._currPlace == RED_ROOM) && (_num == 2))
+ || ((_coreVar._currPlace == BATHROOM) && (_num == 6))
+ || ((_coreVar._currPlace == GREEN_ROOM2) && (_num == 4))
+ || ((_coreVar._currPlace == ROOM9) && (_num == 4))
+ || ((_coreVar._currPlace == DINING_ROOM) && (_num > 2))
+ || ((_coreVar._currPlace == BUREAU) && (_num == 7))
+ || ((_coreVar._currPlace == KITCHEN) && (_num == 6))
+ || ((_coreVar._currPlace == ATTIC) && (_num > 4))
+ || ((_coreVar._currPlace > ATTIC) && (_coreVar._currPlace != INSIDE_WELL)) )
+ _crep = 997;
+ else if (_coreVar._currPlace == INSIDE_WELL) {
+ _crep = 1504;
+ loseGame();
+ } else
+ _crep = 120;
+ }
+}
+/**
+ * Engine function - Inventory / Take
+ * @remarks Originally called 'tsprendre'
+ */
+void MortevielleEngine::fctInventoryTake() {
+ int cx = 0;
+ do {
+ ++cx;
+ } while (_menu._inventoryMenu[cx] != _msg[4]);
+ int cz = 0;
+ int cy = 0;
+ do {
+ ++cy;
+ if (ord(_coreVar._sjer[cy]) != 0)
+ ++cz;
+ } while (cz != cx);
+ cz = ord(_coreVar._sjer[cy]);
+ _coreVar._sjer[cy] = chr(0);
+ _menu.setInventoryText();
+ avpoing(cz);
+ _crep = 998;
+ clearScreenType2();
+}
+
+/**
+ * Engine function - Lift
+ * @remarks Originally called 'tsoulever'
+ */
+void MortevielleEngine::fctLift() {
+ if (!_syn)
+ ecr3(getEngineString(S_LIFT));
+ tfleche();
+ if ((_anyone) || (_keyPressedEsc))
+ return;
+ setCoordinates(3);
+ if (_num == 0) {
+ setCoordinates(8);
+ if (_num != 0) {
+ if (_currBitIndex > 0)
+ ++_coreVar._faithScore;
+ _crep = 997;
+ if ((_coreVar._currPlace == PURPLE_ROOM) && (_coreVar._purpleRoomObjectId != 0))
+ treg(_coreVar._purpleRoomObjectId);
+ }
+ return;
+ }
+ if (_currBitIndex > 0)
+ ++_coreVar._faithScore;
+ int tmpPlace = _coreVar._currPlace;
+ if (_coreVar._currPlace == CRYPT)
+ tmpPlace = 14;
+ else if (_coreVar._currPlace == MOUNTAIN)
+ tmpPlace = 15;
+ _crep = _tabdon[kAsoul + (tmpPlace << 3) + (_num - 1)];
+ if (_crep == 255)
+ _crep = 997;
+}
+
+/**
+ * Engine function - Read
+ * @remarks Originally called 'tlire'
+ */
+void MortevielleEngine::fctRead() {
+ if (_caff > 99)
+ getReadDescription(_caff);
+ else {
+ if (!_syn)
+ ecr3(getEngineString(S_READ));
+ tfleche();
+ if (!(_anyone) && !(_keyPressedEsc)) {
+ setCoordinates(4);
+ if (_num != 0)
+ _crep = 107;
+ }
+ }
+}
+
+/**
+ * Engine function - Self / Read
+ * @remarks Originally called 'tslire'
+ */
+void MortevielleEngine::fctSelfRead() {
+ if (_coreVar._selectedObjectId == 0)
+ _crep = 186;
+ else
+ getReadDescription(_coreVar._selectedObjectId);
+}
+
+/**
+ * Engine function - Look
+ * @remarks Originally called 'tregarder'
+ */
+void MortevielleEngine::fctLook() {
+ int cx;
+
+ if (_caff > 99) {
+ _crep = 103;
+ return;
+ }
+ if (!_syn)
+ ecr3(getEngineString(S_LOOK));
+ tfleche();
+ if ((_anyone) || (_keyPressedEsc))
+ return;
+ setCoordinates(5);
+ if (_num == 0) {
+ setCoordinates(8);
+ _crep = 131;
+ if (_num != 0) {
+ if (_coreVar._currPlace == ATTIC) {
+ if (_num == 1) {
+ _crep = 164;
+ if (_coreVar._atticRodHoleObjectId != 0)
+ treg(_coreVar._atticRodHoleObjectId);
+ else if (_coreVar._atticBallHoleObjectId != 0)
+ treg(_coreVar._atticBallHoleObjectId);
+ } else {
+ _crep = 193;
+ if (_coreVar._atticRodHoleObjectId != 0)
+ treg(_coreVar._atticRodHoleObjectId);
+ }
+ }
+ if (_coreVar._currPlace == CELLAR) {
+ _crep = 164;
+ if (_coreVar._cellarObjectId != 0)
+ treg(_coreVar._cellarObjectId);
+ }
+ if (_coreVar._currPlace == SECRET_PASSAGE) {
+ _crep = 174;
+ if (_coreVar._secretPassageObjectId != 0)
+ treg(_coreVar._secretPassageObjectId);
+ }
+ if (_coreVar._currPlace == WELL) {
+ _crep = 131;
+ if (_coreVar._wellObjectId != 0)
+ treg(_coreVar._wellObjectId);
+ }
+ }
+ return;
+ }
+ cx = _coreVar._currPlace;
+ if (_coreVar._currPlace == CHAPEL)
+ cx = 17;
+ if ((_coreVar._currPlace > MANOR_FRONT) && (_coreVar._currPlace < DOOR))
+ cx -= 4;
+ if (_coreVar._currPlace == ROOM26)
+ cx = 21;
+ _crep = _tabdon[kArega + (cx * 7) + _num - 1];
+ if ((_coreVar._currPlace == ATTIC) && (_num == 8))
+ _crep = 126;
+ if (_coreVar._currPlace == MOUNTAIN)
+ _crep = 103;
+ if (_crep == 255)
+ _crep = 131;
+ if ((_coreVar._currPlace == GREEN_ROOM) && (_num == 1))
+ treg(144);
+ if ((_coreVar._currPlace == BLUE_ROOM) && (_num == 3))
+ treg(147);
+ if ((_coreVar._currPlace == GREEN_ROOM2) && (_num == 3))
+ treg(149);
+ if ((_coreVar._currPlace == ROOM9) && (_num == 2))
+ treg(30);
+ if ((_coreVar._currPlace == DINING_ROOM) && (_num == 3))
+ treg(31);
+}
+
+/**
+ * Engine function - Self / Look
+ * @remarks Originally called 'tsregarder'
+ */
+void MortevielleEngine::fctSelftLook() {
+ if (_coreVar._selectedObjectId != 0)
+ treg(_coreVar._selectedObjectId);
+ else
+ _crep = 186;
+}
+
+/**
+ * Engine function - Search
+ * @remarks Originally called 'tfouiller'
+ */
+void MortevielleEngine::fctSearch() {
+ const byte r[14] = {123, 104, 123, 131, 131, 123, 104, 131, 123, 123, 106, 123, 123, 107};
+
+ if (_caff > 99) {
+ getSearchDescription(_caff);
+ return;
+ }
+
+ if (!_syn)
+ ecr3(getEngineString(S_SEARCH));
+
+ tfleche();
+ if (_anyone || _keyPressedEsc)
+ return;
+
+ if (_coreVar._currPlace == INSIDE_WELL) {
+ _crep = 1504;
+ loseGame();
+ return;
+ }
+
+ setCoordinates(6);
+ if (_num == 0) {
+ setCoordinates(7);
+ if (_num != 0) {
+ int cx = 0;
+ do {
+ ++cx;
+ } while ((cx <= 6) && (_num != ord(_touv[cx])));
+ if (_num != ord(_touv[cx]))
+ _crep = 187;
+ else {
+ if (_currBitIndex > 0)
+ _coreVar._faithScore += 3;
+
+ rechai(_mchai);
+ if (_mchai != 0) {
+ _cs = 0;
+ _is = 0;
+ _heroSearching = true;
+ _menu.setSearchMenu();
+ tsuiv();
+ } else
+ _crep = 997;
+ }
+ } else {
+ setCoordinates(8);
+ _crep = 997;
+ if (_num != 0) {
+ if (_currBitIndex > 0)
+ _coreVar._faithScore += 3;
+ if ((_coreVar._currPlace != WELL) && (_coreVar._currPlace != SECRET_PASSAGE) && (_coreVar._currPlace != ATTIC)) {
+ if (_coreVar._currPlace == PURPLE_ROOM) {
+ _crep = 123;
+ if (_coreVar._purpleRoomObjectId != 0)
+ treg(_coreVar._purpleRoomObjectId);
+ }
+ if (_coreVar._currPlace == CRYPT) {
+ _crep = 123;
+ if (_coreVar._cryptObjectId != 0)
+ treg(_coreVar._cryptObjectId);
+ }
+ }
+ }
+ }
+ } else {
+ if (_currBitIndex > 0)
+ _coreVar._faithScore += 3;
+ _crep = 997;
+ if (_coreVar._currPlace < CELLAR)
+ _crep = r[_coreVar._currPlace];
+
+ if ((_coreVar._currPlace == TOILETS) && (_num == 2))
+ _crep = 162;
+
+ if (_coreVar._currPlace == KITCHEN) {
+ if ((_num == 3) || (_num == 4))
+ _crep = 162;
+ else if (_num == 5)
+ _crep = 159;
+ }
+
+ if (_coreVar._currPlace == MOUNTAIN)
+ _crep = 104;
+ else if (_coreVar._currPlace == CRYPT)
+ _crep = 155;
+ }
+}
+
+/**
+ * Engine function - Self / Search
+ * @remarks Originally called 'tsfouiller'
+ */
+void MortevielleEngine::fctSelfSearch() {
+ if (_coreVar._selectedObjectId != 0)
+ getSearchDescription(_coreVar._selectedObjectId);
+ else
+ _crep = 186;
+}
+
+/**
+ * Engine function - Open
+ * @remarks Originally called 'touvrir'
+ */
+void MortevielleEngine::fctOpen() {
+ if (!_syn)
+ ecr3(getEngineString(S_OPEN));
+
+ if (_caff == ROOM26) {
+ if (_roomDoorId != OWN_ROOM) {
+ _msg[4] = OPCODE_ENTER;
+ _syn = true;
+ } else
+ _crep = 997;
+ return;
+ }
+
+ if (_caff == 15) {
+ showMoveMenuAlert();
+ return;
+ }
+
+ tfleche();
+ if ((_anyone) || (_keyPressedEsc))
+ return;
+
+ setCoordinates(7);
+ if (_num != 0) {
+ if (_currBitIndex > 0)
+ _coreVar._faithScore += 2;
+ ++_openObjCount;
+ int tmpPlace = 0;
+ do {
+ ++tmpPlace;
+ } while (!((tmpPlace > 6) || (ord(_touv[tmpPlace]) == 0) || (ord(_touv[tmpPlace]) == _num)));
+ if (ord(_touv[tmpPlace]) != _num) {
+ if (!( ((_num == 3) && ((_coreVar._currPlace == OWN_ROOM) || (_coreVar._currPlace == ROOM9) || (_coreVar._currPlace == BLUE_ROOM) || (_coreVar._currPlace == BATHROOM)))
+ || ((_num == 4) && ((_coreVar._currPlace == GREEN_ROOM) || (_coreVar._currPlace == PURPLE_ROOM) || (_coreVar._currPlace == RED_ROOM)))
+ || ((_coreVar._currPlace == DARKBLUE_ROOM) && (_num == 5))
+ || ((_num == 6) && ((_coreVar._currPlace == BATHROOM) || (_coreVar._currPlace == DINING_ROOM) || (_coreVar._currPlace == GREEN_ROOM2) || (_coreVar._currPlace == ATTIC)))
+ || ((_coreVar._currPlace == GREEN_ROOM2) && (_num == 2))
+ || ((_coreVar._currPlace == KITCHEN) && (_num == 7))) ) {
+ if ( ((_coreVar._currPlace > DINING_ROOM) && (_coreVar._currPlace < CELLAR))
+ || ((_coreVar._currPlace > RED_ROOM) && (_coreVar._currPlace < DINING_ROOM))
+ || (_coreVar._currPlace == OWN_ROOM) || (_coreVar._currPlace == PURPLE_ROOM) || (_coreVar._currPlace == BLUE_ROOM)) {
+ if (getRandomNumber(1, 4) == 3)
+ _speechManager.startSpeech(7, 9, 1);
+ }
+ _touv[tmpPlace] = chr(_num);
+ aniof(1, _num);
+ }
+ tmpPlace = _coreVar._currPlace;
+ if (_coreVar._currPlace == CRYPT)
+ tmpPlace = CELLAR;
+ _crep = _tabdon[kAouvr + (tmpPlace * 7) + _num - 1];
+ if (_crep == 254)
+ _crep = 999;
+ } else
+ _crep = 18;
+ }
+}
+
+/**
+ * Engine function - Place
+ * @remarks Originally called 'tmettre'
+ */
+void MortevielleEngine::fctPlace() {
+ if (_coreVar._selectedObjectId == 0) {
+ _crep = 186;
+ return;
+ }
+
+ if (!_syn)
+ ecr3(getEngineString(S_PUT));
+
+ tfleche();
+ if (_keyPressedEsc)
+ _crep = 998;
+
+ if ((_anyone) || (_keyPressedEsc))
+ return;
+
+ setCoordinates(8);
+ if (_num != 0) {
+ _crep = 999;
+ if (_caff == 13) {
+ if (_num == 1) {
+ if (_coreVar._atticBallHoleObjectId != 0) {
+ _crep = 188;
+ } else {
+ _coreVar._atticBallHoleObjectId = _coreVar._selectedObjectId;
+ if (_coreVar._selectedObjectId == 141)
+ aniof(1, 7);
+ }
+ } else if (_coreVar._atticRodHoleObjectId != 0) {
+ _crep = 188;
+ } else {
+ _coreVar._atticRodHoleObjectId = _coreVar._selectedObjectId;
+ if (_coreVar._selectedObjectId == 159)
+ aniof(1, 6);
+ }
+ }
+
+ if (_caff == 14) {
+ if (_coreVar._cellarObjectId != 0) {
+ _crep = 188;
+ } else {
+ _coreVar._cellarObjectId = _coreVar._selectedObjectId;
+ if (_coreVar._selectedObjectId == 151) {
+ // Open hidden passage
+ aniof(1, 2);
+ aniof(1, 1);
+ repon(2, 165);
+ displayEmptyHand();
+ _speechManager.startSpeech(6, -9, 1);
+
+ // Do you want to enter the hidden passage?
+ int answer = Alert::show(getEngineString(S_YES_NO), 1);
+ if (answer == 1) {
+ Common::String alertTxt = getString(582);
+ Alert::show(alertTxt, 1);
+
+ bool enterPassageFl = KnowledgeCheck::show();
+ _mouse.hideMouse();
+ hirs();
+ drawRightFrame();
+ clearScreenType2();
+ clearScreenType3();
+ _mouse.showMouse();
+ prepareRoom();
+ drawClock();
+ if (_currBitIndex != 0)
+ showPeoplePresent(_currBitIndex);
+ else
+ displayAloneText();
+
+ _menu.displayMenu();
+ if (enterPassageFl) {
+ _coreVar._currPlace = SECRET_PASSAGE;
+ _menu.setDestinationText(SECRET_PASSAGE);
+ } else {
+ _menu.setDestinationText(_coreVar._currPlace);
+ setPal(14);
+ dessin();
+ aniof(1, 2);
+ aniof(1, 1);
+ alertTxt = getString(577);
+ Alert::show(alertTxt, 1);
+ aniof(2, 1);
+ _crep = 166;
+ }
+ affrep();
+ } else {
+ aniof(2, 1);
+ _crep = 166;
+ }
+ return;
+ }
+ }
+ }
+
+ if (_caff == 16) {
+ if (_coreVar._cryptObjectId == 0)
+ _coreVar._cryptObjectId = _coreVar._selectedObjectId;
+ else
+ _crep = 188;
+ }
+
+ if (_caff == 17) {
+ if (_coreVar._secretPassageObjectId != 0) {
+ _crep = 188;
+ } else if (_coreVar._selectedObjectId == 143) {
+ _coreVar._secretPassageObjectId = 143;
+ aniof(1, 1);
+ } else {
+ _crep = 1512;
+ loseGame();
+ }
+ }
+
+ if (_caff == 24) {
+ if (_coreVar._wellObjectId != 0) {
+ _crep = 188;
+ } else if ((_coreVar._selectedObjectId == 140) || (_coreVar._selectedObjectId == 120)) {
+ _coreVar._wellObjectId = _coreVar._selectedObjectId;
+ aniof(1, 1);
+ } else {
+ _crep = 185;
+ }
+ }
+
+ if (_crep != 188)
+ displayEmptyHand();
+ }
+}
+
+/**
+ * Engine function - Turn
+ * @remarks Originally called 'ttourner'
+ */
+void MortevielleEngine::fctTurn() {
+ if (_caff > 99) {
+ _crep = 149;
+ return;
+ }
+ if (!_syn)
+ ecr3(getEngineString(S_TURN));
+ tfleche();
+ if ((_anyone) || (_keyPressedEsc))
+ return;
+ setCoordinates(9);
+ if (_num != 0) {
+ _crep = 997;
+ if ((_coreVar._currPlace == ATTIC) && (_coreVar._atticRodHoleObjectId == 159) && (_coreVar._atticBallHoleObjectId == 141)) {
+ repon(2, 167);
+ _speechManager.startSpeech(7, 9, 1);
+ int answer = Alert::show(getEngineString(S_YES_NO), 1);
+ if (answer == 1)
+ _endGame = true;
+ else
+ _crep = 168;
+ }
+ if ((_coreVar._currPlace == SECRET_PASSAGE) && (_coreVar._secretPassageObjectId == 143)) {
+ repon(2, 175);
+ clearScreenType3();
+ _speechManager.startSpeech(6, -9, 1);
+ int answer = Alert::show(getEngineString(S_YES_NO), 1);
+ if (answer == 1) {
+ _coreVar._currPlace = CRYPT;
+ affrep();
+ } else
+ _crep = 176;
+ }
+ }
+}
+
+/**
+ * Engine function - Hide Self
+ * @remarks Originally called 'tcacher'
+ */
+void MortevielleEngine::fctSelfHide() {
+ if (!_syn)
+ ecr3(getEngineString(S_HIDE_SELF));
+ tfleche();
+ if (!(_anyone) && !(_keyPressedEsc)) {
+ setCoordinates(10);
+ if (_num == 0)
+ _hiddenHero = false;
+ else {
+ _hiddenHero = true;
+ _crep = 999;
+ }
+ }
+}
+
+/**
+ * Engine function - Attach
+ * @remarks Originally called 'tattacher'
+ */
+void MortevielleEngine::fctAttach() {
+ if (_coreVar._selectedObjectId == 0)
+ _crep = 186;
+ else {
+ if (!_syn)
+ ecr3(getEngineString(S_TIE));
+ tfleche();
+ if (!(_anyone) && !(_keyPressedEsc)) {
+ setCoordinates(8);
+ _crep = 997;
+ if ((_num != 0) && (_coreVar._currPlace == WELL)) {
+ _crep = 999;
+ if ((_coreVar._selectedObjectId == 120) || (_coreVar._selectedObjectId == 140)) {
+ _coreVar._wellObjectId = _coreVar._selectedObjectId;
+ aniof(1, 1);
+ } else
+ _crep = 185;
+ displayEmptyHand();
+ }
+ }
+ }
+}
+
+/**
+ * Engine function - Close
+ * @remarks Originally called 'tfermer'
+ */
+void MortevielleEngine::fctClose() {
+ if (!_syn)
+ ecr3(getEngineString(S_CLOSE));
+
+ if (_caff < ROOM26) {
+ tfleche();
+ if (_keyPressedEsc)
+ _crep = 998;
+ if ((_anyone) || (_keyPressedEsc))
+ return;
+ setCoordinates(7);
+ if (_num != 0) {
+ int cx = 0;
+ do {
+ ++cx;
+ } while ((cx <= 6) && (_num != ord(_touv[cx])));
+ if (_num == ord(_touv[cx])) {
+ aniof(2, _num);
+ _crep = 998;
+ _touv[cx] = chr(0);
+ --_openObjCount;
+ if (_openObjCount < 0)
+ _openObjCount = 0;
+ int chai = 9999;
+ rechai(chai);
+ if (_mchai == chai)
+ _mchai = 0;
+ } else {
+ _crep = 187;
+ }
+ }
+ }
+ if (_caff == ROOM26)
+ _crep = 999;
+}
+
+/**
+ * Engine function - Knock
+ * @remarks Originally called 'tfrapper'
+ */
+void MortevielleEngine::fctKnock() {
+ if (!_syn)
+ ecr3(getEngineString(S_HIT));
+
+ if (_coreVar._currPlace == LANDING) {
+ Alert::show(getEngineString(S_BEFORE_USE_DEP_MENU), 1);
+ return;
+ }
+
+ if (_coreVar._currPlace < DOOR) {
+ tfleche();
+ if (!(_anyone) && !(_keyPressedEsc)) {
+ if ((_coreVar._currPlace < MOUNTAIN) && (_coreVar._currPlace != LANDING))
+ _crep = 133;
+ else
+ _crep = 997;
+ }
+
+ return;
+ }
+
+ if (_coreVar._currPlace == ROOM26) {
+ int rand = (getRandomNumber(0, 8)) - 4;
+ _speechManager.startSpeech(11, rand, 1);
+ int p = getPresenceStats(rand, _coreVar._faithScore, _roomDoorId);
+ int l = _roomDoorId;
+ if (l != OWN_ROOM) {
+ if (p != -500) {
+ if (rand > p)
+ _crep = 190;
+ else {
+ setPresenceFlags(l);
+ getKnockAnswer();
+ }
+ } else
+ getKnockAnswer();
+ }
+
+ if (_roomDoorId == GREEN_ROOM2)
+ _crep = 190;
+ }
+}
+
+/**
+ * Engine function - Self / Put
+ * @remarks Originally called 'tposer'
+ */
+void MortevielleEngine::fctSelfPut() {
+ if (!_syn)
+ ecr3(getEngineString(S_POSE));
+ if (_coreVar._selectedObjectId == 0)
+ _crep = 186;
+ else {
+ if (_caff > 99) {
+ _crep = 999;
+ ajchai();
+ if (_crep != 192)
+ displayEmptyHand();
+ return;
+ }
+ tfleche();
+ if ((_anyone) || (_keyPressedEsc))
+ return;
+ setCoordinates(7);
+ _crep = 124;
+ if (_num != 0) {
+ int chai;
+ rechai(chai);
+ if (chai == 0)
+ _crep = 997;
+ else {
+ int cx = 0;
+ do {
+ ++cx;
+ } while ((cx <= 6) && (_num != ord(_touv[cx])));
+ if (_num != ord(_touv[cx]))
+ _crep = 187;
+ else {
+ _mchai = chai;
+ _crep = 999;
+ }
+ }
+ } else {
+ setCoordinates(8);
+ if (_num != 0) {
+ _crep = 998;
+ if (_caff == PURPLE_ROOM) {
+ if (_coreVar._purpleRoomObjectId != 0)
+ _crep = 188;
+ else
+ _coreVar._purpleRoomObjectId = _coreVar._selectedObjectId;
+ }
+
+ if (_caff == ATTIC) {
+ if (_num == 1) {
+ if (_coreVar._atticBallHoleObjectId != 0)
+ _crep = 188;
+ else
+ _coreVar._atticBallHoleObjectId = _coreVar._selectedObjectId;
+ } else if (_coreVar._atticRodHoleObjectId != 0) {
+ _crep = 188;
+ } else {
+ _coreVar._atticRodHoleObjectId = _coreVar._selectedObjectId;
+ }
+ }
+
+ if (_caff == CRYPT) {
+ if (_coreVar._cryptObjectId != 0)
+ _crep = 188;
+ else
+ _coreVar._cryptObjectId = _coreVar._selectedObjectId;
+ }
+
+ if (_caff == WELL)
+ _crep = 185;
+ if ((_caff == CELLAR) || (_caff == SECRET_PASSAGE))
+ _crep = 124;
+ } else {
+ _crep = 124;
+ if (_caff == WELL) {
+ setCoordinates(5);
+ if (_num != 0)
+ _crep = 185;
+ }
+ }
+ }
+ if (_caff == INSIDE_WELL)
+ _crep = 185;
+ if ((_crep == 999) || (_crep == 185) || (_crep == 998)) {
+ if (_crep == 999)
+ ajchai();
+ if (_crep != 192)
+ displayEmptyHand();
+ }
+ }
+}
+
+/**
+ * Engine function - Listen
+ * @remarks Originally called 'tecouter'
+ */
+void MortevielleEngine::fctListen() {
+ if (_coreVar._currPlace != ROOM26)
+ _crep = 101;
+ else {
+ if (_currBitIndex != 0)
+ ++_coreVar._faithScore;
+ int rand;
+ int p = getPresenceStats(rand, _coreVar._faithScore, _roomDoorId);
+ int l = _roomDoorId;
+ if (l != OWN_ROOM) {
+ if (p != -500) {
+ if (rand > p)
+ _crep = 101;
+ else {
+ setPresenceFlags(l);
+ int j, h, m;
+ updateHour(j, h, m);
+ rand = getRandomNumber(1, 100);
+ if ((h >= 0) && (h < 8)) {
+ if (rand > 30)
+ _crep = 101;
+ else
+ _crep = 178;
+ } else if (rand > 70)
+ _crep = 101;
+ else
+ _crep = 178;
+ }
+ } else
+ _crep = 178;
+ }
+ }
+}
+
+/**
+ * Engine function - Eat
+ * @remarks Originally called 'tmanger'
+ */
+void MortevielleEngine::fctEat() {
+ if ((_coreVar._currPlace > LANDING) && (_coreVar._currPlace < ROOM26)) {
+ _crep = 148;
+ } else {
+ exitRoom();
+ _coreVar._currPlace = DINING_ROOM;
+ _caff = 10;
+ resetRoomVariables(_coreVar._currPlace);
+ _menu.setDestinationText(_coreVar._currPlace);
+
+ int j, h, m;
+ updateHour(j, h, m);
+ if ((h == 12) || (h == 13) || (h == 19)) {
+ _coreVar._faithScore -= (_coreVar._faithScore / 7);
+ if (h == 12) {
+ if (m == 0)
+ h = 4;
+ else
+ h = 3;
+ }
+
+ if ((h == 13) || (h == 19)) {
+ if (m == 0)
+ h = 2;
+ else
+ h = 1;
+ }
+
+ _jh += h;
+ _crep = 135;
+ prepareRoom();
+ } else {
+ _crep = 134;
+ }
+ }
+}
+
+/**
+ * Engine function - Enter
+ * @remarks Originally called 'tentrer'
+ */
+void MortevielleEngine::fctEnter() {
+ if ((_coreVar._currPlace == MANOR_FRONT) || (_coreVar._currPlace == MANOR_BACK)) {
+ gotoDiningRoom();
+ _menu.setDestinationText(_coreVar._currPlace);
+ } else if (_coreVar._currPlace == LANDING)
+ showMoveMenuAlert();
+ else if (_roomDoorId == OWN_ROOM)
+ _crep = 997;
+ else if ((_roomDoorId == ROOM9) && (_coreVar._selectedObjectId != 136)) {
+ _crep = 189;
+ _coreVar._teauto[8] = '*';
+ } else {
+ int z = 0;
+ if (!_blo)
+ z = getPresence(_roomDoorId);
+ if (z != 0) {
+ if ((_roomDoorId == TOILETS) || (_roomDoorId == BATHROOM))
+ _crep = 179;
+ else {
+ _x = (getRandomNumber(0, 10)) - 5;
+ _speechManager.startSpeech(7, _x, 1);
+ aniof(1, 1);
+
+ _x = convertBitIndexToCharacterIndex(z);
+ ++_coreVar._faithScore;
+ _coreVar._currPlace = LANDING;
+ _msg[3] = MENU_DISCUSS;
+ _msg[4] = _menu._discussMenu[_x];
+ _syn = true;
+ if (_roomDoorId == ROOM9) {
+ _col = true;
+ _caff = 70;
+ afdes();
+ repon(2, _caff);
+ } else
+ _col = false;
+ resetRoomVariables(_roomDoorId);
+ _roomDoorId = OWN_ROOM;
+ }
+ } else {
+ _x = (getRandomNumber(0, 10)) - 5;
+ _speechManager.startSpeech(7, _x, 1);
+ aniof(1, 1);
+
+ _coreVar._currPlace = _roomDoorId;
+ affrep();
+ resetRoomVariables(_coreVar._currPlace);
+ _menu.setDestinationText(_coreVar._currPlace);
+ _roomDoorId = OWN_ROOM;
+ _savedBitIndex = 0;
+ _currBitIndex = 0;
+ }
+ }
+}
+
+/**
+ * Engine function - Sleep
+ * @remarks Originally called 'tdormir'
+ */
+void MortevielleEngine::fctSleep() {
+ int z, j, h, m;
+
+ if ((_coreVar._currPlace > LANDING) && (_coreVar._currPlace < ROOM26)) {
+ _crep = 148;
+ return;
+ }
+ if (_coreVar._currPlace != OWN_ROOM) {
+ exitRoom();
+ _coreVar._currPlace = OWN_ROOM;
+ affrep();
+ afdes();
+ resetRoomVariables(_coreVar._currPlace);
+ _menu.setDestinationText(_coreVar._currPlace);
+ }
+ clearScreenType3();
+ clearScreenType2();
+ prepareScreenType2();
+ ecr2(getEngineString(S_WANT_TO_WAKE_UP));
+ updateHour(j, h, m);
+
+ int answer;
+ do {
+ if (h < 8) {
+ _coreVar._faithScore -= (_coreVar._faithScore / 20);
+ z = (7 - h) * 2;
+ if (m == 30)
+ --z;
+ _jh += z;
+ h = 7;
+ }
+ _jh += 2;
+ ++h;
+ if (h > 23)
+ h = 0;
+ prepareRoom();
+ answer = Alert::show(getEngineString(S_YES_NO), 1);
+ _anyone = false;
+ } while (answer != 1);
+ _crep = 998;
+ _num = 0;
+}
+
+/**
+ * Engine function - Force
+ * @remarks Originally called 'tdefoncer'
+ */
+void MortevielleEngine::fctForce() {
+ if (!_syn)
+ ecr3(getEngineString(S_SMASH));
+ if (_caff < 25)
+ tfleche();
+
+ if ((!_anyone) && (!_keyPressedEsc)) {
+ if (_coreVar._currPlace != ROOM26)
+ _crep = 997;
+ else {
+ _crep = 143;
+ _coreVar._faithScore += 2;
+ }
+ }
+}
+
+/**
+ * Engine function - Leave
+ * @remarks Originally called 'tsortir'
+ */
+void MortevielleEngine::fctLeave() {
+ exitRoom();
+ _crep = 0;
+ if ((_coreVar._currPlace == MOUNTAIN) || (_coreVar._currPlace == MANOR_FRONT) || (_coreVar._currPlace == MANOR_BACK) || (_coreVar._currPlace == WELL))
+ _crep = 997;
+ else {
+ int nextPlace = OWN_ROOM;
+
+ if ((_coreVar._currPlace < CRYPT) || (_coreVar._currPlace == ROOM26))
+ nextPlace = DINING_ROOM;
+ else if ((_coreVar._currPlace == DINING_ROOM) || (_coreVar._currPlace == CHAPEL))
+ nextPlace = MANOR_FRONT;
+ else if ((_coreVar._currPlace < DINING_ROOM) || (_coreVar._currPlace == ATTIC))
+ nextPlace = LANDING;
+ else if (_coreVar._currPlace == CRYPT) {
+ nextPlace = SECRET_PASSAGE;
+ _crep = 176;
+ } else if (_coreVar._currPlace == SECRET_PASSAGE)
+ nextPlace = checkLeaveSecretPassage();
+ else if (_coreVar._currPlace == INSIDE_WELL)
+ nextPlace = WELL;
+
+ if (_crep != 997)
+ _coreVar._currPlace = nextPlace;
+
+ _caff = nextPlace;
+ if (_crep == 0)
+ _crep = nextPlace;
+ resetRoomVariables(nextPlace);
+ _menu.setDestinationText(nextPlace);
+ }
+}
+
+/**
+ * Engine function - Wait
+ * @remarks Originally called 'tattendre'
+ */
+void MortevielleEngine::fctWait() {
+ _savedBitIndex = 0;
+ clearScreenType3();
+
+ int answer;
+ do {
+ ++_jh;
+ prepareRoom();
+ if (!_blo)
+ getPresence(_coreVar._currPlace);
+ if ((_currBitIndex != 0) && (_savedBitIndex == 0)) {
+ _crep = 998;
+ if ((_coreVar._currPlace == ATTIC) || (_coreVar._currPlace == CELLAR))
+ initCaveOrCellar();
+ if ((_coreVar._currPlace > OWN_ROOM) && (_coreVar._currPlace < DINING_ROOM))
+ _anyone = true;
+ _savedBitIndex = _currBitIndex;
+ if (!_anyone)
+ prepareRoom();
+ return;
+ }
+ repon(2, 102);
+ answer = Alert::show(getEngineString(S_YES_NO), 1);
+ } while (answer != 2);
+ _crep = 998;
+ if (!_anyone)
+ prepareRoom();
+}
+
+/**
+ * Engine function - Sound
+ * @remarks Originally called 'tsonder'
+ */
+void MortevielleEngine::fctSound() {
+ if (!_syn)
+ ecr3(getEngineString(S_PROBE2));
+ if (_caff < 27) {
+ tfleche();
+ if (!(_anyone) && (!_keyPressedEsc))
+ _crep = 145;
+ _num = 0;
+ }
+}
+
+/**
+ * Engine function - Discuss
+ * @remarks Originally called 'tparler'
+ */
+void MortevielleEngine::fctDiscuss() {
+ bool te[47];
+ int cy, cx, max, suj, co, lig, icm, i, choi, x, y, c;
+ char tou;
+ Common::String lib[47];
+ bool f;
+
+ endSearch();
+ if (_col)
+ suj = 128;
+ else {
+ cx = 0;
+ do {
+ ++cx;
+ } while (_menu._discussMenu[cx] != _msg[4]);
+ _caff = 69 + cx;
+ afdes();
+ repon(2, _caff);
+ suj = _caff + 60;
+ }
+ testKey(false);
+ mennor();
+ _mouse.hideMouse();
+ hirs();
+ premtet();
+ startDialog(suj);
+ hirs();
+ for (int ix = 1; ix <= 46; ++ix)
+ te[ix] = false;
+ for (int ix = 1; ix <= 45; ++ix) {
+ lib[ix] = getString(ix + kQuestionStringIndex);
+ for (i = lib[ix].size(); i <= 40; ++i)
+ lib[ix] = lib[ix] + ' ';
+ }
+ lib[46] = lib[45];
+ lib[45] = ' ';
+ _mouse.showMouse();
+ do {
+ choi = 0;
+ icm = 0;
+ co = 0;
+ lig = 0;
+ do {
+ ++icm;
+ _screenSurface.putxy(co, lig);
+ if (_coreVar._teauto[icm] == '*') {
+ if (te[icm])
+ writetp(lib[icm], 1);
+ else
+ writetp(lib[icm], 0);
+ }
+
+ if (icm == 23) {
+ lig = 0;
+ co = 320;
+ } else
+ lig = lig + 8;
+ } while (icm != 42);
+ _screenSurface.putxy(320, 176);
+ writetp(lib[46], 0);
+ tou = '\0';
+ do {
+ _mouse.moveMouse(f, tou);
+ CHECK_QUIT;
+
+ _mouse.getMousePosition(x, y, c);
+ x *= (3 - _res);
+ if (x > 319)
+ cx = 41;
+ else
+ cx = 1;
+ cy = ((uint)y >> 3) + 1; // 0-199 => 1-25
+ if ((cy > 23) || ((cx == 41) && ((cy >= 20) && (cy <= 22)))) {
+ if (choi != 0) {
+ lig = ((choi - 1) % 23) << 3;
+ if (choi > 23)
+ co = 320;
+ else
+ co = 0;
+ _screenSurface.putxy(co, lig);
+ if (te[choi])
+ writetp(lib[choi], 0);
+ else
+ writetp(lib[choi], 1);
+ te[choi] = !te[choi];
+ choi = 0;
+ }
+ } else {
+ int ix = cy;
+ if (cx == 41)
+ ix += 23;
+ if (ix != choi) {
+ if (choi != 0) {
+ lig = ((choi - 1) % 23) << 3;
+ if (choi > 23)
+ co = 320;
+ else
+ co = 0;
+ _screenSurface.putxy(co, lig);
+ if (te[choi])
+ writetp(lib[choi], 0);
+ else
+ writetp(lib[choi], 1);
+ te[choi] = ! te[choi];
+ }
+ if ((_coreVar._teauto[ix] == '*') || (ix == 46)) {
+ lig = ((ix - 1) % 23) << 3;
+ if (ix > 23)
+ co = 320;
+ else
+ co = 0;
+ _screenSurface.putxy(co, lig);
+ if (te[ix])
+ writetp(lib[ix], 0);
+ else
+ writetp(lib[ix], 1);
+ te[ix] = ! te[ix];
+ choi = ix;
+ } else
+ choi = 0;
+ }
+ }
+ } while (!((tou == '\15') || (((c != 0) || getMouseClick()) && (choi != 0))));
+ setMouseClick(false);
+ if (choi != 46) {
+ int ix = choi - 1;
+ if (_col) {
+ _col = false;
+ _coreVar._currPlace = 15;
+ if (_openObjCount > 0)
+ max = 8;
+ else
+ max = 4;
+ if (getRandomNumber(1, max) == 2)
+ suj = 129;
+ else {
+ suj = 138;
+ _coreVar._faithScore += (3 * (_coreVar._faithScore / 10));
+ }
+ } else if (_nbrep[_caff - 69] < _nbrepm[_caff - 69]) {
+ suj = _tabdon[kArep + (ix << 3) + (_caff - 70)];
+ _coreVar._faithScore += _tabdon[kArcf + ix];
+ ++_nbrep[_caff - 69];
+ } else {
+ _coreVar._faithScore += 3;
+ suj = 139;
+ }
+ _mouse.hideMouse();
+ hirs();
+ premtet();
+ startDialog(suj);
+ _mouse.showMouse();
+ if ((suj == 84) || (suj == 86)) {
+ _coreVar._pourc[5] = '*';
+ _coreVar._teauto[7] = '*';
+ }
+ if ((suj == 106) || (suj == 108) || (suj == 94)) {
+ for (int indx = 29; indx <= 31; ++indx)
+ _coreVar._teauto[indx] = '*';
+ _coreVar._pourc[7] = '*';
+ }
+ if (suj == 70) {
+ _coreVar._pourc[8] = '*';
+ _coreVar._teauto[32] = '*';
+ }
+ _mouse.hideMouse();
+ hirs();
+ _mouse.showMouse();
+ }
+ } while ((choi != 46) && (suj != 138));
+ if (_col) {
+ _coreVar._faithScore += (3 * (_coreVar._faithScore / 10));
+ _mouse.hideMouse();
+ hirs();
+ premtet();
+ startDialog(138);
+ _mouse.showMouse();
+ _col = false;
+ _coreVar._currPlace = LANDING;
+ }
+ _controlMenu = 0;
+ _mouse.hideMouse();
+ hirs();
+ drawRightFrame();
+ _mouse.showMouse();
+ showPeoplePresent(_currBitIndex);
+ prepareRoom();
+ drawClock();
+ affrep();
+ /* chech;*/
+ _menu.setDestinationText(_coreVar._currPlace);
+ clearScreenType3();
+}
+
+/**
+ * Engine function - Smell
+ * @remarks Originally called 'tsentir'
+ */
+void MortevielleEngine::fctSmell() {
+ _crep = 119;
+ if (_caff < ROOM26) {
+ if (!_syn)
+ ecr3(getEngineString(S_SMELL));
+ tfleche();
+ if (!(_anyone) && !(_keyPressedEsc))
+ if (_caff == 16)
+ _crep = 153;
+ } else if (_caff == 123)
+ _crep = 110;
+ _num = 0;
+}
+
+/**
+ * Engine function - Scratch
+ * @remarks Originally called 'tgratter'
+ */
+void MortevielleEngine::fctScratch() {
+ _crep = 155;
+ if (_caff < 27) {
+ if (!_syn)
+ ecr3(getEngineString(S_SCRATCH));
+ tfleche();
+ }
+ _num = 0;
+}
+
+/**
+ * The game is over
+ * @remarks Originally called 'tmaj1'
+ */
+void MortevielleEngine::endGame() {
+ _quitGame = true;
+ tlu(13, 152);
+ displayEmptyHand();
+ clearScreenType1();
+ clearScreenType2();
+ clearScreenType3();
+ repon(9, 1509);
+ testKey(false);
+ _mouse.hideMouse();
+ _caff = 70;
+ _text.taffich();
+ hirs();
+ premtet();
+ startDialog(141);
+ _mouse.showMouse();
+ clearScreenType1();
+ repon(9, 1509);
+ repon(2, 142);
+ testKey(false);
+ _caff = 32;
+ afdes();
+ repon(6, 34);
+ repon(2, 35);
+ startMusicOrSpeech(0);
+ testKey(false);
+ // A wait message was displayed.
+ // testKey (aka tkey1) was called before and after.
+ // Most likely the double call is useless, thus removed
+ //
+ // testKey(false);
+ resetVariables();
+}
+
+/**
+ * You lost!
+ * @remarks Originally called 'tencore'
+ */
+void MortevielleEngine::askRestart() {
+ clearScreenType2();
+ startMusicOrSpeech(0);
+ testKey(false);
+ displayEmptyHand();
+ resetVariables();
+ initGame();
+ _currHour = 10;
+ _currHalfHour = 0;
+ _currDay = 0;
+ _minute = 0;
+ _hour = 10;
+ _day = 0;
+ repon(2, 180);
+
+ int answer = Alert::show(getEngineString(S_YES_NO), 1);
+ _quitGame = (answer != 1);
+}
+
+} // End of namespace Mortevielle
diff --git a/engines/mortevielle/detection.cpp b/engines/mortevielle/detection.cpp
new file mode 100644
index 0000000000..9fe0927706
--- /dev/null
+++ b/engines/mortevielle/detection.cpp
@@ -0,0 +1,100 @@
+/* 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.
+ *
+ */
+
+#include "base/plugins.h"
+#include "engines/advancedDetector.h"
+
+#include "mortevielle/mortevielle.h"
+#include "mortevielle/detection_tables.h"
+#include "mortevielle/saveload.h"
+
+namespace Mortevielle {
+uint32 MortevielleEngine::getGameFlags() const { return _gameDescription->flags; }
+
+Common::Language MortevielleEngine::getLanguage() const { return _gameDescription->language; }
+
+}
+
+static const PlainGameDescriptor MortevielleGame[] = {
+ {"mortevielle", "Mortville Manor"},
+ {0, 0}
+};
+
+class MortevielleMetaEngine : public AdvancedMetaEngine {
+public:
+ MortevielleMetaEngine() : AdvancedMetaEngine(Mortevielle::MortevielleGameDescriptions, sizeof(ADGameDescription),
+ MortevielleGame) {
+ _md5Bytes = 512;
+ _singleid = "mortevielle";
+ }
+ virtual const char *getName() const {
+ return "Mortevielle";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Mortville Manor (C) 1987-89 Lankhor";
+ }
+
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual SaveStateList listSaves(const char *target) const;
+ virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+};
+
+bool MortevielleMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ if (desc) {
+ *engine = new Mortevielle::MortevielleEngine(syst, desc);
+ }
+ return desc != 0;
+}
+
+bool MortevielleMetaEngine::hasFeature(MetaEngineFeature f) const {
+ switch (f) {
+ case kSupportsListSaves:
+ case kSupportsDeleteSave:
+ case kSupportsLoadingDuringStartup:
+ case kSavesSupportMetaInfo:
+ case kSavesSupportThumbnail:
+ case kSavesSupportCreationDate:
+ return true;
+ default:
+ return false;
+ }
+}
+
+int MortevielleMetaEngine::getMaximumSaveSlot() const { return 99; }
+
+SaveStateList MortevielleMetaEngine::listSaves(const char *target) const {
+ return Mortevielle::SavegameManager::listSaves(target);
+}
+
+SaveStateDescriptor MortevielleMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ return Mortevielle::SavegameManager::querySaveMetaInfos(slot);
+}
+
+
+#if PLUGIN_ENABLED_DYNAMIC(MORTEVIELLE)
+ REGISTER_PLUGIN_DYNAMIC(MORTEVIELLE, PLUGIN_TYPE_ENGINE, MortevielleMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(MORTEVIELLE, PLUGIN_TYPE_ENGINE, MortevielleMetaEngine);
+#endif
diff --git a/engines/mortevielle/detection_tables.h b/engines/mortevielle/detection_tables.h
new file mode 100644
index 0000000000..2b9a4511da
--- /dev/null
+++ b/engines/mortevielle/detection_tables.h
@@ -0,0 +1,88 @@
+/* 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.
+ *
+ */
+
+namespace Mortevielle {
+
+static const ADGameDescription MortevielleGameDescriptions[] = {
+ // French
+ {
+ "mortevielle",
+ "",
+ {
+ {"menufr.mor", 0, "e413f36b9e14eef16130adc347a9391f", 144},
+ {"dxx.mor", 0, "949e68e829ecd5ad29e36a00347a9e7e", 207744},
+ AD_LISTEND
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO0()
+ },
+ // French
+ {
+ "mortevielle",
+ "",
+ {
+ {"menu.mor", 0, "3fef0a3f8fca99fdcb6dbca8cbcef46f", 160},
+ {"dxx.mor", 0, "949e68e829ecd5ad29e36a00347a9e7e", 207744},
+ AD_LISTEND
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO0()
+ },
+ // German
+ {
+ "mortevielle",
+ "",
+ {
+ {"menual.mor", 0, "792aea282b07a1d74c4a4abeabc90c19", 144},
+ {"dxx.mor", 0, "949e68e829ecd5ad29e36a00347a9e7e", 207744},
+ AD_LISTEND
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO0()
+ },
+
+ // English. Note that this is technically the French version, but English strings in mort.dat
+ // will automatically replace all the French strings
+ {
+ "mortevielle",
+ "",
+ {
+ {"menufr.mor", 0, "e413f36b9e14eef16130adc347a9391f", 144},
+ {"dxx.mor", 0, "949e68e829ecd5ad29e36a00347a9e7e", 207744},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO0()
+ },
+
+ AD_TABLE_END_MARKER
+};
+
+} // End of namespace Mortevielle
diff --git a/engines/mortevielle/dialogs.cpp b/engines/mortevielle/dialogs.cpp
new file mode 100644
index 0000000000..b2be026ff5
--- /dev/null
+++ b/engines/mortevielle/dialogs.cpp
@@ -0,0 +1,480 @@
+/* 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 "common/str.h"
+#include "mortevielle/dialogs.h"
+#include "mortevielle/mortevielle.h"
+#include "mortevielle/mouse.h"
+#include "mortevielle/outtext.h"
+#include "mortevielle/speech.h"
+
+namespace Mortevielle {
+
+/**
+ * Alert function - Show
+ * @remarks Originally called 'do_alert'
+ */
+int Alert::show(const Common::String &msg, int n) {
+ // Make a copy of the current screen surface for later restore
+ g_vm->_backgroundSurface.copyFrom(g_vm->_screenSurface);
+
+ g_vm->_mouse.hideMouse();
+ while (g_vm->keyPressed())
+ g_vm->getChar();
+
+ g_vm->setMouseClick(false);
+
+ int colNumb = 0;
+ int lignNumb = 0;
+ int caseNumb = 0;
+ Common::String alertStr = "";
+ Common::String caseStr;
+
+ decodeAlertDetails(msg, caseNumb, lignNumb, colNumb, alertStr, caseStr);
+ g_vm->sauvecr(50, (NUM_LINES + 1) << 4);
+
+ int i = 0;
+ Common::Point curPos;
+ if (alertStr == "") {
+ drawAlertBox(10, 5, colNumb);
+ } else {
+ drawAlertBox(8, 7, colNumb);
+ i = 0;
+ g_vm->_screenSurface._textPos.y = 70;
+ do {
+ curPos.x = 320;
+ Common::String displayStr = "";
+ while ((alertStr[i + 1] != '\174') && (alertStr[i + 1] != '\135')) {
+ ++i;
+ displayStr += alertStr[i];
+ if (g_vm->_res == 2)
+ curPos.x -= 3;
+ else
+ curPos.x -= 5;
+ }
+ g_vm->_screenSurface.putxy(curPos.x, g_vm->_screenSurface._textPos.y);
+ g_vm->_screenSurface._textPos.y += 6;
+ g_vm->_screenSurface.drawString(displayStr, 4);
+ ++i;
+ } while (alertStr[i] != ']');
+ }
+ int esp;
+ if (caseNumb == 1)
+ esp = colNumb - 40;
+ else
+ esp = (uint)(colNumb - caseNumb * 40) / 2;
+
+ int coldep = 320 - ((uint)colNumb / 2) + ((uint)esp / 2);
+ Common::String buttonStr[3];
+ setButtonText(caseStr, coldep, caseNumb, &buttonStr[0], esp);
+
+ int limit[3][3];
+ memset(&limit[0][0], 0, sizeof(int) * 3 * 3);
+
+ limit[1][1] = ((uint)(coldep) / 2) * g_vm->_res;
+ limit[1][2] = limit[1][1] + 40;
+ if (caseNumb == 1) {
+ limit[2][1] = limit[2][2];
+ } else {
+ limit[2][1] = ((uint)(320 + ((uint)esp >> 1)) / 2) * g_vm->_res;
+ limit[2][2] = (limit[2][1]) + 40;
+ }
+ g_vm->_mouse.showMouse();
+ int id = 0;
+ bool dummyFl = false;
+ bool test3;
+ do {
+ char dummyKey = '\377';
+ g_vm->_mouse.moveMouse(dummyFl, dummyKey);
+ CHECK_QUIT0;
+
+ curPos = g_vm->_mouse._pos;
+ bool newaff = false;
+ if ((curPos.y > 95) && (curPos.y < 105)) {
+ bool test1 = (curPos.x > limit[1][1]) && (curPos.x < limit[1][2]);
+ bool test2 = test1;
+ if (caseNumb > 1)
+ test2 |= ((curPos.x > limit[2][1]) && (curPos.x < limit[2][2]));
+ if (test2) {
+ newaff = true;
+
+ int ix;
+ if (test1)
+ ix = 1;
+ else
+ ix = 2;
+ if (ix != id) {
+ g_vm->_mouse.hideMouse();
+ if (id != 0) {
+ setPosition(id, coldep, esp);
+
+ Common::String tmpStr(" ");
+ tmpStr += buttonStr[id];
+ tmpStr += " ";
+ g_vm->_screenSurface.drawString(tmpStr, 0);
+ }
+ setPosition(ix, coldep, esp);
+
+ Common::String tmp2 = " ";
+ tmp2 += buttonStr[ix];
+ tmp2 += " ";
+ g_vm->_screenSurface.drawString(tmp2, 1);
+
+ id = ix;
+ g_vm->_mouse.showMouse();
+ }
+ }
+ }
+ if ((id != 0) && !newaff) {
+ g_vm->_mouse.hideMouse();
+ setPosition(id, coldep, esp);
+
+ Common::String tmp3(" ");
+ tmp3 += buttonStr[id];
+ tmp3 += " ";
+ g_vm->_screenSurface.drawString(tmp3, 0);
+
+ id = 0;
+ g_vm->_mouse.showMouse();
+ }
+ test3 = (curPos.y > 95) && (curPos.y < 105) && (((curPos.x > limit[1][1]) && (curPos.x < limit[1][2]))
+ || ((curPos.x > limit[2][1]) && (curPos.x < limit[2][2])));
+ } while (!g_vm->getMouseClick());
+ g_vm->setMouseClick(false);
+ g_vm->_mouse.hideMouse();
+ if (!test3) {
+ id = n;
+ setPosition(n, coldep, esp);
+ Common::String tmp4(" ");
+ tmp4 += buttonStr[n];
+ tmp4 += " ";
+ g_vm->_screenSurface.drawString(tmp4, 1);
+ }
+ g_vm->charecr(50, (NUM_LINES + 1) * 16);
+ g_vm->_mouse.showMouse();
+
+ /* Restore the background area */
+ g_vm->_screenSurface.copyFrom(g_vm->_backgroundSurface, 0, 0);
+
+ return id;
+}
+
+/**
+ * Alert function - Decode Alert Details
+ * @remarks Originally called 'decod'
+ */
+void Alert::decodeAlertDetails(Common::String inputStr, int &choiceNumb, int &lineNumb, int &col, Common::String &choiceStr, Common::String &choiceListStr) {
+ // The second character of the string contains the number of choices
+ choiceNumb = atoi(inputStr.c_str() + 1);
+
+ choiceStr = "";
+ col = 0;
+ lineNumb = 0;
+
+ // Originally set to 5, decreased to 4 because strings are 0 based, and not 1 based as in Pascal
+ int i = 4;
+ int k = 0;
+ bool empty = true;
+
+ for (; inputStr[i] != ']'; ++i) {
+ choiceStr += inputStr[i];
+ if ((inputStr[i] == '|') || (inputStr[i + 1] == ']')) {
+ if (k > col)
+ col = k;
+ k = 0;
+ ++lineNumb;
+ } else if (inputStr[i] != ' ')
+ empty = false;
+ ++k;
+ }
+
+ if (empty) {
+ choiceStr = "";
+ col = 20;
+ } else {
+ choiceStr += ']';
+ col += 6;
+ }
+ ++i;
+ choiceListStr = g_vm->copy(inputStr, i, 30);
+ if (g_vm->_res == 2)
+ col *= 6;
+ else
+ col *= 10;
+}
+
+void Alert::setPosition(int ji, int coldep, int esp) {
+ g_vm->_screenSurface.putxy(coldep + (40 + esp) * (ji - 1), 98);
+}
+
+/**
+ * Alert function - Draw Alert Box
+ * @remarks Originally called 'fait_boite'
+ */
+void Alert::drawAlertBox(int lidep, int nli, int tx) {
+ if (tx > 640)
+ tx = 640;
+ int x = 320 - ((uint)tx / 2);
+ int y = (lidep - 1) * 8;
+ int xx = x + tx;
+ int yy = y + (nli * 8);
+ g_vm->_screenSurface.fillRect(15, Common::Rect(x, y, xx, yy));
+ g_vm->_screenSurface.fillRect(0, Common::Rect(x, y + 2, xx, y + 4));
+ g_vm->_screenSurface.fillRect(0, Common::Rect(x, yy - 4, xx, yy - 2));
+}
+
+/**
+ * Alert function - Set Button Text
+ * @remarks Originally called 'fait_choix'
+ */
+void Alert::setButtonText(Common::String c, int coldep, int nbcase, Common::String *str, int esp) {
+ int i = 1;
+ int x = coldep;
+ for (int l = 1; l <= nbcase; ++l) {
+ str[l] = "";
+ do {
+ ++i;
+ char ch = c[i];
+ str[l] += ch;
+ } while (c[i + 1] != ']');
+ i += 2;
+
+ while (str[l].size() < 3)
+ str[l] += ' ';
+
+ g_vm->_screenSurface.putxy(x, 98);
+
+ Common::String tmp(" ");
+ tmp += str[l];
+ tmp += " ";
+
+ g_vm->_screenSurface.drawString(tmp, 0);
+ x += esp + 40;
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+/**
+ * Questions asked before entering the hidden passage
+ */
+bool KnowledgeCheck::show() {
+ const int textIndexArr[10] = {511, 516, 524, 531, 545, 552, 559, 563, 570, 576};
+ const int correctAnswerArr[10] = {4, 7, 1, 6, 4, 4, 2, 5, 3, 1 };
+
+ Hotspot coor[kMaxHotspots+1];
+
+ for (int i = 0; i <= kMaxHotspots; ++i) {
+ coor[i]._rect = Common::Rect();
+ coor[i]._enabled = false;
+ }
+
+ Common::String choiceArray[15];
+
+ int currChoice, prevChoice;
+ int correctCount = 0;
+
+ for (int indx = 0; indx < 10; ++indx) {
+ g_vm->_mouse.hideMouse();
+ g_vm->hirs();
+ g_vm->_mouse.showMouse();
+ int dialogHeight;
+ if (g_vm->_res == 1)
+ dialogHeight = 29;
+ else
+ dialogHeight = 23;
+ g_vm->_screenSurface.fillRect(15, Common::Rect(0, 14, 630, dialogHeight));
+ Common::String tmpStr = g_vm->getString(textIndexArr[indx]);
+ g_vm->_text.displayStr(tmpStr, 20, 15, 100, 2, 0);
+
+ int firstOption;
+ int lastOption;
+
+ if (indx != 9) {
+ firstOption = textIndexArr[indx] + 1;
+ lastOption = textIndexArr[indx + 1] - 1;
+ } else {
+ firstOption = 503;
+ lastOption = 510;
+ }
+ int optionPosY = 35;
+ int maxLength = 0;
+
+ prevChoice = 1;
+ for (int j = firstOption; j <= lastOption; ++j, ++prevChoice) {
+ tmpStr = g_vm->getString(j);
+ if ((int) tmpStr.size() > maxLength)
+ maxLength = tmpStr.size();
+ g_vm->_text.displayStr(tmpStr, 100, optionPosY, 100, 1, 0);
+ choiceArray[prevChoice] = tmpStr;
+ optionPosY += 8;
+ }
+
+ for (int j = 1; j <= lastOption - firstOption + 1; ++j) {
+ coor[j]._rect = Common::Rect(45 * g_vm->_res, 27 + j * 8, (maxLength * 3 + 55) * g_vm->_res, 34 + j * 8);
+ coor[j]._enabled = true;
+
+ while ((int)choiceArray[j].size() < maxLength) {
+ choiceArray[j] += ' ';
+ }
+ }
+ coor[lastOption - firstOption + 2]._enabled = false;
+ int rep;
+ if (g_vm->_res == 1)
+ rep = 10;
+ else
+ rep = 6;
+ g_vm->_screenSurface.drawBox(80, 33, 40 + (maxLength * rep), (lastOption - firstOption) * 8 + 16, 15);
+ rep = 0;
+
+ prevChoice = 0;
+ warning("Expected answer: %d", correctAnswerArr[indx]);
+ do {
+ g_vm->setMouseClick(false);
+ bool flag;
+ char key;
+ g_vm->_mouse.moveMouse(flag, key);
+ CHECK_QUIT0;
+
+ currChoice = 1;
+ while (coor[currChoice]._enabled && !g_vm->_mouse.isMouseIn(coor[currChoice]._rect))
+ ++currChoice;
+ if (coor[currChoice]._enabled) {
+ if ((prevChoice != 0) && (prevChoice != currChoice)) {
+ tmpStr = choiceArray[prevChoice] + '$';
+ g_vm->_text.displayStr(tmpStr, 100, 27 + (prevChoice * 8), 100, 1, 0);
+ }
+ if (prevChoice != currChoice) {
+ tmpStr = choiceArray[currChoice] + '$';
+ g_vm->_text.displayStr(tmpStr, 100, 27 + (currChoice * 8), 100, 1, 1);
+ prevChoice = currChoice;
+ }
+ } else if (prevChoice != 0) {
+ tmpStr = choiceArray[prevChoice] + '$';
+ g_vm->_text.displayStr(tmpStr, 100, 27 + (prevChoice * 8), 100, 1, 0);
+ prevChoice = 0;
+ }
+ } while (!((prevChoice != 0) && g_vm->getMouseClick()));
+
+ if (prevChoice == correctAnswerArr[indx])
+ // Answer is correct
+ ++correctCount;
+ else {
+ // Skip questions that may give hints on previous wrong answer
+ if (indx == 4)
+ ++indx;
+ else if ((indx == 6) || (indx == 7))
+ indx = 9;
+ }
+ }
+
+ return (correctCount == 10);
+}
+
+/*------------------------------------------------------------------------*/
+
+/**
+ * Draw the F3/F8 dialog
+ */
+void f3f8::draw() {
+ Common::String f3 = g_vm->getEngineString(S_F3);
+ Common::String f8 = g_vm->getEngineString(S_F8);
+
+ // Write the F3 and F8 text strings
+ g_vm->_screenSurface.putxy(3, 44);
+ g_vm->_screenSurface.drawString(f3, 5);
+ g_vm->_screenSurface._textPos.y = 51;
+ g_vm->_screenSurface.drawString(f8, 5);
+
+ // Get the width of the written text strings
+ int f3Width = g_vm->_screenSurface.getStringWidth(f3);
+ int f8Width = g_vm->_screenSurface.getStringWidth(f8);
+
+ // Write out the bounding box
+ g_vm->_screenSurface.drawBox(0, 42, MAX(f3Width, f8Width) + 6, 16, 7);
+}
+
+/**
+ * Alert function - Loop until F8 is pressed, update
+ * Graphical Device if modified
+ * @remarks Originally called 'diver'
+ */
+void f3f8::checkForF8(int SpeechNum, bool drawAni50Fl) {
+ g_vm->testKeyboard();
+ do {
+ g_vm->_speechManager.startSpeech(SpeechNum, 0, 0);
+ g_vm->_key = waitForF3F8();
+ CHECK_QUIT;
+
+ if (g_vm->_newGraphicalDevice != g_vm->_currGraphicalDevice) {
+ g_vm->_currGraphicalDevice = g_vm->_newGraphicalDevice;
+ g_vm->hirs();
+ aff50(drawAni50Fl);
+ }
+ } while (g_vm->_key != 66); // keycode for F8
+}
+
+/**
+ * Alert function - Loop until F3 or F8 is pressed
+ * @remarks Originally called 'atf3f8'
+ */
+int f3f8::waitForF3F8() {
+ int key;
+
+ do {
+ key = g_vm->testou();
+ if (g_vm->shouldQuit())
+ return key;
+ } while ((key != 61) && (key != 66));
+
+ return key;
+}
+
+void f3f8::aff50(bool drawAni50Fl) {
+ g_vm->_caff = 50;
+ g_vm->_maff = 0;
+ g_vm->_text.taffich();
+ g_vm->draw(kAdrDes, 63, 12);
+ if (drawAni50Fl)
+ ani50();
+ else
+ g_vm->repon(2, kDialogStringIndex + 142);
+
+ // Draw the f3/f8 dialog
+ draw();
+}
+
+void f3f8::ani50() {
+ g_vm->_crep = g_vm->animof(1, 1);
+ g_vm->pictout(kAdrAni, g_vm->_crep, 63, 12);
+ g_vm->_crep = g_vm->animof(2, 1);
+ g_vm->pictout(kAdrAni, g_vm->_crep, 63, 12);
+ g_vm->_largestClearScreen = (g_vm->_res == 1);
+ g_vm->repon(2, kDialogStringIndex + 143);
+}
+
+} // End of namespace Mortevielle
diff --git a/engines/mortevielle/dialogs.h b/engines/mortevielle/dialogs.h
new file mode 100644
index 0000000000..9b980af379
--- /dev/null
+++ b/engines/mortevielle/dialogs.h
@@ -0,0 +1,69 @@
+/* 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
+ */
+
+#ifndef MORTEVIELLE_ALERT_H
+#define MORTEVIELLE_ALERT_H
+
+#include "common/rect.h"
+#include "common/str.h"
+
+namespace Mortevielle {
+
+static const int NUM_LINES = 7;
+const int kMaxHotspots = 14;
+
+struct Hotspot {
+ Common::Rect _rect;
+ bool _enabled;
+};
+
+class Alert {
+private:
+ static void decodeAlertDetails(Common::String inputStr, int &choiceNumb, int &lineNumb, int &col, Common::String &choiceStr, Common::String &choiceListStr);
+ static void setPosition(int ji, int coldep, int esp);
+ static void drawAlertBox(int lidep, int nli, int tx);
+ static void setButtonText(Common::String c, int coldep, int nbcase, Common::String *str, int esp);
+public:
+ static int show(const Common::String &msg, int n);
+};
+
+class KnowledgeCheck {
+public:
+ static bool show();
+};
+
+class f3f8 {
+public:
+ static void draw();
+ static void checkForF8(int SpeechNum, bool drawAni50Fl);
+ static int waitForF3F8();
+ static void aff50(bool drawAni50Fl);
+ static void ani50();
+};
+
+} // End of namespace Mortevielle
+#endif
diff --git a/engines/mortevielle/graphics.cpp b/engines/mortevielle/graphics.cpp
new file mode 100644
index 0000000000..fec2cfbf6b
--- /dev/null
+++ b/engines/mortevielle/graphics.cpp
@@ -0,0 +1,1165 @@
+/* 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 "common/endian.h"
+#include "common/system.h"
+#include "graphics/palette.h"
+#include "mortevielle/graphics.h"
+#include "mortevielle/mortevielle.h"
+#include "mortevielle/mouse.h"
+
+namespace Mortevielle {
+
+/*-------------------------------------------------------------------------*
+ * Palette Manager
+ *
+ *-------------------------------------------------------------------------*/
+
+/**
+ * Set palette entries from the 64 colour available EGA palette
+ */
+void PaletteManager::setPalette(const int *palette, uint idx, uint size) {
+ assert((idx + size) <= 16);
+
+ // Build up the EGA palette
+ byte egaPalette[64 * 3];
+
+ byte *p = &egaPalette[0];
+ for (int i = 0; i < 64; ++i) {
+ *p++ = (i >> 2 & 1) * 0xaa + (i >> 5 & 1) * 0x55;
+ *p++ = (i >> 1 & 1) * 0xaa + (i >> 4 & 1) * 0x55;
+ *p++ = (i & 1) * 0xaa + (i >> 3 & 1) * 0x55;
+ }
+
+ // Loop through setting palette colours based on the passed indexes
+ for (; size > 0; --size, ++idx) {
+ int palIndex = palette[idx];
+ assert(palIndex < 64);
+
+ const byte *pRgb = (const byte *)&egaPalette[palIndex * 3];
+ g_system->getPaletteManager()->setPalette(pRgb, idx, 1);
+ }
+}
+
+/**
+ * Set the default EGA palette
+ */
+void PaletteManager::setDefaultPalette() {
+ int defaultPalette[16] = { 0, 1, 2, 3, 4, 5, 20, 7, 56, 57, 58, 59, 60, 61, 62, 63 };
+ setPalette(defaultPalette, 0, 16);
+}
+
+/*-------------------------------------------------------------------------*
+ * Image decoding
+ *
+ * The code in this section is responsible for decoding image resources.
+ * Images are broken down into rectangular sections, which can use one
+ * of 18 different encoding methods.
+ *-------------------------------------------------------------------------*/
+
+#define INCR_TAIX { if (_xSize & 1) ++_xSize; }
+#define DEFAULT_WIDTH (SCREEN_WIDTH / 2)
+#define BUFFER_SIZE 40000
+
+void GfxSurface::decode(const byte *pSrc) {
+ _width = _height = 0;
+ // If no transparency, use invalid (for EGA) palette index of 16. Otherwise get index to use
+ _transparency = (*pSrc == 0) ? 16 : *(pSrc + 2);
+ bool offsetFlag = *pSrc++ == 0;
+ int entryCount = *pSrc++;
+ pSrc += 2;
+
+ if (offsetFlag)
+ pSrc += 30;
+
+ // First run through the data to calculate starting offsets
+ const byte *p = pSrc;
+ _offset.x = _offset.y = 999;
+
+ assert(entryCount > 0);
+ for (int idx = 0; idx < entryCount; ++idx) {
+ _xp = READ_BE_UINT16(p + 4);
+ if (_xp < _offset.x)
+ _offset.x = _xp;
+
+ _yp = READ_BE_UINT16(p + 6);
+ if (_yp < _offset.y)
+ _offset.y = _yp;
+
+ // Move to next entry
+ int size = READ_BE_UINT16(p) + READ_BE_UINT16(p + 2);
+ if ((size % 2) == 1)
+ ++size;
+
+ p += size + 14;
+ }
+
+ // Temporary output buffer
+ byte outputBuffer[65536];
+ Common::fill(&outputBuffer[0], &outputBuffer[65536], _transparency);
+
+ byte *pDest = &outputBuffer[0];
+ const byte *pSrcStart = pSrc;
+ const byte *pLookup = NULL;
+
+ byte lookupTable[BUFFER_SIZE];
+ byte srcBuffer[BUFFER_SIZE];
+
+ // Main processing loop
+ for (int entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
+ int lookupBytes = READ_BE_UINT16(pSrc);
+ int srcSize = READ_BE_UINT16(pSrc + 2);
+ _xp = READ_BE_UINT16(pSrc + 4) - _offset.x;
+ _yp = READ_BE_UINT16(pSrc + 6) - _offset.y;
+ assert((_xp >= 0) && (_yp >= 0) && (_xp < SCREEN_WIDTH) && (_yp < SCREEN_ORIG_HEIGHT));
+ pSrc += 8;
+
+ int decomCode = READ_BE_UINT16(pSrc);
+ _xSize = READ_BE_UINT16(pSrc + 2) + 1;
+ _ySize = READ_BE_UINT16(pSrc + 4) + 1;
+ majTtxTty();
+
+ pSrc += 6;
+ pDest = &outputBuffer[0];
+
+ _lookupIndex = 0;
+ _nibbleFlag = false;
+
+ int decomIndex = 0;
+ if (decomCode >> 8) {
+ // Build up reference table
+ int tableOffset = 0;
+
+ if (decomCode & 1) {
+ // Handle decompression of the pattern lookup table
+ do {
+ int outerCount = desanalyse(pSrc);
+ int innerCount = desanalyse(pSrc);
+
+ const byte *pSrcSaved = pSrc;
+ bool savedNibbleFlag = _nibbleFlag;
+ int savedLookupIndex = _lookupIndex;
+
+ do {
+ pSrc = pSrcSaved;
+ _nibbleFlag = savedNibbleFlag;
+ _lookupIndex = savedLookupIndex;
+
+ for (int idx = 0; idx < innerCount; ++idx, ++tableOffset) {
+ assert(tableOffset < BUFFER_SIZE);
+ lookupTable[tableOffset] = suiv(pSrc);
+ }
+ } while (--outerCount > 0);
+ } while (_lookupIndex < (lookupBytes - 1));
+
+ } else {
+ assert(lookupBytes < BUFFER_SIZE);
+ for (int idx = 0; idx < (lookupBytes * 2); ++idx)
+ lookupTable[idx] = suiv(pSrc);
+ }
+
+ if (_nibbleFlag) {
+ ++pSrc;
+ _nibbleFlag = false;
+ }
+ if ((lookupBytes + srcSize) & 1)
+ ++pSrc;
+
+ tableOffset = 0;
+ _lookupIndex = 0;
+
+ if (decomCode & 2) {
+ // Handle decompression of the temporary source buffer
+ do {
+ int outerCount = desanalyse(pSrc);
+ int innerCount = desanalyse(pSrc);
+ _lookupIndex += innerCount;
+
+ if (_nibbleFlag) {
+ ++pSrc;
+ ++_lookupIndex;
+ _nibbleFlag = false;
+ }
+
+ const byte *pStart = pSrc;
+ do {
+ pSrc = pStart;
+ for (int idx = 0; idx < innerCount; ++idx) {
+ assert(tableOffset < BUFFER_SIZE);
+ srcBuffer[tableOffset++] = *pSrc++;
+ }
+ } while (--outerCount > 0);
+ } while (_lookupIndex < (srcSize - 1));
+ } else {
+ assert(srcSize < BUFFER_SIZE);
+ for (int idx = 0; idx < srcSize; ++idx)
+ srcBuffer[idx] = *pSrc++;
+ }
+
+ if (_nibbleFlag)
+ ++pSrc;
+
+ // Switch over to using the decompressed source and lookup buffers
+ pSrcStart = pSrc;
+ pDest = &outputBuffer[_yp * DEFAULT_WIDTH + _xp];
+ pSrc = &srcBuffer[0];
+ pLookup = &lookupTable[0] - 1;
+
+ _lookupValue = _lookupIndex = 0;
+ _nibbleFlag = false;
+ decomIndex = decomCode >> 8;
+ }
+
+ // Main decompression switch
+ switch (decomIndex) {
+ case 0:
+ // Draw rect at pos
+ pDest = &outputBuffer[_yp * DEFAULT_WIDTH + _xp];
+ pSrcStart = pSrc;
+ INCR_TAIX;
+
+ for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest += DEFAULT_WIDTH) {
+ byte *pDestLine = pDest;
+ for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
+ *pDestLine++ = suiv(pSrc);
+ }
+ }
+
+ pSrcStart += lookupBytes + ((lookupBytes & 1) ? 1 : 0);
+ break;
+
+ case 1:
+ // Draw rect using horizontal lines alternating left to right, then right to left
+ INCR_TAIX;
+ for (int yCtr = 0; yCtr < _ySize; ++yCtr) {
+ if ((yCtr % 2) == 0) {
+ for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
+ *pDest++ = csuiv(pSrc, pLookup);
+ }
+ } else {
+ for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
+ *--pDest = csuiv(pSrc, pLookup);
+ }
+ }
+ pDest += DEFAULT_WIDTH;
+ }
+ break;
+
+ case 2:
+ // Draw rect alternating top to bottom, bottom to top
+ for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
+ if ((xCtr % 2) == 0) {
+ for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest += DEFAULT_WIDTH) {
+ *pDest = csuiv(pSrc, pLookup);
+ }
+ } else {
+ for (int yCtr = 0; yCtr < _ySize; ++yCtr) {
+ pDest -= DEFAULT_WIDTH;
+ *pDest = csuiv(pSrc, pLookup);
+ }
+ }
+ ++pDest;
+ }
+ break;
+
+ case 3:
+ // Draw horizontal area?
+ _thickness = 2;
+ horizontal(pSrc, pDest, pLookup);
+ break;
+
+ case 4:
+ // Draw vertical area?
+ _thickness = 2;
+ vertical(pSrc, pDest, pLookup);
+ break;
+
+ case 5:
+ _thickness = 3;
+ horizontal(pSrc, pDest, pLookup);
+ break;
+
+ case 6:
+ _thickness = 4;
+ vertical(pSrc, pDest, pLookup);
+ break;
+
+ case 7:
+ // Draw rect using horizontal lines left to right
+ INCR_TAIX;
+ for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest += DEFAULT_WIDTH) {
+ byte *pDestLine = pDest;
+ for (int xCtr = 0; xCtr < _xSize; ++xCtr)
+ *pDestLine++ = csuiv(pSrc, pLookup);
+ }
+ break;
+
+ case 8:
+ // Draw box
+ for (int xCtr = 0; xCtr < _xSize; ++xCtr, ++pDest) {
+ byte *pDestLine = pDest;
+ for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDestLine += DEFAULT_WIDTH)
+ *pDestLine = csuiv(pSrc, pLookup);
+ }
+ break;
+
+ case 9:
+ _thickness = 4;
+ horizontal(pSrc, pDest, pLookup);
+ break;
+
+ case 10:
+ _thickness = 6;
+ horizontal(pSrc, pDest, pLookup);
+ break;
+
+ case 11:
+ decom11(pSrc, pDest, pLookup);
+ break;
+
+ case 12:
+ INCR_TAIX;
+ _thickness = _xInc = 1;
+ _yInc = DEFAULT_WIDTH;
+ _yEnd = _ySize;
+ _xEnd = _xSize;
+ diag(pSrc, pDest, pLookup);
+ break;
+
+ case 13:
+ INCR_TAIX;
+ _thickness = _xSize;
+ _yInc = 1;
+ _yEnd = _xSize;
+ _xInc = DEFAULT_WIDTH;
+ _xEnd = _ySize;
+ diag(pSrc, pDest, pLookup);
+ break;
+
+ case 14:
+ _thickness = _yInc = 1;
+ _yEnd = _xSize;
+ _xInc = DEFAULT_WIDTH;
+ _xEnd = _ySize;
+ diag(pSrc, pDest, pLookup);
+ break;
+
+ case 15:
+ INCR_TAIX;
+ _thickness = 2;
+ _yInc = DEFAULT_WIDTH;
+ _yEnd = _ySize;
+ _xInc = 1;
+ _xEnd = _xSize;
+ diag(pSrc, pDest, pLookup);
+ break;
+
+ case 16:
+ _thickness = 3;
+ _yInc = 1;
+ _yEnd = _xSize;
+ _xInc = DEFAULT_WIDTH;
+ _xEnd = _ySize;
+ diag(pSrc, pDest, pLookup);
+ break;
+
+ case 17:
+ INCR_TAIX;
+ _thickness = 3;
+ _yInc = DEFAULT_WIDTH;
+ _yEnd = _ySize;
+ _xInc = 1;
+ _xEnd = _xSize;
+ diag(pSrc, pDest, pLookup);
+ break;
+
+ case 18:
+ INCR_TAIX;
+ _thickness = 5;
+ _yInc = DEFAULT_WIDTH;
+ _yEnd = _ySize;
+ _xInc = 1;
+ _xEnd = _xSize;
+ diag(pSrc, pDest, pLookup);
+ break;
+
+ default:
+ error("Unknown decompression block type %d", decomIndex);
+ }
+
+ pSrc = pSrcStart;
+ debugC(2, kMortevielleGraphics, "Decoding image block %d position %d,%d size %d,%d method %d",
+ entryIndex + 1, _xp, _yp, _width, _height, decomIndex);
+ }
+
+ // At this point, the outputBuffer has the data for the image. Initialise the surface
+ // with the calculated size, and copy the lines to the surface
+ create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
+
+ for (int yCtr = 0; yCtr < _height; ++yCtr) {
+ const byte *copySrc = &outputBuffer[yCtr * DEFAULT_WIDTH];
+ byte *copyDest = (byte *)getBasePtr(0, yCtr);
+
+ Common::copy(copySrc, copySrc + _width, copyDest);
+ }
+}
+
+void GfxSurface::majTtxTty() {
+ if (!_yp)
+ _width += _xSize;
+
+ if (!_xp)
+ _height += _ySize;
+}
+
+byte GfxSurface::suiv(const byte *&pSrc) {
+ int v = *pSrc;
+ if (_nibbleFlag) {
+ ++pSrc;
+ ++_lookupIndex;
+ _nibbleFlag = false;
+ return v & 0xf;
+ } else {
+ _nibbleFlag = !_nibbleFlag;
+ return v >> 4;
+ }
+}
+
+byte GfxSurface::csuiv(const byte *&pSrc, const byte *&pLookup) {
+ assert(pLookup);
+
+ while (!_lookupValue) {
+ int v;
+ do {
+ v = suiv(pSrc) & 0xff;
+ _lookupValue += v;
+ } while (v == 0xf);
+ ++pLookup;
+ }
+
+ --_lookupValue;
+ return *pLookup;
+}
+
+int GfxSurface::desanalyse(const byte *&pSrc) {
+ int total = 0;
+ int v = suiv(pSrc);
+ if (v == 15) {
+ int v2;
+ do {
+ v2 = suiv(pSrc);
+ total += v2;
+ } while (v2 == 15);
+
+ total *= 15;
+ v = suiv(pSrc);
+ }
+
+ total += v;
+ return total;
+}
+
+void GfxSurface::horizontal(const byte *&pSrc, byte *&pDest, const byte *&pLookup) {
+ INCR_TAIX;
+ byte *pDestEnd = pDest + (_ySize - 1) * DEFAULT_WIDTH + _xSize;
+
+ for (;;) {
+ // If position is past end point, then skip this line
+ if (((_thickness - 1) * DEFAULT_WIDTH) + pDest >= pDestEnd) {
+ if (--_thickness == 0)
+ break;
+ continue;
+ }
+
+ bool continueFlag = false;
+ do {
+ for (int xIndex = 0; xIndex < _xSize; ++xIndex) {
+ if ((xIndex % 2) == 0) {
+ if (xIndex != 0)
+ ++pDest;
+
+ // Write out vertical slice top to bottom
+ for (int yIndex = 0; yIndex < _thickness; ++yIndex, pDest += DEFAULT_WIDTH)
+ *pDest = csuiv(pSrc, pLookup);
+
+ ++pDest;
+ } else {
+ // Write out vertical slice bottom to top
+ for (int yIndex = 0; yIndex < _thickness; ++yIndex) {
+ pDest -= DEFAULT_WIDTH;
+ *pDest = csuiv(pSrc, pLookup);
+ }
+ }
+ }
+
+ if ((_xSize % 2) == 0) {
+ int blockSize = _thickness * DEFAULT_WIDTH;
+ pDest += blockSize;
+ blockSize -= DEFAULT_WIDTH;
+
+ if (pDestEnd < (pDest + blockSize)) {
+ do {
+ if (--_thickness == 0)
+ return;
+ } while ((pDest + (_thickness - 1) * DEFAULT_WIDTH) >= pDestEnd);
+ }
+ } else {
+ while ((pDest + (_thickness - 1) * DEFAULT_WIDTH) >= pDestEnd) {
+ if (--_thickness == 0)
+ return;
+ }
+ }
+
+ for (int xIndex = 0; xIndex < _xSize; ++xIndex, --pDest) {
+ if ((xIndex % 2) == 0) {
+ // Write out vertical slice top to bottom
+ for (int yIndex = 0; yIndex < _thickness; ++yIndex, pDest += DEFAULT_WIDTH)
+ *pDest = csuiv(pSrc, pLookup);
+ } else {
+ // Write out vertical slice top to bottom
+ for (int yIndex = 0; yIndex < _thickness; ++yIndex) {
+ pDest -= DEFAULT_WIDTH;
+ *pDest = csuiv(pSrc, pLookup);
+ }
+ }
+ }
+
+ if ((_xSize % 2) == 1) {
+ ++pDest;
+
+ if ((pDest + (_thickness - 1) * DEFAULT_WIDTH) < pDestEnd) {
+ continueFlag = true;
+ break;
+ }
+ } else {
+ pDest += _thickness * DEFAULT_WIDTH + 1;
+ continueFlag = true;
+ break;
+ }
+
+ ++pDest;
+ } while (((_thickness - 1) * DEFAULT_WIDTH + pDest) < pDestEnd);
+
+ if (continueFlag)
+ continue;
+
+ // Move to next line
+ if (--_thickness == 0)
+ break;
+ }
+}
+
+void GfxSurface::vertical(const byte *&pSrc, byte *&pDest, const byte *&pLookup) {
+ int drawIndex = 0;
+
+ for (;;) {
+ // Reduce thickness as necessary
+ while ((drawIndex + _thickness) > _xSize) {
+ if (--_thickness == 0)
+ return;
+ }
+
+ // Loop
+ for (int yCtr = 0; yCtr < _ySize; ++yCtr) {
+ if ((yCtr % 2) == 0) {
+ if (yCtr > 0)
+ pDest += DEFAULT_WIDTH;
+
+ drawIndex += _thickness;
+ for (int xCtr = 0; xCtr < _thickness; ++xCtr)
+ *pDest++ = csuiv(pSrc, pLookup);
+ } else {
+ pDest += DEFAULT_WIDTH;
+ drawIndex -= _thickness;
+ for (int xCtr = 0; xCtr < _thickness; ++xCtr)
+ *--pDest = csuiv(pSrc, pLookup);
+ }
+ }
+ if ((_ySize % 2) == 0) {
+ pDest += _thickness;
+ drawIndex += _thickness;
+ }
+
+ while (_xSize < (drawIndex + _thickness)) {
+ if (--_thickness == 0)
+ return;
+ }
+
+ // Loop
+ for (int yCtr = 0; yCtr < _ySize; ++yCtr) {
+ if ((yCtr % 2) == 0) {
+ if (yCtr > 0)
+ pDest -= DEFAULT_WIDTH;
+
+ drawIndex += _thickness;
+
+ for (int xCtr = 0; xCtr < _thickness; ++xCtr)
+ *pDest++ = csuiv(pSrc, pLookup);
+ } else {
+ pDest -= DEFAULT_WIDTH;
+ drawIndex -= _thickness;
+
+ for (int xCtr = 0; xCtr < _thickness; ++xCtr)
+ *--pDest = csuiv(pSrc, pLookup);
+ }
+ }
+ if ((_ySize % 2) == 0) {
+ pDest += _thickness;
+ drawIndex += _thickness;
+ }
+ }
+}
+
+void GfxSurface::decom11(const byte *&pSrc, byte *&pDest, const byte *&pLookup) {
+ int yPos = 0, drawIndex = 0;
+ _yInc = DEFAULT_WIDTH;
+ _xInc = -1;
+ --_xSize;
+ --_ySize;
+
+ int areaNum = 0;
+ while (areaNum != -1) {
+ switch (areaNum) {
+ case 0:
+ *pDest = csuiv(pSrc, pLookup);
+ areaNum = 1;
+ break;
+
+ case 1:
+ increments(pDest);
+
+ if (!drawIndex) {
+ NIH();
+ NIV();
+
+ if (yPos == _ySize) {
+ increments(pDest);
+ ++drawIndex;
+ } else {
+ ++yPos;
+ }
+
+ *++pDest = csuiv(pSrc, pLookup);
+ areaNum = 2;
+ } else if (yPos != _ySize) {
+ ++yPos;
+ --drawIndex;
+ areaNum = 0;
+ } else {
+ NIH();
+ NIV();
+ increments(pDest);
+ ++drawIndex;
+
+ *++pDest = csuiv(pSrc, pLookup);
+
+ if (drawIndex == _xSize) {
+ areaNum = -1;
+ } else {
+ areaNum = 2;
+ }
+ }
+ break;
+
+ case 2:
+ increments(pDest);
+
+ if (!yPos) {
+ NIH();
+ NIV();
+
+ if (drawIndex == _xSize) {
+ increments(pDest);
+ ++yPos;
+ } else {
+ ++drawIndex;
+ }
+
+ pDest += DEFAULT_WIDTH;
+ areaNum = 0;
+ } else if (drawIndex != _xSize) {
+ ++drawIndex;
+ --yPos;
+
+ *pDest = csuiv(pSrc, pLookup);
+ areaNum = 2;
+ } else {
+ pDest += DEFAULT_WIDTH;
+ ++yPos;
+ NIH();
+ NIV();
+ increments(pDest);
+
+ *pDest = csuiv(pSrc, pLookup);
+
+ if (yPos == _ySize)
+ areaNum = -1;
+ else
+ areaNum = 1;
+ }
+ break;
+ }
+ }
+}
+
+void GfxSurface::diag(const byte *&pSrc, byte *&pDest, const byte *&pLookup) {
+ int diagIndex = 0, drawIndex = 0;
+ --_xEnd;
+
+ while (!TFP(diagIndex)) {
+ for (;;) {
+ NIH();
+ for (int idx = 0; idx <= _thickness; ++idx) {
+ *pDest = csuiv(pSrc, pLookup);
+ NIH();
+ increments(pDest);
+ }
+
+ NIV();
+ pDest += _yInc;
+
+ for (int idx = 0; idx <= _thickness; ++idx) {
+ *pDest = csuiv(pSrc, pLookup);
+ NIH();
+ increments(pDest);
+ }
+
+ NIH();
+ NIV();
+ increments(pDest);
+
+ ++drawIndex;
+ if (_xEnd < (drawIndex + 1)) {
+ TF1(pDest, diagIndex);
+ break;
+ }
+
+ pDest += _xInc;
+ ++drawIndex;
+ if (_xEnd < (drawIndex + 1)) {
+ TF2(pSrc, pDest, pLookup, diagIndex);
+ break;
+ }
+ }
+
+ if (TFP(diagIndex))
+ break;
+
+ for (;;) {
+ for (int idx = 0; idx <= _thickness; ++idx) {
+ *pDest = csuiv(pSrc, pLookup);
+ NIH();
+ increments(pDest);
+ }
+
+ NIV();
+ pDest += _yInc;
+
+ for (int idx = 0; idx <= _thickness; ++idx) {
+ *pDest = csuiv(pSrc, pLookup);
+ NIH();
+ increments(pDest);
+ }
+
+ NIH();
+ NIV();
+ increments(pDest);
+
+ if (--drawIndex == 0) {
+ TF1(pDest, diagIndex);
+ NIH();
+ break;
+ } else {
+ pDest += _xInc;
+
+ if (--drawIndex == 0) {
+ TF2(pSrc, pDest, pLookup, diagIndex);
+ NIH();
+ break;
+ }
+ }
+
+ NIH();
+ }
+ }
+}
+
+
+void GfxSurface::increments(byte *&pDest) {
+ pDest += _xInc + _yInc;
+}
+
+void GfxSurface::NIH() {
+ _xInc = -_xInc;
+}
+
+void GfxSurface::NIV() {
+ _yInc = -_yInc;
+}
+
+bool GfxSurface::TFP(int v) {
+ int diff = _yEnd - v;
+ if (!diff)
+ // Time to finish loop in outer method
+ return true;
+
+ if (diff < (_thickness + 1))
+ _thickness = diff - 1;
+
+ return false;
+}
+
+void GfxSurface::TF1(byte *&pDest, int &v) {
+ v += _thickness + 1;
+ pDest += (_thickness + 1) * _yInc;
+}
+
+void GfxSurface::TF2(const byte *&pSrc, byte *&pDest, const byte *&pLookup, int &v) {
+ v += _thickness + 1;
+
+ for (int idx = 0; idx <= _thickness; ++idx) {
+ *pDest = csuiv(pSrc, pLookup);
+ pDest += _yInc;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+GfxSurface::~GfxSurface() {
+ free();
+}
+
+/*-------------------------------------------------------------------------*
+ * Screen surface
+ *-------------------------------------------------------------------------*/
+
+/**
+ * Called to populate the font data from the passed file
+ */
+void ScreenSurface::readFontData(Common::File &f, int dataSize) {
+ assert(dataSize == (FONT_NUM_CHARS * FONT_HEIGHT));
+ f.read(_fontData, FONT_NUM_CHARS * FONT_HEIGHT);
+}
+
+/**
+ * Returns a graphics surface representing a subset of the screen. The affected area
+ * is also marked as dirty
+ */
+Graphics::Surface ScreenSurface::lockArea(const Common::Rect &bounds) {
+ _dirtyRects.push_back(bounds);
+
+ Graphics::Surface s;
+ s.format = format;
+ s.pixels = getBasePtr(bounds.left, bounds.top);
+ s.pitch = pitch;
+ s.w = bounds.width();
+ s.h = bounds.height();
+
+ return s;
+}
+
+/**
+ * Updates the affected areas of the surface to the underlying physical screen
+ */
+void ScreenSurface::updateScreen() {
+ // Iterate through copying dirty areas to the screen
+ for (Common::List<Common::Rect>::iterator i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) {
+ Common::Rect r = *i;
+ g_system->copyRectToScreen((const byte *)getBasePtr(r.left, r.top), pitch,
+ r.left, r.top, r.width(), r.height());
+ }
+ _dirtyRects.clear();
+
+ // Update the screen
+ g_system->updateScreen();
+}
+
+/**
+ * Draws a decoded picture on the screen
+ * @remarks - Because the ScummVM surface is using a double height 640x400 surface to
+ * simulate the original 640x400 surface, all Y values have to be doubled.
+ * - Image resources are stored at 320x200, so when drawn onto the screen a single pixel
+ * from the source image is drawn using the two pixels at the given index in the palette map
+ * - Because the original game supported 320 width resolutions, the X coordinate
+ * also needs to be doubled for EGA mode
+ */
+void ScreenSurface::drawPicture(GfxSurface &surface, int x, int y) {
+ // Adjust the draw position by the draw offset
+ x += surface._offset.x;
+ y += surface._offset.y;
+
+ // Lock the affected area of the surface to write to
+ Graphics::Surface destSurface = lockArea(Common::Rect(x * 2, y * 2,
+ (x + surface.w) * 2, (y + surface.h) * 2));
+
+ // Get a lookup for the palette mapping
+ const byte *paletteMap = &g_vm->_mem[0x7000 * 16 + 2];
+
+ // Loop through writing
+ for (int yp = 0; yp < surface.h; ++yp) {
+ if (((y + yp) < 0) || ((y + yp) >= 200))
+ continue;
+
+ const byte *pSrc = (const byte *)surface.getBasePtr(0, yp);
+ byte *pDest = (byte *)destSurface.getBasePtr(0, yp * 2);
+
+ for (int xp = 0; xp < surface.w; ++xp, ++pSrc) {
+ if (*pSrc == surface._transparency) {
+ // Transparent point, so skip pixels
+ pDest += 2;
+ } else {
+ // Draw the pixel using the specified index in the palette map
+ *pDest = paletteMap[*pSrc * 2];
+ *(pDest + SCREEN_WIDTH) = paletteMap[*pSrc * 2];
+ ++pDest;
+
+ // Use the secondary mapping value to draw the secondary column pixel
+ *pDest = paletteMap[*pSrc * 2 + 1];
+ *(pDest + SCREEN_WIDTH) = paletteMap[*pSrc * 2 + 1];
+ ++pDest;
+ }
+ }
+ }
+}
+
+/**
+ * Copys a given surface to the given position
+ */
+void ScreenSurface::copyFrom(Graphics::Surface &src, int x, int y) {
+ Graphics::Surface destSurface = lockArea(Common::Rect(x, y, x + src.w, y + src.h));
+
+ // Loop through writing
+ for (int yp = 0; yp < src.h; ++yp) {
+ if (((y + yp) < 0) || ((y + yp) >= SCREEN_HEIGHT))
+ continue;
+
+ const byte *pSrc = (const byte *)src.getBasePtr(0, yp);
+ byte *pDest = (byte *)getBasePtr(0, yp);
+ Common::copy(pSrc, pSrc + src.w, pDest);
+ }
+}
+
+/**
+ * Draws a character at the specified co-ordinates
+ * @remarks Because the ScummVM surface is using a double height 640x400 surface to
+ * simulate the original 640x400 surface, all Y values have to be doubled
+ */
+void ScreenSurface::writeCharacter(const Common::Point &pt, unsigned char ch, int palIndex) {
+ Graphics::Surface destSurface = lockArea(Common::Rect(pt.x, pt.y * 2,
+ pt.x + FONT_WIDTH, (pt.y + FONT_HEIGHT) * 2));
+
+ // Get the start of the character to use
+ assert((ch >= ' ') && (ch <= (unsigned char)(32 + FONT_NUM_CHARS)));
+ const byte *charData = &_fontData[((int)ch - 32) * FONT_HEIGHT];
+
+ // Loop through decoding each character's data
+ for (int yp = 0; yp < FONT_HEIGHT; ++yp) {
+ byte *lineP = (byte *)destSurface.getBasePtr(0, yp * 2);
+ byte byteVal = *charData++;
+
+ for (int xp = 0; xp < FONT_WIDTH; ++xp, ++lineP, byteVal <<= 1) {
+ if (byteVal & 0x80) {
+ *lineP = palIndex;
+ *(lineP + SCREEN_WIDTH) = palIndex;
+ }
+ }
+ }
+}
+
+/**
+ * Draws a box at the specified position and size
+ * @remarks Because the ScummVM surface is using a double height 640x400 surface to
+ * simulate the original 640x400 surface, all Y values have to be doubled
+ */
+void ScreenSurface::drawBox(int x, int y, int dx, int dy, int col) {
+ if (g_vm->_res == 1) {
+ x = (uint)x >> 1;
+ dx = (uint)dx >> 1;
+ }
+
+ Graphics::Surface destSurface = lockArea(Common::Rect(x, y * 2, x + dx, (y + dy) * 2));
+
+ destSurface.hLine(0, 0, dx, col);
+ destSurface.hLine(0, 1, dx, col);
+ destSurface.hLine(0, destSurface.h - 1, dx, col);
+ destSurface.hLine(0, destSurface.h - 2, dx, col);
+ destSurface.vLine(0, 2, destSurface.h - 3, col);
+ destSurface.vLine(1, 2, destSurface.h - 3, col);
+ destSurface.vLine(dx - 1, 2, destSurface.h - 3, col);
+ destSurface.vLine(dx - 2, 2, destSurface.h - 3, col);
+}
+
+/**
+ * Fills an area with the specified colour
+ * @remarks Because the ScummVM surface is using a double height 640x400 surface to
+ * simulate the original 640x400 surface, all Y values have to be doubled
+ */
+void ScreenSurface::fillRect(int colour, const Common::Rect &bounds) {
+ Graphics::Surface destSurface = lockArea(Common::Rect(bounds.left, bounds.top * 2,
+ bounds.right, bounds.bottom * 2));
+
+ // Fill the area
+ destSurface.fillRect(Common::Rect(0, 0, destSurface.w, destSurface.h), colour);
+}
+
+/**
+ * Clears the screen
+ */
+void ScreenSurface::clearScreen() {
+ Graphics::Surface destSurface = lockArea(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+ destSurface.fillRect(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0);
+}
+
+/**
+ * Sets a single pixel at the specified co-ordinates
+ * @remarks Because the ScummVM surface is using a double height 640x400 surface to
+ * simulate the original 640x400 surface, all Y values have to be doubled
+ */
+void ScreenSurface::setPixel(const Common::Point &pt, int palIndex) {
+ assert((pt.x >= 0) && (pt.y >= 0) && (pt.x <= SCREEN_WIDTH) && (pt.y <= SCREEN_ORIG_HEIGHT));
+ Graphics::Surface destSurface = lockArea(Common::Rect(pt.x, pt.y * 2, pt.x + 1, (pt.y + 1) * 2));
+
+ byte *destP = (byte *)destSurface.pixels;
+ *destP = palIndex;
+ *(destP + SCREEN_WIDTH) = palIndex;
+}
+
+/**
+ * Write out a string
+ * @remarks Originally called 'writeg'
+ */
+void ScreenSurface::drawString(const Common::String &l, int command) {
+ int i, x;
+ Common::Point pt;
+ int cecr = 0;
+
+ if (l == "")
+ return;
+
+ g_vm->_mouse.hideMouse();
+ pt = _textPos;
+
+ if (g_vm->_res == 2)
+ i = 6;
+ else
+ i = 10;
+ x = pt.x + i * l.size();
+
+ switch (command) {
+ case 1:
+ case 3: {
+ cecr = 0;
+ g_vm->_screenSurface.fillRect(15, Common::Rect(pt.x, pt.y, x, pt.y + 7));
+ }
+ break;
+ case 4:
+ cecr = 0;
+ break;
+ case 5:
+ cecr = 15;
+ break;
+ case 0:
+ case 2: {
+ cecr = 15;
+ g_vm->_screenSurface.fillRect(0, Common::Rect(pt.x, pt.y, x, pt.y + 7));
+ }
+ break;
+ default:
+ break;
+ }
+
+ pt.x += 1;
+ pt.y += 1;
+ for (x = 1; (x <= (int)l.size()) && (l[x - 1] != 0); ++x) {
+ g_vm->_screenSurface.writeCharacter(Common::Point(pt.x, pt.y), ord(l[x - 1]), cecr);
+ pt.x += i;
+ }
+ g_vm->_mouse.showMouse();
+}
+
+/**
+ * Gets the width in pixels of the specified string
+ */
+int ScreenSurface::getStringWidth(const Common::String &s) {
+ int charWidth = (g_vm->_res == 2) ? 6 : 10;
+
+ return s.size() * charWidth;
+}
+
+void ScreenSurface::drawLine(int x, int y, int xx, int yy, int coul) {
+ int step, i;
+ float a, b;
+ float xr, yr, xro, yro;
+
+ xr = x;
+ yr = y;
+ xro = xx;
+ yro = yy;
+
+ if (abs(y - yy) > abs(x - xx)) {
+ a = (float)((x - xx)) / (y - yy);
+ b = (yr * xro - yro * xr) / (y - yy);
+ i = y;
+ if (y > yy)
+ step = -1;
+ else
+ step = 1;
+ do {
+ g_vm->_screenSurface.setPixel(Common::Point(abs((int)(a * i + b)), i), coul);
+ i += step;
+ } while (i != yy);
+ } else {
+ a = (float)((y - yy)) / (x - xx);
+ b = ((yro * xr) - (yr * xro)) / (x - xx);
+ i = x;
+ if (x > xx)
+ step = -1;
+ else
+ step = 1;
+ do {
+ g_vm->_screenSurface.setPixel(Common::Point(i, abs((int)(a * i + b))), coul);
+ i = i + step;
+ } while (i != xx);
+ }
+}
+
+/**
+ * Draw plain rectangle
+ * @remarks Originally called 'paint_rect'
+ */
+void ScreenSurface::drawRectangle(int x, int y, int dx, int dy) {
+ int co;
+
+ if (g_vm->_currGraphicalDevice == MODE_CGA)
+ co = 3;
+ else
+ co = 11;
+ g_vm->_screenSurface.fillRect(co, Common::Rect(x, y, x + dx, y + dy));
+}
+
+
+} // End of namespace Mortevielle
diff --git a/engines/mortevielle/graphics.h b/engines/mortevielle/graphics.h
new file mode 100644
index 0000000000..03e0d016ec
--- /dev/null
+++ b/engines/mortevielle/graphics.h
@@ -0,0 +1,111 @@
+/* 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
+ */
+
+#ifndef MORTEVIELLE_GRAPHICS_H
+#define MORTEVIELLE_GRAPHICS_H
+
+#include "common/file.h"
+#include "common/list.h"
+#include "common/rect.h"
+#include "graphics/surface.h"
+
+namespace Mortevielle {
+
+class PaletteManager {
+public:
+ void setPalette(const int *palette, uint idx, uint size);
+ void setDefaultPalette();
+};
+
+#define FONT_WIDTH 8
+#define FONT_HEIGHT 6
+#define FONT_NUM_CHARS 121
+
+class GfxSurface: public Graphics::Surface {
+private:
+ int _xp, _yp;
+ int _xSize, _ySize, _var12;
+ int _var14, _lookupIndex, _lookupValue;
+ bool _nibbleFlag;
+ int _thickness;
+ int _yInc, _yEnd, _xInc, _xEnd;
+ int _width, _height;
+
+ void majTtxTty();
+ byte suiv(const byte *&pSrc);
+ byte csuiv(const byte *&pSrc, const byte *&pLookup);
+ int desanalyse(const byte *&pSrc);
+ void horizontal(const byte *&pSrc, byte *&pDest, const byte *&pLookup);
+ void vertical(const byte *&pSrc, byte *&pDest, const byte *&pLookup);
+ void decom11(const byte *&pSrc, byte *&pDest, const byte *&pLookup);
+ void diag(const byte *&pSrc, byte *&pDest, const byte *&pLookup);
+ void increments(byte *&pDest);
+ void NIH();
+ void NIV();
+ bool TFP(int v);
+ void TF1(byte *&pDest, int &v);
+ void TF2(const byte *&pSrc, byte *&pDest, const byte *&pLookup, int &v);
+public:
+ // Specifies offset when drawing the image
+ Common::Point _offset;
+ // Transparency palette index
+ int _transparency;
+public:
+ ~GfxSurface();
+
+ void decode(const byte *pSrc);
+};
+
+class ScreenSurface: public Graphics::Surface {
+private:
+ Common::List<Common::Rect> _dirtyRects;
+ byte _fontData[FONT_NUM_CHARS * FONT_HEIGHT];
+public:
+ Common::Point _textPos; // Original called xwhere/ywhere
+public:
+ void readFontData(Common::File &f, int dataSize);
+ Graphics::Surface lockArea(const Common::Rect &bounds);
+ void updateScreen();
+ void drawPicture(GfxSurface &surface, int x, int y);
+ void copyFrom(Graphics::Surface &src, int x, int y);
+ void writeCharacter(const Common::Point &pt, unsigned char ch, int palIndex);
+ void drawBox(int x, int y, int dx, int dy, int col);
+ void fillRect(int colour, const Common::Rect &bounds);
+ void clearScreen();
+ void putxy(int x, int y) { _textPos = Common::Point(x, y); }
+ void drawString(const Common::String &l, int command);
+ int getStringWidth(const Common::String &s);
+ void drawLine(int x, int y, int xx, int yy, int coul);
+ void drawRectangle(int x, int y, int dx, int dy);
+
+ // TODO: Refactor code to remove this method, for increased performance
+ void setPixel(const Common::Point &pt, int palIndex);
+};
+
+} // End of namespace Mortevielle
+
+#endif
diff --git a/engines/mortevielle/menu.cpp b/engines/mortevielle/menu.cpp
new file mode 100644
index 0000000000..99509f8490
--- /dev/null
+++ b/engines/mortevielle/menu.cpp
@@ -0,0 +1,600 @@
+/* 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 re_distribute 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 "common/scummsys.h"
+#include "common/str.h"
+#include "common/textconsole.h"
+#include "mortevielle/menu.h"
+#include "mortevielle/mortevielle.h"
+#include "mortevielle/mouse.h"
+#include "mortevielle/outtext.h"
+
+namespace Mortevielle {
+
+const byte _menuConstants[8][4] = {
+ { 7, 37, 23, 8},
+ {19, 33, 23, 7},
+ {31, 89, 10, 21},
+ {43, 25, 11, 5},
+ {55, 37, 5, 8},
+ {64, 13, 11, 2},
+ {62, 42, 13, 9},
+ {62, 46, 13, 10}
+};
+
+/**
+ * Setup a menu's contents
+ * @remarks Originally called 'menut'
+ */
+void Menu::setText(int menuId, Common::String name) {
+ byte h = hi(menuId);
+ byte l = lo(menuId);
+ Common::String s = name;
+
+ while (s.size() < 22)
+ s += ' ';
+
+ switch (h) {
+ case MENU_INVENTORY:
+ if (l != 7) {
+ _inventoryStringArray[l] = s;
+ _inventoryStringArray[l].insertChar(' ', 0);
+ }
+ break;
+ case MENU_MOVE:
+ _moveStringArray[l] = s;
+ break;
+ case MENU_ACTION:
+ _actionStringArray[l] = s;
+ break;
+ case MENU_SELF:
+ _selfStringArray[l] = s;
+ break;
+ case MENU_DISCUSS:
+ _discussStringArray[l] = s;
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Init destination menu
+ * @remarks Originally called 'tmlieu'
+ */
+void Menu::setDestinationText(int roomId) {
+ Common::String nomp;
+
+ if (roomId == 26)
+ roomId = LANDING;
+
+ int destinationId = 0;
+ for (; (destinationId < 7) && (g_vm->_v_lieu[destinationId][roomId]); ++destinationId) {
+ nomp = g_vm->getString(g_vm->_v_lieu[destinationId][roomId] + kMenuPlaceStringIndex);
+ while (nomp.size() < 20)
+ nomp += ' ';
+ setText(_moveMenu[destinationId + 1], nomp);
+ }
+ nomp = "* ";
+ for (int i = 7; i >= destinationId + 1; --i)
+ setText(_moveMenu[i], nomp);
+}
+
+/**
+ * _disable a menu item
+ * @param menuId Hi byte represents menu number, lo byte reprsents item index
+ */
+void Menu::disableMenuItem(int menuId) {
+ byte h = hi(menuId);
+ byte l = lo(menuId);
+
+ switch (h) {
+ case MENU_INVENTORY:
+ if (l > 6) {
+ _inventoryStringArray[l].setChar('<', 0);
+ _inventoryStringArray[l].setChar('>', 21);
+ } else
+ _inventoryStringArray[l].setChar('*', 0);
+ break;
+ case MENU_MOVE:
+ _moveStringArray[l].setChar('*', 0);
+ break;
+ case MENU_ACTION:
+ _actionStringArray[l].setChar('*', 0);
+ break;
+ case MENU_SELF:
+ _selfStringArray[l].setChar('*', 0);
+ break;
+ case MENU_DISCUSS:
+ _discussStringArray[l].setChar('*', 0);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Enable a menu item
+ * @param menuId Hi byte represents menu number, lo byte reprsents item index
+ * @remarks Originally called menu_enable
+ */
+void Menu::enableMenuItem(int menuId) {
+ byte h = hi(menuId);
+ byte l = lo(menuId);
+
+ switch (h) {
+ case MENU_INVENTORY:
+ _inventoryStringArray[l].setChar(' ', 0);
+ _inventoryStringArray[l].setChar(' ', 21);
+ break;
+ case MENU_MOVE:
+ _moveStringArray[l].setChar(' ', 0);
+ break;
+ case MENU_ACTION:
+ _actionStringArray[l].setChar(' ', 0);
+ break;
+ case MENU_SELF:
+ _selfStringArray[l].setChar(' ', 0);
+ // The original sets two times the same value. Skipped
+ // _selfStringArray[l].setChar(' ', 0);
+ break;
+ case MENU_DISCUSS:
+ _discussStringArray[l].setChar(' ', 0);
+ break;
+ default:
+ break;
+ }
+}
+
+void Menu::displayMenu() {
+ int ind_tabl, k, col;
+
+ int pt, x, y, color, msk, num_letr;
+
+ g_vm->_mouse.hideMouse();
+
+ g_vm->_screenSurface.fillRect(7, Common::Rect(0, 0, 639, 10));
+ col = 28 * g_vm->_res;
+ if (g_vm->_currGraphicalDevice == MODE_CGA)
+ color = 1;
+ else
+ color = 9;
+ num_letr = 0;
+ do { // One character after the other
+ ++num_letr;
+ ind_tabl = 0;
+ y = 1;
+ do { // One column after the other
+ k = 0;
+ x = col;
+ do { // One line after the other
+ msk = 0x80;
+ for (pt = 0; pt <= 7; ++pt) {
+ if ((_charArr[num_letr - 1][ind_tabl] & msk) != 0) {
+ g_vm->_screenSurface.setPixel(Common::Point(x + 1, y + 1), 0);
+ g_vm->_screenSurface.setPixel(Common::Point(x, y + 1), 0);
+ g_vm->_screenSurface.setPixel(Common::Point(x, y), color);
+ }
+ msk = (uint)msk >> 1;
+ ++x;
+ }
+ ++ind_tabl;
+ ++k;
+ } while (k != 3);
+ ++y;
+ } while (y != 9);
+ col += 48 * g_vm->_res;
+ } while (num_letr != 6);
+ g_vm->_mouse.showMouse();
+}
+
+/**
+ * Show the menu
+ */
+void Menu::drawMenu() {
+ displayMenu();
+ _menuActive = true;
+ _msg4 = OPCODE_NONE;
+ _msg3 = OPCODE_NONE;
+ _menuSelected = false;
+ g_vm->setMouseClick(false);
+ _multiTitle = false;
+}
+
+/**
+ * Menu function - Invert a menu entry
+ * @remarks Originally called 'invers'
+ */
+void Menu::invert(int indx) {
+ if (_msg4 == OPCODE_NONE)
+ return;
+
+ int menuIndex = lo(_msg4);
+
+ g_vm->_screenSurface.putxy(_menuConstants[_msg3 - 1][0] << 3, (menuIndex + 1) << 3);
+
+ Common::String str;
+ switch (_msg3) {
+ case 1:
+ str = _inventoryStringArray[menuIndex];
+ break;
+ case 2:
+ str = _moveStringArray[menuIndex];
+ break;
+ case 3:
+ str = _actionStringArray[menuIndex];
+ break;
+ case 4:
+ str = _selfStringArray[menuIndex];
+ break;
+ case 5:
+ str = _discussStringArray[menuIndex];
+ break;
+ case 6:
+ str = g_vm->getEngineString(S_SAVE_LOAD + menuIndex);
+ break;
+ case 7:
+ str = g_vm->getEngineString(S_SAVE_LOAD + 1);
+ str += ' ';
+ str += (char)(48 + menuIndex);
+ break;
+ case 8:
+ if (menuIndex == 1) {
+ str = g_vm->getEngineString(S_RESTART);
+ } else {
+ str = g_vm->getEngineString(S_SAVE_LOAD + 2);
+ str += ' ';
+ str += (char)(47 + menuIndex);
+ }
+ break;
+ default:
+ break;
+ }
+ if ((str[0] != '*') && (str[0] != '<'))
+ g_vm->_screenSurface.drawString(str, indx);
+ else
+ _msg4 = OPCODE_NONE;
+}
+
+void Menu::util(Common::Point pos) {
+
+ int ymx = (_menuConstants[_msg3 - 1][3] << 3) + 16;
+ int dxcar = _menuConstants[_msg3 - 1][2];
+ int xmn = (_menuConstants[_msg3 - 1][0] << 2) * g_vm->_res;
+
+ int ix;
+ if (g_vm->_res == 1)
+ ix = 5;
+ else
+ ix = 3;
+ int xmx = dxcar * ix * g_vm->_res + xmn + 2;
+ if ((pos.x > xmn) && (pos.x < xmx) && (pos.y < ymx) && (pos.y > 15)) {
+ ix = (((uint)pos.y >> 3) - 1) + (_msg3 << 8);
+ if (ix != _msg4) {
+ invert(1);
+ _msg4 = ix;
+ invert(0);
+ }
+ } else if (_msg4 != OPCODE_NONE) {
+ invert(1);
+ _msg4 = OPCODE_NONE;
+ }
+}
+
+/**
+ * Draw a menu
+ */
+void Menu::menuDown(int ii) {
+ int cx, xcc, xco;
+ int lignNumb;
+
+ // Make a copy of the current screen surface for later restore
+ g_vm->_backgroundSurface.copyFrom(g_vm->_screenSurface);
+
+ // Draw the menu
+ xco = _menuConstants[ii - 1][0];
+ lignNumb = _menuConstants[ii - 1][3];
+ g_vm->_mouse.hideMouse();
+ g_vm->sauvecr(10, (_menuConstants[ii - 1][1] + 1) << 1);
+ xco = xco << 3;
+ if (g_vm->_res == 1)
+ cx = 10;
+ else
+ cx = 6;
+ xcc = xco + (_menuConstants[ii - 1][2] * cx) + 6;
+ if ((ii == 4) && (g_vm->getLanguage() == Common::EN_ANY))
+ // Extra width needed for Self menu in English version
+ xcc = 435;
+
+ g_vm->_screenSurface.fillRect(15, Common::Rect(xco, 12, xcc, 10 + (_menuConstants[ii - 1][1] << 1)));
+ g_vm->_screenSurface.fillRect(0, Common::Rect(xcc, 12, xcc + 4, 10 + (_menuConstants[ii - 1][1] << 1)));
+ g_vm->_screenSurface.fillRect(0, Common::Rect(xco, 8 + (_menuConstants[ii - 1][1] << 1), xcc + 4, 12 + (_menuConstants[ii - 1][1] << 1)));
+ g_vm->_screenSurface.putxy(xco, 16);
+ cx = 0;
+ do {
+ ++cx;
+ switch (ii) {
+ case 1:
+ if (_inventoryStringArray[cx][0] != '*')
+ g_vm->_screenSurface.drawString(_inventoryStringArray[cx], 4);
+ break;
+ case 2:
+ if (_moveStringArray[cx][0] != '*')
+ g_vm->_screenSurface.drawString(_moveStringArray[cx], 4);
+ break;
+ case 3:
+ if (_actionStringArray[cx][0] != '*')
+ g_vm->_screenSurface.drawString(_actionStringArray[cx], 4);
+ break;
+ case 4:
+ if (_selfStringArray[cx][0] != '*')
+ g_vm->_screenSurface.drawString(_selfStringArray[cx], 4);
+ break;
+ case 5:
+ if (_discussStringArray[cx][0] != '*')
+ g_vm->_screenSurface.drawString(_discussStringArray[cx], 4);
+ break;
+ case 6:
+ g_vm->_screenSurface.drawString(g_vm->getEngineString(S_SAVE_LOAD + cx), 4);
+ break;
+ case 7: {
+ Common::String s = g_vm->getEngineString(S_SAVE_LOAD + 1);
+ s += ' ';
+ s += (char)(48 + cx);
+ g_vm->_screenSurface.drawString(s, 4);
+ }
+ break;
+ case 8:
+ if (cx == 1)
+ g_vm->_screenSurface.drawString(g_vm->getEngineString(S_RESTART), 4);
+ else {
+ Common::String s = g_vm->getEngineString(S_SAVE_LOAD + 2);
+ s += ' ';
+ s += (char)(47 + cx);
+ g_vm->_screenSurface.drawString(s, 4);
+ }
+ break;
+ default:
+ break;
+ }
+ g_vm->_screenSurface.putxy(xco, g_vm->_screenSurface._textPos.y + 8);
+ } while (cx != lignNumb);
+ _multiTitle = true;
+ g_vm->_mouse.showMouse();
+}
+
+/**
+ * Menu is being removed, so restore the previous background area.
+ */
+void Menu::menuUp(int xx) {
+ if (_multiTitle) {
+ g_vm->charecr(10, (_menuConstants[xx - 1][1] + 1) << 1);
+
+ /* Restore the background area */
+ assert(g_vm->_screenSurface.pitch == g_vm->_backgroundSurface.pitch);
+
+ // Get a pointer to the source and destination of the area to restore
+ const byte *pSrc = (const byte *)g_vm->_backgroundSurface.getBasePtr(0, 10);
+ Graphics::Surface destArea = g_vm->_screenSurface.lockArea(Common::Rect(0, 10, SCREEN_WIDTH, SCREEN_HEIGHT));
+ byte *pDest = (byte *)destArea.getBasePtr(0, 0);
+
+ // Copy the data
+ Common::copy(pSrc, pSrc + (400 - 10) * SCREEN_WIDTH, pDest);
+
+ _multiTitle = false;
+ }
+}
+
+/**
+ * Erase the menu
+ */
+void Menu::eraseMenu() {
+ _menuActive = false;
+ g_vm->setMouseClick(false);
+ menuUp(_msg3);
+}
+
+/**
+ * Handle updates to the menu
+ */
+void Menu::mdn() {
+ if (!_menuActive)
+ return;
+
+ Common::Point curPos = g_vm->_mouse._pos;
+ if (!g_vm->getMouseClick()) {
+ if (curPos == g_vm->_prevPos)
+ return;
+ else
+ g_vm->_prevPos = curPos;
+
+ bool tes = (curPos.y < 11)
+ && ((curPos.x >= (28 * g_vm->_res) && curPos.x <= (28 * g_vm->_res + 24))
+ || (curPos.x >= (76 * g_vm->_res) && curPos.x <= (76 * g_vm->_res + 24))
+ || ((curPos.x > 124 * g_vm->_res) && (curPos.x < 124 * g_vm->_res + 24))
+ || ((curPos.x > 172 * g_vm->_res) && (curPos.x < 172 * g_vm->_res + 24))
+ || ((curPos.x > 220 * g_vm->_res) && (curPos.x < 220 * g_vm->_res + 24))
+ || ((curPos.x > 268 * g_vm->_res) && (curPos.x < 268 * g_vm->_res + 24)));
+ if (tes) {
+ int ix;
+
+ if (curPos.x < 76 * g_vm->_res)
+ ix = MENU_INVENTORY;
+ else if (curPos.x < 124 * g_vm->_res)
+ ix = MENU_MOVE;
+ else if (curPos.x < 172 * g_vm->_res)
+ ix = MENU_ACTION;
+ else if (curPos.x < 220 * g_vm->_res)
+ ix = MENU_SELF;
+ else if (curPos.x < 268 * g_vm->_res)
+ ix = MENU_DISCUSS;
+ else
+ ix = MENU_FILE;
+
+ if ((ix != _msg3) || (!_multiTitle))
+ if (!((ix == MENU_FILE) && ((_msg3 == MENU_SAVE) || (_msg3 == MENU_LOAD)))) {
+ menuUp(_msg3);
+ menuDown(ix);
+ _msg3 = ix;
+ _msg4 = OPCODE_NONE;
+ }
+ } else { // Not in the MenuTitle line
+ if ((curPos.y > 11) && (_multiTitle))
+ util(curPos);
+ }
+ } else { // There was a click
+ if ((_msg3 == MENU_FILE) && (_msg4 != OPCODE_NONE)) {
+ // Another menu to be _displayed
+ g_vm->setMouseClick(false);
+ menuUp(_msg3);
+ if (lo(_msg4) == 1)
+ _msg3 = 7;
+ else
+ _msg3 = 8;
+ menuDown(_msg3);
+
+ g_vm->setMouseClick(false);
+ } else {
+ // A menu was clicked on
+ _menuSelected = (_multiTitle) && (_msg4 != OPCODE_NONE);
+ menuUp(_msg3);
+ g_vm->_msg[4] = _msg4;
+ g_vm->_msg[3] = _msg3;
+ _msg3 = OPCODE_NONE;
+ _msg4 = OPCODE_NONE;
+
+ g_vm->setMouseClick(false);
+ }
+ }
+}
+
+void Menu::initMenu() {
+ int i;
+ Common::File f;
+
+ if (!f.open("menufr.mor"))
+ if (!f.open("menual.mor"))
+ if (!f.open("menu.mor"))
+ error("Missing file - menufr.mor or menual.mor or menu.mor");
+
+ f.read(_charArr, 7 * 24);
+ f.close();
+
+ // Skipped: dialog asking to swap floppy
+
+ for (i = 1; i <= 8; ++i)
+ _inventoryStringArray[i] = "* ";
+ _inventoryStringArray[7] = "< -*-*-*-*-*-*-*-*-*- ";
+ for (i = 1; i <= 7; ++i)
+ _moveStringArray[i] = "* ";
+ i = 1;
+ do {
+ _actionStringArray[i] = g_vm->getString(i + kMenuActionStringIndex);
+
+ while (_actionStringArray[i].size() < 10)
+ _actionStringArray[i] += ' ';
+
+ if (i < 9) {
+ if (i < 6) {
+ _selfStringArray[i] = g_vm->getString(i + kMenuSelfStringIndex);
+ while (_selfStringArray[i].size() < 10)
+ _selfStringArray[i] += ' ';
+ }
+ _discussStringArray[i] = g_vm->getString(i + kMenuSayStringIndex) + ' ';
+ }
+ ++i;
+ } while (i != 22);
+ for (i = 1; i <= 8; ++i) {
+ _discussMenu[i] = 0x500 + i;
+ if (i < 8)
+ _moveMenu[i] = 0x200 + i;
+ _inventoryMenu[i] = 0x100 + i;
+ if (i > 6)
+ disableMenuItem(_inventoryMenu[i]);
+ }
+ _msg3 = OPCODE_NONE;
+ _msg4 = OPCODE_NONE;
+ g_vm->_msg[3] = OPCODE_NONE;
+ g_vm->_msg[4] = OPCODE_NONE;
+ g_vm->setMouseClick(false);
+}
+
+/**
+ * Engine function - Switch action menu to "Search" mode
+ * @remarks Originally called 'mfoudi'
+ */
+void Menu::setSearchMenu() {
+ for (int i = 1; i <= 7; ++i)
+ disableMenuItem(_moveMenu[i]);
+
+ for (int i = 1; i <= 11; ++i)
+ disableMenuItem(_actionMenu[i]);
+
+ setText(OPCODE_SOUND, g_vm->getEngineString(S_SUITE));
+ setText(OPCODE_LIFT, g_vm->getEngineString(S_STOP));
+}
+
+/**
+ * Engine function - Switch action menu from "Search" mode back to normal mode
+ * @remarks Originally called 'mfouen'
+ */
+void Menu::unsetSearchMenu() {
+ setDestinationText(g_vm->_coreVar._currPlace);
+ for (int i = 1; i <= 11; ++i)
+ enableMenuItem(_actionMenu[i]);
+
+ setText(OPCODE_SOUND, g_vm->getEngineString(S_PROBE));
+ setText(OPCODE_LIFT, g_vm->getEngineString(S_RAISE));
+}
+
+/**
+ * Set Inventory menu texts
+ * @remarks Originally called 'modinv'
+ */
+void Menu::setInventoryText() {
+ int r;
+ Common::String nomp;
+
+ int cy = 0;
+ for (int i = 1; i <= 6; ++i) {
+ if (g_vm->_coreVar._sjer[i] != chr(0)) {
+ ++cy;
+ r = (ord(g_vm->_coreVar._sjer[i]) + 400);
+ nomp = g_vm->getString(r - 501 + kInventoryStringIndex);
+ setText(_inventoryMenu[cy], nomp);
+ enableMenuItem(_inventoryMenu[i]);
+ }
+ }
+
+ if (cy < 6) {
+ for (int i = cy + 1; i <= 6; ++i) {
+ setText(_inventoryMenu[i], " ");
+ disableMenuItem(_inventoryMenu[i]);
+ }
+ }
+}
+} // End of namespace Mortevielle
diff --git a/engines/mortevielle/menu.h b/engines/mortevielle/menu.h
new file mode 100644
index 0000000000..03c091909e
--- /dev/null
+++ b/engines/mortevielle/menu.h
@@ -0,0 +1,81 @@
+/* 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
+ */
+
+#ifndef MORTEVIELLE_MENU_H
+#define MORTEVIELLE_MENU_H
+
+#include "common/rect.h"
+#include "common/str.h"
+
+namespace Mortevielle {
+
+enum {
+ MENU_INVENTORY = 1, MENU_MOVE = 2, MENU_ACTION = 3, MENU_SELF = 4,
+ MENU_DISCUSS = 5, MENU_FILE = 6, MENU_SAVE = 7, MENU_LOAD = 8
+};
+
+class Menu {
+private:
+ byte _charArr[7][24];
+ int _msg3;
+ int _msg4;
+
+ void util(Common::Point pos);
+ void invert(int indx);
+ void menuDown(int ii);
+public:
+ bool _menuActive;
+ bool _menuSelected;
+ bool _multiTitle;
+ bool _menuDisplayed;
+ Common::String _inventoryStringArray[9];
+ Common::String _moveStringArray[8];
+ Common::String _actionStringArray[22];
+ Common::String _selfStringArray[7];
+ Common::String _discussStringArray[9];
+ int _discussMenu[9];
+ int _inventoryMenu[9];
+ int _moveMenu[8];
+
+ void setText(int menuId, Common::String name);
+ void setDestinationText(int roomId);
+ void setInventoryText();
+ void disableMenuItem(int menuId);
+ void enableMenuItem(int menuId);
+ void displayMenu();
+ void drawMenu();
+ void menuUp(int xx);
+ void eraseMenu();
+ void mdn();
+ void initMenu();
+
+ void setSearchMenu();
+ void unsetSearchMenu();
+};
+
+} // End of namespace Mortevielle
+#endif
diff --git a/engines/mortevielle/module.mk b/engines/mortevielle/module.mk
new file mode 100644
index 0000000000..38e6e0508d
--- /dev/null
+++ b/engines/mortevielle/module.mk
@@ -0,0 +1,22 @@
+MODULE := engines/mortevielle
+
+MODULE_OBJS := \
+ actions.o \
+ detection.o \
+ dialogs.o \
+ graphics.o \
+ menu.o \
+ mortevielle.o \
+ mouse.o \
+ outtext.o \
+ saveload.o \
+ sound.o \
+ speech.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_MORTEVIELLE), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/mortevielle/mortevielle.cpp b/engines/mortevielle/mortevielle.cpp
new file mode 100644
index 0000000000..9f41292f49
--- /dev/null
+++ b/engines/mortevielle/mortevielle.cpp
@@ -0,0 +1,3790 @@
+/* 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 "common/system.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "engines/util.h"
+#include "engines/engine.h"
+#include "graphics/cursorman.h"
+#include "graphics/palette.h"
+#include "graphics/pixelformat.h"
+#include "mortevielle/mortevielle.h"
+#include "mortevielle/dialogs.h"
+#include "mortevielle/menu.h"
+#include "mortevielle/mouse.h"
+#include "mortevielle/outtext.h"
+#include "mortevielle/saveload.h"
+#include "mortevielle/outtext.h"
+
+namespace Mortevielle {
+
+const byte tabdr[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 tab30[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 tab31[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
+};
+
+MortevielleEngine *g_vm;
+
+MortevielleEngine::MortevielleEngine(OSystem *system, const ADGameDescription *gameDesc):
+ Engine(system), _gameDescription(gameDesc), _randomSource("mortevielle"),
+ _soundManager(_mixer) {
+ g_vm = this;
+ _lastGameFrame = 0;
+ _mouseClick = false;
+ _inMainGameLoop = false;
+ _quitGame = false;
+
+ _roomPresenceLuc = false;
+ _roomPresenceIda = false;
+ _purpleRoomPresenceLeo = false;
+ _roomPresenceGuy = false;
+ _roomPresenceEva = false;
+ _roomPresenceMax = false;
+ _roomPresenceBob = false;
+ _roomPresencePat = false;
+ _toiletsPresenceBobMax = false;
+ _bathRoomPresenceBobMax = false;
+ _room9PresenceLeo = false;
+
+ _soundOff = false;
+ _largestClearScreen = false;
+ _hiddenHero = false;
+ _heroSearching = false;
+ _keyPressedEsc = false;
+ _reloadCFIEC = false;
+
+ _blo = false;
+ _col = false;
+ _syn = false;
+ _obpart = false;
+ _okdes = false;
+ _anyone = false;
+ _brt = false;
+
+ _textColor = 0;
+ _currGraphicalDevice = -1;
+ _newGraphicalDevice = -1;
+ _place = -1;
+
+ _x26KeyCount = -1;
+ _caff = -1;
+ _day = 0;
+
+ memset(_mem, 0, sizeof(_mem));
+ _anyone = false;
+}
+
+MortevielleEngine::~MortevielleEngine() {
+}
+
+/**
+ * Specifies whether the engine supports given features
+ */
+bool MortevielleEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+/**
+ * Return true if a game can currently be loaded
+ */
+bool MortevielleEngine::canLoadGameStateCurrently() {
+ // Saving is only allowed in the main game event loop
+ return _inMainGameLoop;
+}
+
+/**
+ * Return true if a game can currently be saved
+ */
+bool MortevielleEngine::canSaveGameStateCurrently() {
+ // Loading is only allowed in the main game event loop
+ return _inMainGameLoop;
+}
+
+/**
+ * Load in a savegame at the specified slot number
+ */
+Common::Error MortevielleEngine::loadGameState(int slot) {
+ return _savegameManager.loadGame(slot);
+}
+
+/**
+ * Save the current game
+ */
+Common::Error MortevielleEngine::saveGameState(int slot, const Common::String &desc) {
+ if (slot == 0)
+ return Common::kWritingFailed;
+
+ return _savegameManager.saveGame(slot, desc);
+}
+
+/**
+ * Initialise the game state
+ */
+Common::ErrorCode MortevielleEngine::initialise() {
+ // Initialise graphics mode
+ initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, true);
+
+ // Set debug channels
+ DebugMan.addDebugChannel(kMortevielleCore, "core", "Core debugging");
+ DebugMan.addDebugChannel(kMortevielleGraphics, "graphics", "Graphics debugging");
+
+ // Set up an intermediate screen surface
+ _screenSurface.create(SCREEN_WIDTH, SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
+
+ // Set the screen mode
+ _currGraphicalDevice = MODE_EGA;
+ _res = 2;
+
+ _txxFileFl = false;
+ // Load texts from TXX files
+ loadTexts();
+
+ // Load the mort.dat resource
+ Common::ErrorCode result = loadMortDat();
+ if (result != Common::kNoError)
+ return result;
+
+ // Load some error messages (was previously in chartex())
+ _hintPctMessage = getString(580); // You should have noticed %d hints
+
+ // Set default EGA palette
+ _paletteManager.setDefaultPalette();
+
+ // Setup the mouse cursor
+ initMouse();
+
+ _currGraphicalDevice = MODE_EGA;
+ _newGraphicalDevice = _currGraphicalDevice;
+ loadPalette();
+ loadCFIPH();
+ loadCFIEC();
+ decodeNumber(&_cfiecBuffer[161 * 16], (_cfiecBufferSize - (161 * 16)) / 64);
+ _x26KeyCount = 1;
+ init_nbrepm();
+ initMouse();
+
+ loadPlaces();
+ _soundOff = false;
+ _largestClearScreen = false;
+
+ testKeyboard();
+ showConfigScreen();
+ _newGraphicalDevice = _currGraphicalDevice;
+ testKeyboard();
+ if (_newGraphicalDevice != _currGraphicalDevice)
+ _currGraphicalDevice = _newGraphicalDevice;
+ hirs();
+
+ free(_cfiecBuffer);
+ free(_speechManager._cfiphBuffer);
+ return Common::kNoError;
+}
+
+/**
+ * Loads the contents of the Mort.dat data file
+ */
+Common::ErrorCode MortevielleEngine::loadMortDat() {
+ Common::File f;
+
+ // Open the mort.dat file
+ if (!f.open(MORT_DAT)) {
+ GUIErrorMessage("Could not locate Mort.dat file");
+ return Common::kReadingFailed;
+ }
+
+ // Validate the data file header
+ char fileId[4];
+ f.read(fileId, 4);
+ if (strncmp(fileId, "MORT", 4) != 0) {
+ GUIErrorMessage("The located mort.dat data file is invalid");
+ return Common::kReadingFailed;
+ }
+
+ // Check the version
+ if (f.readByte() < MORT_DAT_REQUIRED_VERSION) {
+ GUIErrorMessage("The located mort.dat data file is too a version");
+ return Common::kReadingFailed;
+ }
+ f.readByte(); // Minor version
+
+ // Loop to load resources from the data file
+ while (f.pos() < f.size()) {
+ // Get the Id and size of the next resource
+ char dataType[4];
+ int dataSize;
+ f.read(dataType, 4);
+ dataSize = f.readUint16LE();
+
+ if (!strncmp(dataType, "FONT", 4)) {
+ // Font resource
+ _screenSurface.readFontData(f, dataSize);
+ } else if (!strncmp(dataType, "SSTR", 4)) {
+ readStaticStrings(f, dataSize, kStaticStrings);
+ } else if ((!strncmp(dataType, "GSTR", 4)) && (!_txxFileFl)) {
+ readStaticStrings(f, dataSize, kGameStrings);
+ } else {
+ // Unknown section
+ f.skip(dataSize);
+ }
+ }
+
+ // Close the file
+ f.close();
+
+ assert(_engineStrings.size() > 0);
+ return Common::kNoError;
+}
+
+/**
+ * Read in a static strings block, and if the language matches, load up the static strings
+ */
+void MortevielleEngine::readStaticStrings(Common::File &f, int dataSize, DataType dataType) {
+ // Figure out what language Id is needed
+ byte desiredLanguageId;
+ switch(getLanguage()) {
+ case Common::EN_ANY:
+ desiredLanguageId = LANG_ENGLISH;
+ break;
+ case Common::FR_FRA:
+ desiredLanguageId = LANG_FRENCH;
+ break;
+ case Common::DE_DEU:
+ desiredLanguageId = LANG_GERMAN;
+ break;
+ default:
+ warning("Language not supported, switching to English");
+ desiredLanguageId = LANG_ENGLISH;
+ break;
+ }
+
+ // Read in the language
+ byte languageId = f.readByte();
+ --dataSize;
+
+ // If the language isn't correct, then skip the entire block
+ if (languageId != desiredLanguageId) {
+ f.skip(dataSize);
+ return;
+ }
+
+ // Load in each of the strings
+ while (dataSize > 0) {
+ Common::String s;
+ char ch;
+ while ((ch = (char)f.readByte()) != '\0')
+ s += ch;
+
+ if (dataType == kStaticStrings)
+ _engineStrings.push_back(s);
+ else if (dataType == kGameStrings)
+ _gameStrings.push_back(s);
+
+ dataSize -= s.size() + 1;
+ }
+ assert(dataSize == 0);
+}
+
+/**
+ * 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();
+ }
+
+ // 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() {
+ // If there isn't any pending keypress, wait until there is
+ while (!shouldQuit() && _keypresses.empty()) {
+ 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;
+
+ // Handle alphabetic keys
+ if ((evt.kbd.keycode >= Common::KEYCODE_a) && (evt.kbd.keycode <= Common::KEYCODE_z)) {
+ 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 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
+};
+
+/**
+ * Initialise 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();
+ }
+
+ g_system->delayMillis(10);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+Common::Error MortevielleEngine::run() {
+ // Initialise the game
+ Common::ErrorCode err = initialise();
+ if (err != Common::kNoError)
+ return err;
+
+ // Check for a savegame
+ int loadSlot = 0;
+ if (ConfMan.hasKey("save_slot")) {
+ int gameToLoad = ConfMan.getInt("save_slot");
+ if ((gameToLoad >= 1) && (gameToLoad <= 999))
+ loadSlot = gameToLoad;
+ }
+
+ if (loadSlot == 0)
+ // Show the game introduction
+ showIntroduction();
+
+ // Either load the initial game state savegame, or the specified savegame number
+ adzon();
+ _savegameManager.loadSavegame(loadSlot);
+
+ // Run the main game loop
+ mainGame();
+
+ return Common::kNoError;
+}
+
+/**
+ * Show the game introduction
+ */
+void MortevielleEngine::showIntroduction() {
+ f3f8::aff50(false);
+ _speechManager._mlec = 0;
+ f3f8::checkForF8(142, false);
+ CHECK_QUIT;
+
+ f3f8::ani50();
+ f3f8::checkForF8(143, true);
+ CHECK_QUIT;
+
+ // TODO: Once music is implemented, only use the below delay if music is turned off
+ showTitleScreen();
+ delay(3000);
+ music();
+}
+
+/**
+ * Main game loop. Handles potentially playing the game multiple times, such as if the player
+ * loses, and chooses to start playing the game again.
+ */
+void MortevielleEngine::mainGame() {
+ if (_reloadCFIEC)
+ loadCFIEC();
+
+ for (_crep = 1; _crep <= _x26KeyCount; ++_crep)
+ decodeNumber(&_cfiecBuffer[161 * 16], ((822 * 128) - (161 * 16)) / 64);
+
+ loadBRUIT5();
+ _menu.initMenu();
+
+ charToHour();
+ initGame();
+ hirs();
+ drawRightFrame();
+ _mouse.showMouse();
+
+ // Loop to play the game
+ do {
+ playGame();
+ CHECK_QUIT;
+ } while (!_quitGame);
+}
+
+/**
+ * This method handles playing a loaded game
+ * @remarks Originally called tjouer
+ */
+void MortevielleEngine::playGame() {
+ gameLoaded();
+
+ // Loop handling actions until the game has to be quit, or show the lose or end sequence
+ do {
+ handleAction();
+ CHECK_QUIT;
+ } while (!((_quitGame) || (_endGame) || (_loseGame)));
+
+ if (_endGame)
+ endGame();
+ else if (_loseGame)
+ askRestart();
+}
+
+/**
+ * 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 oo, funct = 0;
+
+ clearScreenType3();
+ oo = false;
+ _controlMenu = 0;
+ if (!_keyPressedEsc) {
+ _menu.drawMenu();
+ _menu._menuDisplayed = true;
+ temps = 0;
+ _key = 0;
+ funct = false;
+ inkey = '.';
+
+ _inMainGameLoop = true;
+ do {
+ _menu.mdn();
+ prepareRoom();
+ _mouse.moveMouse(funct, inkey);
+ CHECK_QUIT;
+ ++temps;
+ } while (!((_menu._menuSelected) || (temps > lim) || (funct) || (_anyone)));
+ _inMainGameLoop = false;
+
+ _menu.eraseMenu();
+ _menu._menuDisplayed = false;
+ if ((inkey == '\1') || (inkey == '\3') || (inkey == '\5') || (inkey == '\7') || (inkey == '\11')) {
+ changeGraphicalDevice((uint)(ord(inkey) - 1) >> 1);
+ return;
+ }
+ if (_menu._menuSelected && (_msg[3] == MENU_SAVE)) {
+ Common::String saveName = Common::String::format("Savegame #%d", _msg[4] & 15);
+ _savegameManager.saveGame(_msg[4] & 15, saveName);
+ }
+ if (_menu._menuSelected && (_msg[3] == MENU_LOAD))
+ _savegameManager.loadGame((_msg[4] & 15) - 1);
+ if (inkey == '\103') { /* F9 */
+ temps = Alert::show(_hintPctMessage, 1);
+ return;
+ } else if (inkey == '\77') {
+ if ((_menuOpcode != OPCODE_NONE) && ((_msg[3] == MENU_ACTION) || (_msg[3] == MENU_SELF))) {
+ _msg[4] = _menuOpcode;
+ ecr3(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) {
+ repon(2, 141);
+ if (_num == 9999)
+ _num = 0;
+ } else {
+ _menuOpcode = _msg[3];
+ if ((_msg[3] == MENU_ACTION) || (_msg[3] == MENU_SELF))
+ _menuOpcode = _msg[4];
+ if (!_anyone) {
+ if ((_heroSearching) || (_obpart)) {
+ if (_mouse._pos.y < 12)
+ return;
+
+ if ((_msg[4] == OPCODE_SOUND) || (_msg[4] == OPCODE_LIFT)) {
+ oo = true;
+ if ((_msg[4] == OPCODE_LIFT) || (_obpart)) {
+ endSearch();
+ _caff = _coreVar._currPlace;
+ _crep = 998;
+ } else
+ tsuiv();
+ mennor();
+ }
+ }
+ }
+ do {
+ if (! oo)
+ handleOpcode();
+
+ if ((_controlMenu == 0) && (! _loseGame) && (! _endGame)) {
+ g_vm->_text.taffich();
+ if (_okdes) {
+ _okdes = false;
+ dessin();
+ }
+ if ((!_syn) || (_col))
+ repon(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)
+ _v_lieu[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, 163, 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 newHour = readclock();
+ int th = _jh + ((newHour - _mh) / _t);
+ 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)
+ _blo = 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 != ROOM9)
+ _room9PresenceLeo = 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]);
+
+ clearScreenType10();
+ 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.mdn();
+
+ 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 cf = 1; cf <= 8; ++cf)
+ _menu.disableMenuItem(_menu._discussMenu[cf]);
+
+ Common::String sYou = getEngineString(S_YOU);
+ Common::String sAre = getEngineString(S_ARE);
+ Common::String sAlone = getEngineString(S_ALONE);
+
+ clearScreenType10();
+ _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
+ _room9PresenceLeo = 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 || _room9PresenceLeo)) ||
+ ((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 == ROOM9) && (_room9PresenceLeo)))
+ 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;
+ _jh = 0;
+ if (!_coreVar._alreadyEnteredManor)
+ _blo = true;
+ _t = kTime1;
+ _mh = readclock();
+}
+
+/**
+ * Engine function - Set Random Presence - Green Room
+ * @remarks Originally called 'pl1'
+ */
+void MortevielleEngine::setRandomPresenceGreenRoom(int cf) {
+ if ( ((_place == GREEN_ROOM) && (!_roomPresenceLuc) && (!_roomPresenceIda))
+ || ((_place == DARKBLUE_ROOM) && (!_roomPresenceGuy) && (!_roomPresenceEva)) ) {
+ int p = getPresenceStatsGreenRoom();
+ int rand;
+ phaz(rand, p, cf);
+
+ if (rand > p)
+ displayAloneText();
+ else
+ setPresenceGreenRoom(_place);
+ }
+}
+
+/**
+ * Engine function - Set Random Presence - Purple Room
+ * @remarks Originally called 'pl2'
+ */
+void MortevielleEngine::setRandomPresencePurpleRoom(int cf) {
+ if (!_purpleRoomPresenceLeo) {
+ int p = getPresenceStatsPurpleRoom();
+ int rand;
+ phaz(rand, p, cf);
+
+ if (rand > p)
+ displayAloneText();
+ else
+ setPresencePurpleRoom();
+ }
+}
+
+/**
+ * Engine function - Set Random Presence - Blue Room
+ * @remarks Originally called 'pl5'
+ */
+void MortevielleEngine::setRandomPresenceBlueRoom(int cf) {
+ if (!_roomPresenceMax) {
+ int p = getPresenceStatsBlueRoom();
+ int rand;
+
+ phaz(rand, p, cf);
+
+ if (rand > p)
+ displayAloneText();
+ else
+ setPresenceBlueRoom();
+ }
+}
+
+/**
+ * Engine function - Set Random Presence - Red Room
+ * @remarks Originally called 'pl6'
+ */
+void MortevielleEngine::setRandomPresenceRedRoom(int cf) {
+ if ( ((_place == RED_ROOM) && (!_roomPresenceBob))
+ || ((_place == GREEN_ROOM2) && (!_roomPresencePat)) ) {
+ int p = getPresenceStatsRedRoom();
+ int rand;
+
+ phaz(rand, p, cf);
+
+ if (rand > p)
+ displayAloneText();
+ else
+ setPresenceRedRoom(_place);
+ }
+}
+
+/**
+ * Engine function - Set Random Presence - Room 9
+ * @remarks Originally called 'pl9'
+ */
+void MortevielleEngine::setRandomPresenceRoom9(int cf) {
+ if (!_room9PresenceLeo) {
+ cf = -10;
+ int p, rand;
+ phaz(rand, p, cf);
+
+ if (rand > p)
+ displayAloneText();
+ else
+ setPresencePurpleRoom();
+ }
+}
+
+/**
+ * Engine function - Set Random Presence - Dining Room
+ * @remarks Originally called 'pl10'
+ */
+void MortevielleEngine::setRandomPresenceDiningRoom(int cf) {
+ int h, rand;
+ int p = getPresenceStatsDiningRoom(h);
+ phaz(rand, p, cf);
+
+ if (rand > p)
+ displayAloneText();
+ else
+ setPresenceDiningRoom(h);
+}
+
+/**
+ * Engine function - Set Random Presence - Bureau
+ * @remarks Originally called 'pl11'
+ */
+void MortevielleEngine::setRandomPresenceBureau(int cf) {
+ int h, rand;
+
+ int p = getPresenceStatsBureau(h);
+ phaz(rand, p, cf);
+ if (rand > p)
+ displayAloneText();
+ else
+ setPresenceBureau(h);
+}
+
+/**
+ * Engine function - Set Random Presence - Kitchen
+ * @remarks Originally called 'pl12'
+ */
+void MortevielleEngine::setRandomPresenceKitchen(int cf) {
+ int p, rand;
+
+ p = getPresenceStatsKitchen();
+ phaz(rand, p, cf);
+ if (rand > p)
+ displayAloneText();
+ else
+ setPresenceKitchen();
+}
+
+/**
+ * Engine function - Set Random Presence - Attic / Cellar
+ * @remarks Originally called 'pl13'
+ */
+void MortevielleEngine::setRandomPresenceAttic(int cf) {
+ int p, rand;
+
+ p = getPresenceStatsAttic();
+ phaz(rand, p, cf);
+ if (rand > p)
+ displayAloneText();
+ else
+ setPresenceKitchen();
+}
+
+/**
+ * Engine function - Set Random Presence - Landing
+ * @remarks Originally called 'pl15'
+ */
+void MortevielleEngine::setRandomPresenceLanding(int cf) {
+ int p, rand;
+
+ p = getPresenceStatsLanding();
+ phaz(rand, p, cf);
+ if (rand > p)
+ displayAloneText();
+ else
+ setPresenceLanding();
+}
+
+/**
+ * Engine function - Set Random Presence - Chapel
+ * @remarks Originally called 'pl20'
+ */
+void MortevielleEngine::setRandomPresenceChapel(int cf) {
+ int h, rand;
+
+ int p = getPresenceStatsChapel(h);
+ phaz(rand, p, cf);
+ if (rand > 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 ((_prebru == 0) && (!_coreVar._alreadyEnteredManor)) {
+ // Type 1: Speech
+ _speechManager.startSpeech(10, 1, 1);
+ ++_prebru;
+ } else {
+ if (((_coreVar._currPlace == MOUNTAIN) || (_coreVar._currPlace == MANOR_FRONT) || (_coreVar._currPlace == MANOR_BACK)) && (getRandomNumber(1, 3) == 2))
+ // Type 1: Speech
+ _speechManager.startSpeech(9, getRandomNumber(2, 4), 1);
+ else if ((_coreVar._currPlace == CHAPEL) && (getRandomNumber(1, 2) == 1))
+ // Type 1: Speech
+ _speechManager.startSpeech(8, 1, 1);
+ else if ((_coreVar._currPlace == WELL) && (getRandomNumber(1, 2) == 2))
+ // Type 1: Speech
+ _speechManager.startSpeech(12, 1, 1);
+ else if (_coreVar._currPlace == INSIDE_WELL)
+ // Type 1: Speech
+ _speechManager.startSpeech(13, 1, 1);
+ else
+ // Type 2 : music
+ _speechManager.startSpeech(getRandomNumber(1, 17), 1, 2);
+ }
+}
+
+/**
+ * Engine function - You lose!
+ * @remarks Originally called 'tperd'
+ */
+void MortevielleEngine::loseGame() {
+ initouv();
+ _roomDoorId = OWN_ROOM;
+ _openObjCount = 0;
+ _mchai = 0;
+ _menu.unsetSearchMenu();
+ if (!_blo)
+ getPresence(MANOR_FRONT);
+
+ _loseGame = true;
+ clearScreenType1();
+ _screenSurface.drawBox(60, 35, 400, 50, 15);
+ repon(9, _crep);
+ clearScreenType2();
+ clearScreenType3();
+ _col = false;
+ _syn = false;
+ _okdes = 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 || (ord(_coreVar._sjer[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;
+ affrep();
+}
+
+/**
+ * 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, 24, 5);
+ f3f8::draw();
+
+ key = 0;
+ do {
+ _speechManager.startSpeech(rep, haut[_caff - 69], 0);
+ key = f3f8::waitForF3F8();
+ CHECK_QUIT;
+ } while (key != 66);
+ hirs();
+ _mouse.showMouse();
+}
+
+/**
+ * Engine function - End of Search: reset globals
+ * @remarks Originally called 'finfouill'
+ */
+void MortevielleEngine::endSearch() {
+ _heroSearching = false;
+ _obpart = false;
+ _cs = 0;
+ _is = 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;
+ afdes();
+ _screenSurface.drawBox(223, 47, 155, 91, 15);
+ repon(2, 33);
+ testKey(false);
+ mennor();
+ _mouse.hideMouse();
+ hirs();
+ premtet();
+ startDialog(140);
+ drawRightFrame();
+ drawClock();
+ _mouse.showMouse();
+ _coreVar._currPlace = OWN_ROOM;
+ affrep();
+ resetPresenceInRooms(DINING_ROOM);
+ if (!_blo)
+ 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 {
+ _okdes = true;
+ _coreVar._currPlace = MOUNTAIN;
+ affrep();
+ }
+}
+
+/**
+ * Engine function - Go to Manor front
+ * @remarks Originally called 't1deva'
+ */
+void MortevielleEngine::gotoManorFront() {
+ _manorDistance = 0;
+ _coreVar._currPlace = MANOR_FRONT;
+ affrep();
+}
+
+/**
+ * Engine function - Go to Manor back
+ * @remarks Originally called 't1derr'
+ */
+void MortevielleEngine::gotoManorBack() {
+ _coreVar._currPlace = MANOR_BACK;
+ affrep();
+}
+
+/**
+ * Engine function - Dead : Flooded in Well
+ * @remarks Originally called 't1deau'
+ */
+void MortevielleEngine::floodedInWell() {
+ _crep = 1503;
+ loseGame();
+}
+
+/**
+ * Engine function - Change Graphical Device
+ * @remarks Originally called 'change_gd'
+ */
+void MortevielleEngine::changeGraphicalDevice(int newDevice) {
+ _mouse.hideMouse();
+ _currGraphicalDevice = newDevice;
+ hirs();
+ _mouse.initMouse();
+ _mouse.showMouse();
+ drawRightFrame();
+ prepareRoom();
+ drawClock();
+ if (_currBitIndex != 0)
+ showPeoplePresent(_currBitIndex);
+ else
+ displayAloneText();
+ clearScreenType2();
+ clearScreenType3();
+ _maff = 68;
+ afdes();
+ repon(2, _crep);
+ _menu.displayMenu();
+}
+
+/**
+ * Called when a savegame has been loaded.
+ * @remarks Originally called 'antegame'
+ */
+void MortevielleEngine::gameLoaded() {
+ _mouse.hideMouse();
+ _menu._menuDisplayed = false;
+ _loseGame = true;
+ _anyone = false;
+ _okdes = true;
+ _col = false;
+ _hiddenHero = false;
+ _brt = false;
+ _maff = 68;
+ _menuOpcode = OPCODE_NONE;
+ _prebru = 0;
+ _x = 0;
+ _y = 0;
+ _num = 0;
+ _startHour = 0;
+ _endHour = 0;
+ _cs = 0;
+ _is = 0;
+ _roomDoorId = OWN_ROOM;
+ _syn = true;
+ _heroSearching = true;
+ _mchai = 0;
+ _manorDistance = 0;
+ initouv();
+ _openObjCount = 0;
+ _takeObjCount = 0;
+ affrep();
+ _hintPctMessage = getString(580);
+
+ _okdes = false;
+ _endGame = true;
+ _loseGame = false;
+ _heroSearching = false;
+
+ displayAloneText();
+ prepareRoom();
+ drawClock();
+ afdes();
+ repon(2, _crep);
+ clearScreenType3();
+ _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)
+ clearScreenType2();
+ _syn = false;
+ _keyPressedEsc = false;
+ if (!_anyone) {
+ if (_brt) {
+ if ((_msg[3] == MENU_MOVE) || (_msg[4] == OPCODE_LEAVE) || (_msg[4] == OPCODE_SLEEP) || (_msg[4] == OPCODE_EAT)) {
+ _controlMenu = 4;
+ mennor();
+ return;
+ }
+ }
+ if (_msg[3] == MENU_MOVE)
+ fctMove();
+ if (_msg[3] == MENU_DISCUSS)
+ fctDiscuss();
+ if (_msg[3] == MENU_INVENTORY)
+ fctInventoryTake();
+ if (_msg[4] == OPCODE_ATTACH)
+ fctAttach();
+ if (_msg[4] == OPCODE_WAIT)
+ fctWait();
+ if (_msg[4] == OPCODE_FORCE)
+ fctForce();
+ if (_msg[4] == OPCODE_SLEEP)
+ fctSleep();
+ if (_msg[4] == OPCODE_LISTEN)
+ fctListen();
+ if (_msg[4] == OPCODE_ENTER)
+ fctEnter();
+ if (_msg[4] == OPCODE_CLOSE)
+ fctClose();
+ if (_msg[4] == OPCODE_SEARCH)
+ fctSearch();
+ if (_msg[4] == OPCODE_KNOCK)
+ fctKnock();
+ if (_msg[4] == OPCODE_SCRATCH)
+ fctScratch();
+ if (_msg[4] == OPCODE_READ)
+ fctRead();
+ if (_msg[4] == OPCODE_EAT)
+ fctEat();
+ if (_msg[4] == OPCODE_PLACE)
+ fctPlace();
+ if (_msg[4] == OPCODE_OPEN)
+ fctOpen();
+ if (_msg[4] == OPCODE_TAKE)
+ fctTake();
+ if (_msg[4] == OPCODE_LOOK)
+ fctLook();
+ if (_msg[4] == OPCODE_SMELL)
+ fctSmell();
+ if (_msg[4] == OPCODE_SOUND)
+ fctSound();
+ if (_msg[4] == OPCODE_LEAVE)
+ fctLeave();
+ if (_msg[4] == OPCODE_LIFT)
+ fctLift();
+ if (_msg[4] == OPCODE_TURN)
+ fctTurn();
+ if (_msg[4] == OPCODE_SSEARCH)
+ fctSelfSearch();
+ if (_msg[4] == OPCODE_SREAD)
+ fctSelfRead();
+ if (_msg[4] == OPCODE_SPUT)
+ fctSelfPut();
+ if (_msg[4] == OPCODE_SLOOK)
+ fctSelftLook();
+ _hiddenHero = false;
+
+ if (_msg[4] == OPCODE_SHIDE)
+ fctSelfHide();
+ } else {
+ if (_anyone) {
+ interactNPC();
+ _anyone = false;
+ mennor();
+ return;
+ }
+ }
+ int hour, day, minute;
+ updateHour(day, hour, minute);
+ if ((((hour == 12) || (hour == 13) || (hour == 19)) && (_coreVar._currPlace != 10)) ||
+ ((hour > 0) && (hour < 6) && (_coreVar._currPlace != 0)))
+ ++_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();
+ }
+ if ((_coreVar._faithScore > 99) && (hour > 0) && (hour < 9)) {
+ _crep = 1508;
+ loseGame();
+ }
+ if ((day > 1) && (hour > 8) && (!_loseGame)) {
+ _crep = 1502;
+ loseGame();
+ }
+ }
+ mennor();
+}
+
+/**
+ * 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 = chr(minute);
+}
+
+/**
+ * Engine function - extract time from a char
+ * @remarks Originally called 'theure'
+ */
+void MortevielleEngine::charToHour() {
+ int fullHour = ord(_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 Screen - Type 1
+ * @remarks Originally called 'clsf1'
+ */
+void MortevielleEngine::clearScreenType1() {
+ _mouse.hideMouse();
+ _screenSurface.fillRect(0, Common::Rect(0, 11, 514, 175));
+ _mouse.showMouse();
+}
+
+/**
+ * Engine function - Clear Screen - Type 2
+ * @remarks Originally called 'clsf2'
+ */
+void MortevielleEngine::clearScreenType2() {
+ _mouse.hideMouse();
+ if (_largestClearScreen) {
+ _screenSurface.fillRect(0, Common::Rect(1, 176, 633, 199));
+ _screenSurface.drawBox(0, 175, 634, 24, 15);
+ _largestClearScreen = false;
+ } else {
+ _screenSurface.fillRect(0, Common::Rect(1, 176, 633, 190));
+ _screenSurface.drawBox(0, 175, 634, 15, 15);
+ }
+ _mouse.showMouse();
+}
+
+/**
+ * Engine function - Clear Screen - Type 3
+ * @remarks Originally called 'clsf3'
+ */
+void MortevielleEngine::clearScreenType3() {
+ _mouse.hideMouse();
+ _screenSurface.fillRect(0, Common::Rect(1, 192, 633, 199));
+ _screenSurface.drawBox(0, 191, 634, 8, 15);
+ _mouse.showMouse();
+}
+
+/**
+ * Engine function - Clear Screen - Type 10
+ * @remarks Originally called 'clsf10'
+ */
+void MortevielleEngine::clearScreenType10() {
+ int co, cod;
+ Common::String st;
+
+ _mouse.hideMouse();
+ if (_res == 1) {
+ co = 634;
+ cod = 534;
+ } else {
+ co = 600;
+ cod = 544;
+ }
+ _screenSurface.fillRect(15, Common::Rect(cod, 93, co, 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);
+
+ co = 580 - (_screenSurface.getStringWidth(st) / 2);
+ _screenSurface.putxy(co, 92);
+ _screenSurface.drawString(st, 4);
+
+ _screenSurface.fillRect(15, Common::Rect(560, 24, 610, 86));
+ /* rempli(69,12,32,5,255);*/
+ _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() {
+ Alert::show(getEngineString(S_USE_DEP_MENU), 1);
+}
+
+/**
+ * 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;
+ }
+ }
+}
+
+void MortevielleEngine::cinq_huit(char &c, int &idx, byte &pt, bool &the_end) {
+ uint16 oct, ocd;
+
+ /* 5-8 */
+ oct = _inpBuffer[idx];
+ oct = ((uint16)(oct << (16 - pt))) >> (16 - pt);
+ if (pt < 6) {
+ ++idx;
+ oct = oct << (5 - pt);
+ pt += 11;
+ oct = oct | ((uint)_inpBuffer[idx] >> pt);
+ } else {
+ pt -= 5;
+ oct = (uint)oct >> pt;
+ }
+
+ switch (oct) {
+ case 11:
+ c = '$';
+ the_end = true;
+ break;
+ case 30:
+ case 31:
+ ocd = _inpBuffer[idx];
+ ocd = (uint16)(ocd << (16 - pt)) >> (16 - pt);
+ if (pt < 6) {
+ ++idx;
+ ocd = ocd << (5 - pt);
+ pt += 11;
+ ocd = ocd | ((uint)_inpBuffer[idx] >> pt);
+ } else {
+ pt -= 5;
+ ocd = (uint)ocd >> pt;
+ }
+
+ if (oct == 30)
+ c = chr(tab30[ocd]);
+ else
+ c = chr(tab31[ocd]);
+
+ if (c == '\0') {
+ the_end = true;
+ c = '#';
+ }
+ break;
+ default:
+ c = chr(tabdr[oct]);
+ break;
+ }
+}
+
+/**
+ * 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("deline: num < 0! Skipping");
+ } else if (!_txxFileFl) {
+ wrkStr = getGameString(num);
+ } else {
+ int hint = _ntpBuffer[num]._hintId;
+ byte point = _ntpBuffer[num]._point;
+ int length = 0;
+ bool endFl = false;
+ char let;
+ do {
+ cinq_huit(let, hint, point, endFl);
+ wrkStr += let;
+ ++length;
+ } while (!endFl);
+ }
+
+ while (wrkStr.lastChar() == '$')
+ // Remove trailing '$'s
+ wrkStr.deleteLastChar();
+
+ return wrkStr;
+}
+
+void MortevielleEngine::copcha() {
+ int i = kAcha;
+ do {
+ _tabdon[i] = _tabdon[i + 390];
+ ++i;
+ } while (i != kAcha + 390);
+}
+
+/**
+ * Engine function - When restarting the game, reset the main variables used by the engine
+ * @remarks Originally called 'inzon'
+ */
+void MortevielleEngine::resetVariables() {
+ copcha();
+
+ _coreVar._alreadyEnteredManor = false;
+ _coreVar._selectedObjectId = 0;
+ _coreVar._cellarObjectId = 0;
+ _coreVar._atticBallHoleObjectId = 0;
+ _coreVar._atticRodHoleObjectId = 0;
+ _coreVar._wellObjectId = 0;
+ _coreVar._secretPassageObjectId = 0;
+ _coreVar._purpleRoomObjectId = 136;
+ _coreVar._cryptObjectId = 141;
+ _coreVar._faithScore = getRandomNumber(4, 10);
+ _coreVar._currPlace = MANOR_FRONT;
+
+ for (int i = 2; i <= 6; ++i)
+ _coreVar._sjer[i] = chr(0);
+
+ _coreVar._sjer[1] = chr(113);
+ _coreVar._fullHour = chr(20);
+
+ for (int i = 1; i <= 10; ++i)
+ _coreVar._pourc[i] = ' ';
+
+ for (int i = 1; i <= 6; ++i)
+ _coreVar._teauto[i] = '*';
+
+ for (int i = 7; i <= 9; ++i)
+ _coreVar._teauto[i] = ' ';
+
+ for (int i = 10; i <= 28; ++i)
+ _coreVar._teauto[i] = '*';
+
+ for (int i = 29; i <= 42; ++i)
+ _coreVar._teauto[i] = ' ';
+
+ _coreVar._teauto[33] = '*';
+
+ for (int i = 1; i <= 8; ++i)
+ _nbrep[i] = 0;
+
+ init_nbrepm();
+}
+
+/**
+ * Engine function - Set the palette
+ * @remarks Originally called 'writepal'
+ */
+void MortevielleEngine::setPal(int n) {
+ switch (_currGraphicalDevice) {
+ case MODE_TANDY:
+ case MODE_EGA:
+ case MODE_AMSTRAD1512:
+ for (int i = 1; i <= 16; ++i) {
+ _mem[(0x7000 * 16) + (2 * i)] = _stdPal[n][i].x;
+ _mem[(0x7000 * 16) + (2 * i) + 1] = _stdPal[n][i].y;
+ }
+ break;
+ case MODE_CGA: {
+ nhom pal[16];
+ for (int i = 0; i < 16; ++i) {
+ pal[i] = _cgaPal[n]._a[i];
+ }
+
+ if (n < 89)
+ palette(_cgaPal[n]._p);
+
+ for (int i = 0; i <= 15; ++i)
+ displayCGAPattern(i, _patternArr[pal[i]._id], pal);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Engine function - Display a CGA pattern, using a specified palette
+ * @remarks Originally called 'outbloc'
+ */
+void MortevielleEngine::displayCGAPattern(int n, Pattern p, nhom *pal) {
+ int addr = n * 404 + 0xd700;
+
+ WRITE_LE_UINT16(&_mem[0x6000 * 16 + addr], p._tax);
+ WRITE_LE_UINT16(&_mem[0x6000 * 16 + addr + 2], p._tay);
+ addr += 4;
+ for (int i = 0; i < p._tax; ++i) {
+ for (int j = 0; j < p._tay; ++j)
+ _mem[(0x6000 * 16) + addr + j * p._tax + i] = pal[n]._hom[p._des[i + 1][j + 1]];
+ }
+}
+
+/**
+ * Engine function - Load Palette from File
+ * @remarks Originally called 'charpal'
+ */
+void MortevielleEngine::loadPalette() {
+ Common::File f;
+ byte b;
+
+ 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)
+ _fxxBuffer[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");
+
+ for (int j = 0; j <= 90; ++j) {
+ _cgaPal[j]._p = f.readByte();
+ for (int i = 0; i <= 15; ++i) {
+ nhom &with = _cgaPal[j]._a[i];
+
+ b = f.readByte();
+ with._id = (uint)b >> 4;
+ with._hom[0] = ((uint)b >> 2) & 3;
+ with._hom[1] = b & 3;
+ }
+ }
+
+ _cgaPal[10]._a[9] = _cgaPal[10]._a[5];
+ for (int j = 0; j <= 14; ++j) {
+ _patternArr[j]._tax = f.readByte();
+ _patternArr[j]._tay = f.readByte();
+ for (int i = 1; i <= 20; ++i) {
+ for (int k = 1; k <= 20; ++k)
+ _patternArr[j]._des[i][k] = f.readByte();
+ }
+ }
+ f.close();
+}
+
+/**
+ * Engine function - Load Texts from File
+ * @remarks Originally called 'chartex'
+ */
+void MortevielleEngine::loadTexts() {
+ Common::File inpFile;
+ Common::File ntpFile;
+
+ _txxFileFl = false;
+ if (getLanguage() == Common::EN_ANY) {
+ warning("English version expected - Switching to 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")) {
+ warning("Missing file - TXX.INP or .MOR - Switching to DAT file");
+ return;
+ }
+
+ if ((inpFile.size() > (kMaxTi * 2)) || (ntpFile.size() > (kMaxTd * 3))) {
+ warning("TXX file - Unexpected format - Switching to DAT file");
+ return;
+ }
+
+ for (int i = 0; i < inpFile.size() / 2; ++i)
+ _inpBuffer[i] = inpFile.readUint16LE();
+
+ inpFile.close();
+ _txxFileFl = true;
+
+ for (int i = 0; i < (ntpFile.size() / 3); ++i) {
+ _ntpBuffer[i]._hintId = ntpFile.readSint16LE();
+ _ntpBuffer[i]._point = ntpFile.readByte();
+ }
+
+ ntpFile.close();
+
+}
+
+void MortevielleEngine::loadBRUIT5() {
+ Common::File f;
+
+ if (!f.open("bruit5"))
+ error("Missing file - bruit5");
+
+ f.read(&_mem[kAdrNoise5 * 16 + 0], 149 * 128);
+ f.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();
+
+ 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");
+ }
+
+ _speechManager._cfiphBuffer = (int16 *)malloc(sizeof(int16) * (f.size() / 2));
+
+ for (int i = 0; i < (f.size() / 2); ++i)
+ _speechManager._cfiphBuffer[i] = f.readSint16LE();
+
+ f.close();
+}
+
+/**
+ * Engine function - Play Music
+ * @remarks Originally called 'music'
+ */
+void MortevielleEngine::music() {
+ if (_soundOff)
+ return;
+
+ _reloadCFIEC = true;
+
+ Common::File fic;
+ if (!fic.open("mort.img"))
+ error("Missing file - mort.img");
+
+ fic.read(&_mem[0x3800 * 16 + 0], 500);
+ fic.read(&_mem[0x47a0 * 16 + 0], 123);
+ fic.close();
+
+ _soundManager.decodeMusic(&_mem[0x3800 * 16], &_mem[0x5000 * 16], 623);
+ _addFix = (float)((kTempoMusic - 8)) / 256;
+ _speechManager.cctable(_speechManager._tbi);
+
+ bool fin = false;
+ int k = 0;
+ do {
+ fin = keyPressed();
+ _soundManager.musyc(_speechManager._tbi, 9958, kTempoMusic);
+ ++k;
+ fin = fin | keyPressed() | (k >= 5);
+ } while (!fin);
+ while (keyPressed())
+ getChar();
+}
+
+/**
+ * Engine function - Show title screen
+ * @remarks Originally called 'suite'
+ */
+void MortevielleEngine::showTitleScreen() {
+ hirs();
+ repon(7, 2035);
+ _caff = 51;
+ _text.taffich();
+ testKeyboard();
+ if (_newGraphicalDevice != _currGraphicalDevice)
+ _currGraphicalDevice = _newGraphicalDevice;
+ hirs();
+ draw(kAdrDes, 0, 0);
+
+ Common::String cpr = "COPYRIGHT 1989 : LANKHOR";
+ _screenSurface.putxy(104 + 72 * _res, 185);
+ _screenSurface.drawString(cpr, 0);
+}
+
+/**
+ * Draw picture
+ * @remarks Originally called 'dessine'
+ */
+void MortevielleEngine::draw(int ad, int x, int y) {
+ _mouse.hideMouse();
+ setPal(_numpal);
+ pictout(ad, 0, x, y);
+ _mouse.showMouse();
+}
+
+/**
+ * Draw right frame
+ * @remarks Originally called 'dessine_rouleau'
+ */
+void MortevielleEngine::drawRightFrame() {
+ setPal(89);
+ if (_currGraphicalDevice == MODE_HERCULES) {
+ _mem[0x7000 * 16 + 14] = 15;
+ }
+ _mouse.hideMouse();
+ pictout(0x73a2, 0, 0, 0);
+ _mouse.showMouse();
+}
+
+/**
+ * Read the current system time
+ */
+int MortevielleEngine::readclock() {
+ TimeDate dateTime;
+ g_system->getTimeAndDate(dateTime);
+
+ int m = dateTime.tm_min * 60;
+ int h = dateTime.tm_hour * 3600;
+ return h + m + dateTime.tm_sec;
+}
+
+/**
+ * Engine function - Prepare room and hint string
+ * @remarks Originally called 'tinke'
+ */
+void MortevielleEngine::prepareRoom() {
+ Common::String d1 = getEngineString(S_SHOULD_HAVE_NOTICED);
+ Common::String d2 = getEngineString(S_NUMBER_OF_HINTS);
+ const char d3 = '[';
+ const char d4 = ']';
+ const char d5 = '1';
+ Common::String d6 = getEngineString(S_OK);
+ int cf, day, hour, minute;
+ Common::String stpo;
+
+ _anyone = false;
+ updateHour(day, hour, minute);
+ if (day != _day) {
+ _day = day;
+ int i = 0;
+ do {
+ ++i;
+ if (_nbrepm[i] != 0)
+ --_nbrepm[i];
+ _nbrep[i] = 0;
+ } while (i != 8);
+ }
+ if ((hour > _hour) || ((hour == 0) && (_hour == 23))) {
+ _hour = hour;
+ _minute = 0;
+ drawClock();
+ cf = 0;
+ for (int i = 1; i <= 10; ++i) {
+ if (_coreVar._pourc[i] == '*')
+ ++cf;
+ }
+
+ if (cf == 10)
+ stpo = "10";
+ else
+ stpo = chr(cf + 48);
+
+ _hintPctMessage = Common::String(d3);
+ _hintPctMessage += d5;
+ _hintPctMessage += d4;
+ _hintPctMessage += d3;
+ _hintPctMessage += d1;
+ _hintPctMessage += stpo;
+ _hintPctMessage += '0';
+ _hintPctMessage += d2;
+ _hintPctMessage += d4;
+ _hintPctMessage += d3;
+ _hintPctMessage += d6;
+ _hintPctMessage += d4;
+ }
+ if (minute > _minute) {
+ _minute = 30;
+ drawClock();
+ }
+ if (_mouse._pos.y < 12)
+ return;
+
+ if (!_blo) {
+ if ((hour == 12) || ((hour > 18) && (hour < 21)) || ((hour >= 0) && (hour < 7)))
+ _t = kTime2;
+ else
+ _t = kTime1;
+ cf = _coreVar._faithScore;
+ if ((cf > 33) && (cf < 66))
+ _t -= (_t / 3);
+
+ if (cf > 65)
+ _t -= ((_t / 3) * 2);
+
+ int nh = readclock();
+ if ((nh - _mh) > _t) {
+ bool activeMenu = _menu._menuActive;
+ _menu.eraseMenu();
+ _jh += ((nh - _mh) / _t);
+ _mh = nh;
+ switch (_place) {
+ case GREEN_ROOM:
+ case DARKBLUE_ROOM:
+ setRandomPresenceGreenRoom(cf);
+ break;
+ case PURPLE_ROOM:
+ setRandomPresencePurpleRoom(cf);
+ break;
+ case BLUE_ROOM:
+ setRandomPresenceBlueRoom(cf);
+ break;
+ case RED_ROOM:
+ case GREEN_ROOM2:
+ setRandomPresenceRedRoom(cf);
+ break;
+ case ROOM9:
+ setRandomPresenceRoom9(cf);
+ break;
+ case DINING_ROOM:
+ setRandomPresenceDiningRoom(cf);
+ break;
+ case BUREAU:
+ setRandomPresenceBureau(cf);
+ break;
+ case KITCHEN:
+ setRandomPresenceKitchen(cf);
+ break;
+ case ATTIC:
+ case CELLAR:
+ setRandomPresenceAttic(cf);
+ break;
+ case LANDING:
+ case ROOM26:
+ setRandomPresenceLanding(cf);
+ break;
+ case CHAPEL:
+ setRandomPresenceChapel(cf);
+ 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 (!_brt) {
+ _brt = true;
+ _startHour = readclock();
+ if (getRandomNumber(1, 5) < 5) {
+ clearScreenType3();
+ prepareScreenType2();
+ ecr3(getEngineString(S_HEAR_NOISE));
+ int rand = (getRandomNumber(0, 4)) - 2;
+ _speechManager.startSpeech(1, rand, 1);
+ clearScreenType3();
+ }
+ }
+ }
+ }
+
+ if (activeMenu)
+ _menu.drawMenu();
+ }
+ }
+ _endHour = readclock();
+ if ((_brt) && ((_endHour - _startHour) > 17)) {
+ getPresenceBitIndex(_place);
+ _brt = false;
+ _startHour = 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;
+ int h, co;
+
+ _mouse.hideMouse();
+
+ _screenSurface.drawRectangle(570, 118, 20, 10);
+ _screenSurface.drawRectangle(578, 114, 6, 18);
+ if ((_currGraphicalDevice == MODE_CGA) || (_currGraphicalDevice == MODE_HERCULES))
+ co = 0;
+ else
+ co = 1;
+
+ if (_minute == 0)
+ _screenSurface.drawLine(((uint)x >> 1) * _res, y, ((uint)x >> 1) * _res, (y - rg), co);
+ else
+ _screenSurface.drawLine(((uint)x >> 1) * _res, y, ((uint)x >> 1) * _res, (y + rg), co);
+
+ h = _hour;
+ if (h > 12)
+ h -= 12;
+ if (h == 0)
+ h = 12;
+
+ _screenSurface.drawLine(((uint)x >> 1) * _res, y, ((uint)(x + cv[0][h - 1]) >> 1) * _res, y + cv[1][h - 1], co);
+ _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) {
+ // Copy the substring into a temporary buffer
+ char *tmp = new char[size + 1];
+ strncpy(tmp, s.c_str() + idx - 1, size);
+ tmp[size] = '\0';
+
+ Common::String result(tmp);
+ delete[] tmp;
+ return result;
+}
+
+void MortevielleEngine::hirs() {
+ // Note: The original used this to set the graphics mode and clear the screen, both at
+ // the start of the game, and whenever the screen need to be cleared. As such, this
+ // method is deprecated in favour of clearing the screen
+ debugC(1, kMortevielleCore, "TODO: hirs is deprecated in favour of ScreenSurface::clearScreen");
+
+ if (_currGraphicalDevice == MODE_TANDY) {
+ _screenSurface.fillRect(0, Common::Rect(0, 0, 639, 200));
+ _res = 1;
+ } else if (_currGraphicalDevice == MODE_CGA) {
+ palette(1);
+ _res = 1;
+ } else
+ _res = 2;
+
+ _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);
+ clearScreenType3();
+ prepareScreenType2();
+ ecr3(getEngineString(S_SOMEONE_ENTERS));
+ int rand = (getRandomNumber(0, 4)) - 2;
+ _speechManager.startSpeech(2, rand, 1);
+
+ // The original was doing here a useless loop.
+ // It has been removed
+
+ clearScreenType3();
+ displayAloneText();
+}
+
+/**
+ * Display control menu string
+ * @remarks Originally called 'tctrm'
+ */
+void MortevielleEngine::displayControlMenu() {
+ repon(2, (3000 + _controlMenu));
+ _controlMenu = 0;
+}
+
+void MortevielleEngine::pictout(int seg, int dep, int x, int y) {
+ GfxSurface surface;
+ surface.decode(&_mem[seg * 16 + dep]);
+
+ if (_currGraphicalDevice == MODE_HERCULES) {
+ _mem[0x7000 * 16 + 2] = 0;
+ _mem[0x7000 * 16 + 32] = 15;
+ }
+
+ if ((_caff != 51) && (READ_LE_UINT16(&_mem[0x7000 * 16 + 0x4138]) > 0x100))
+ WRITE_LE_UINT16(&_mem[0x7000 * 16 + 0x4138], 0x100);
+
+ _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], 1 * 1916);
+ f.close();
+
+ if (!f.open("dec.mor"))
+ error("Missing file - dec.mor");
+
+ f.read(&_mem[0x73a2 * 16 + 0], 1 * 1664);
+ f.close();
+}
+
+/**
+ * Returns the offset within the compressed image data resource of the desired image
+ */
+int MortevielleEngine::animof(int ouf, int num) {
+ int nani = _mem[kAdrAni * 16 + 1];
+ int aux = num;
+ if (ouf != 1)
+ aux += nani;
+
+ int animof_result = (nani << 2) + 2 + READ_BE_UINT16(&_mem[kAdrAni * 16 + (aux << 1)]);
+
+ return animof_result;
+}
+
+void MortevielleEngine::text1(int x, int y, int nb, int m) {
+ int co;
+
+ if (_res == 1)
+ co = 10;
+ else
+ co = 6;
+ Common::String tmpStr = getString(m);
+ if ((y == 182) && ((int) tmpStr.size() * co > nb * 6))
+ y = 176;
+ _text.displayStr(tmpStr, x, y, nb, 20, _textColor);
+}
+
+void MortevielleEngine::repon(int f, int m) {
+ if ((m > 499) && (m < 563)) {
+ Common::String tmpStr = getString(m - 501 + kInventoryStringIndex);
+
+ if ((int) tmpStr.size() > ((58 + (_res - 1) * 37) << 1))
+ _largestClearScreen = true;
+ else
+ _largestClearScreen = false;
+
+ clearScreenType2();
+ _text.displayStr(tmpStr, 8, 176, 85, 3, 5);
+ } else {
+ modif(m);
+ switch (f) {
+ case 2:
+ case 8:
+ clearScreenType2();
+ prepareScreenType2();
+ text1(8, 182, 103, m);
+ if ((m == 68) || (m == 69))
+ _coreVar._teauto[40] = '*';
+ if ((m == 104) && (_caff == 14)) {
+ _coreVar._teauto[36] = '*';
+ if (_coreVar._teauto[39] == '*') {
+ _coreVar._pourc[3] = '*';
+ _coreVar._teauto[38] = '*';
+ }
+ }
+ break;
+ case 1:
+ case 6:
+ case 9: {
+ int i;
+ if ((f == 1) || (f == 6))
+ i = 4;
+ else
+ i = 5;
+
+ Common::String tmpStr = getString(m);
+ _text.displayStr(tmpStr, 80, 40, 60, 25, i);
+
+ if (m == 180)
+ _coreVar._pourc[6] = '*';
+ else if (m == 179)
+ _coreVar._pourc[10] = '*';
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void MortevielleEngine::modif(int &nu) {
+ if (nu == 26)
+ nu = 25;
+ else if ((nu > 29) && (nu < 36))
+ nu -= 4;
+ else if ((nu > 69) && (nu < 78))
+ nu -= 37;
+ else if ((nu > 99) && (nu < 194))
+ nu -= 59;
+ else if ((nu > 996) && (nu < 1000))
+ nu -= 862;
+ else if ((nu > 1500) && (nu < 1507))
+ nu -= 1363;
+ else if ((nu > 1507) && (nu < 1513))
+ nu -= 1364;
+ else if ((nu > 1999) && (nu < 2002))
+ nu -= 1851;
+ else if (nu == 2010)
+ nu = 151;
+ else if ((nu > 2011) && (nu < 2025))
+ nu -= 1860;
+ else if (nu == 2026)
+ nu = 165;
+ else if ((nu > 2029) && (nu < 2037))
+ nu -= 1864;
+ else if ((nu > 3000) && (nu < 3005))
+ nu -= 2828;
+ else if (nu == 4100)
+ nu = 177;
+ else if (nu == 4150)
+ nu = 178;
+ else if ((nu > 4151) && (nu < 4156))
+ nu -= 3973;
+ else if (nu == 4157)
+ nu = 183;
+ else if ((nu == 4160) || (nu == 4161))
+ nu -= 3976;
+}
+
+void MortevielleEngine::initouv() {
+ for (int cx = 1; cx <= 7; ++cx)
+ _touv[cx] = chr(0);
+}
+
+void MortevielleEngine::ecr2(Common::String text) {
+ // Some dead code was present in the original: removed
+ _screenSurface.putxy(8, 177);
+ int tlig = 59 + (_res - 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;
+ clearScreenType2();
+ _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::ecr3(Common::String text) {
+ clearScreenType3();
+ _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 cf, 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) && (!_room9PresenceLeo))
+ 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 == ROOM9) && (!_room9PresenceLeo) && (!_purpleRoomPresenceLeo))
+ retVal = 10;
+ if ( ((roomId == PURPLE_ROOM) && (_room9PresenceLeo))
+ || ((roomId == ROOM9) && (_purpleRoomPresenceLeo)))
+ retVal = -400;
+ if (retVal != -500) {
+ retVal += cf;
+ 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 == ROOM9)
+ _room9PresenceLeo = true;
+}
+
+void MortevielleEngine::init_nbrepm() {
+ static const byte ipm[9] = { 0, 4, 5, 6, 7, 5, 6, 5, 8 };
+
+ for (int idx = 0; idx < 9; ++idx)
+ _nbrepm[idx] = ipm[idx];
+}
+
+void MortevielleEngine::phaz(int &rand, int &p, int cf) {
+ p += cf;
+ rand = getRandomNumber(1, 100);
+}
+
+/**
+ * Get Presence
+ * @remarks Originally called 't11'
+ */
+int MortevielleEngine::getPresence(int roomId) {
+ int retVal = 0;
+ int rand;
+
+ int p = getPresenceStats(rand, _coreVar._faithScore, roomId);
+ _place = roomId;
+ if ((roomId > OWN_ROOM) && (roomId < DINING_ROOM)) {
+ if (p != -500) {
+ if (rand > p) {
+ displayAloneText();
+ retVal = 0;
+ } else {
+ setPresenceFlags(_place);
+ retVal = getPresenceBitIndex(_place);
+ }
+ } else
+ retVal = getPresenceBitIndex(_place);
+ }
+
+ if (roomId > ROOM9) {
+ if ((roomId > LANDING) && (roomId != CHAPEL) && (roomId != ROOM26))
+ displayAloneText();
+ else {
+ int h = 0;
+ if (roomId == DINING_ROOM)
+ p = getPresenceStatsDiningRoom(h);
+ else if (roomId == BUREAU)
+ p = getPresenceStatsBureau(h);
+ else if (roomId == KITCHEN)
+ p = getPresenceStatsKitchen();
+ else if ((roomId == ATTIC) || (roomId == CELLAR))
+ p = getPresenceStatsAttic();
+ else if ((roomId == LANDING) || (roomId == ROOM26))
+ p = getPresenceStatsLanding();
+ else if (roomId == CHAPEL)
+ p = getPresenceStatsChapel(h);
+ p += _coreVar._faithScore;
+ rand = getRandomNumber(1, 100);
+ if (rand > p) {
+ displayAloneText();
+ retVal = 0;
+ } else {
+ if (roomId == DINING_ROOM)
+ p = setPresenceDiningRoom(h);
+ else if (roomId == BUREAU)
+ p = setPresenceBureau(h);
+ else if ((roomId == KITCHEN) || (roomId == ATTIC) || (roomId == CELLAR))
+ p = setPresenceKitchen();
+ else if ((roomId == LANDING) || (roomId == ROOM26))
+ p = setPresenceLanding();
+ else if (roomId == CHAPEL)
+ p = setPresenceChapel(h);
+ retVal = p;
+ }
+ }
+ }
+
+ return retVal;
+}
+
+void MortevielleEngine::writetp(Common::String s, int t) {
+ if (_res == 2)
+ _screenSurface.drawString(s, t);
+ else
+ _screenSurface.drawString(copy(s, 1, 25), t);
+}
+
+void MortevielleEngine::aniof(int ouf, int num) {
+ if ((_caff == 7) && ((num == 4) || (num == 5)))
+ return;
+
+ if ((_caff == 10) && (num == 7))
+ num = 6;
+ else if (_caff == 12) {
+ if (num == 3)
+ num = 4;
+ else if (num == 4)
+ num = 3;
+ }
+
+ int ad = kAdrAni;
+ int offset = animof(ouf, num);
+
+ GfxSurface surface;
+ surface.decode(&_mem[ad * 16 + offset]);
+ _screenSurface.drawPicture(surface, 0, 12);
+
+ prepareScreenType1();
+}
+
+void MortevielleEngine::dessin() {
+ clearScreenType1();
+ if (_caff > 99) {
+ draw(kAdrDes, 60, 33);
+ _screenSurface.drawBox(118, 32, 291, 121, 15); // Medium box
+ } else if (_caff > 69) {
+ draw(kAdrDes, 112, 48); // Heads
+ _screenSurface.drawBox(222, 47, 155, 91, 15);
+ } else {
+ draw(kAdrDes, 0, 12);
+ prepareScreenType1();
+ if ((_caff < 30) || (_caff > 32)) {
+ for (int cx = 1; cx <= 6; ++cx) {
+ if (ord(_touv[cx]) != 0)
+ aniof(1, ord(_touv[cx]));
+ }
+
+ if (_caff == 13) {
+ if (_coreVar._atticBallHoleObjectId == 141)
+ aniof(1, 7);
+
+ if (_coreVar._atticRodHoleObjectId == 159)
+ aniof(1, 6);
+ } else if ((_caff == 14) && (_coreVar._cellarObjectId == 151))
+ aniof(1, 2);
+ else if ((_caff == 17) && (_coreVar._secretPassageObjectId == 143))
+ aniof(1, 1);
+ else if ((_caff == 24) && (_coreVar._wellObjectId != 0))
+ aniof(1, 1);
+ }
+
+ if (_caff < ROOM26)
+ startMusicOrSpeech(1);
+ }
+}
+
+void MortevielleEngine::afdes() {
+ _text.taffich();
+ dessin();
+ _okdes = false;
+}
+
+/**
+ * Engine function - Place
+ * @remarks Originally called 'tkey1'
+ */
+void MortevielleEngine::testKey(bool d) {
+ bool quest = false;
+ int x, y, c;
+
+ _mouse.hideMouse();
+ fenat('K');
+
+ // Wait for release from any key or mouse button
+ while (keyPressed())
+ _key = testou();
+
+ do {
+ _mouse.getMousePosition(x, y, c);
+ keyPressed();
+ } while (c != 0);
+
+ // Event loop
+ do {
+ if (d)
+ prepareRoom();
+ quest = keyPressed();
+ _mouse.getMousePosition(x, y, c);
+ CHECK_QUIT;
+ } while (!(quest || (c != 0) || (d && _anyone)));
+ if (quest)
+ testou();
+ setMouseClick(false);
+ _mouse.showMouse();
+}
+
+void MortevielleEngine::tlu(int af, int ob) {
+ _caff = 32;
+ afdes();
+ repon(6, ob + 4000);
+ repon(2, 999);
+ testKey(true);
+ _caff = af;
+ _msg[3] = OPCODE_NONE;
+ _crep = 998;
+}
+
+void MortevielleEngine::affrep() {
+ _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);
+ }
+
+ for (int cx = 1; cx <= 7; ++cx)
+ _touv[cx] = chr(0);
+ _roomDoorId = OWN_ROOM;
+ _openObjCount = 0;
+ _mchai = 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 :
+ tlu(_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);
+ }
+}
+
+void MortevielleEngine::mennor() {
+ _menu.menuUp(_msg[3]);
+}
+
+void MortevielleEngine::premtet() {
+ draw(kAdrDes, 10, 80);
+ _screenSurface.drawBox(18, 79, 155, 91, 15);
+}
+
+void MortevielleEngine::ajchai() {
+ int cy = kAcha + ((_mchai - 1) * 10) - 1;
+ int cx = 0;
+ do {
+ ++cx;
+ } while ((cx <= 9) && (_tabdon[cy + cx] != 0));
+
+ if (_tabdon[cy + cx] == 0)
+ _tabdon[cy + cx] = _coreVar._selectedObjectId;
+ else
+ _crep = 192;
+}
+
+void MortevielleEngine::ajjer(int ob) {
+ int cx = 0;
+ do {
+ ++cx;
+ } while ((cx <= 5) && (ord(_coreVar._sjer[cx]) != 0));
+
+ if (ord(_coreVar._sjer[cx]) == 0) {
+ _coreVar._sjer[(cx)] = chr(ob);
+ _menu.setInventoryText();
+ } else
+ _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;
+ repon(2, _crep);
+ if (_crep == 138)
+ _speechManager.startSpeech(5, 2, 1);
+ else
+ _speechManager.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 cx = convertBitIndexToCharacterIndex(_currBitIndex);
+ _caff = 69 + cx;
+ _crep = _caff;
+ _msg[3] = MENU_DISCUSS;
+ _msg[4] = _menu._discussMenu[cx];
+ _syn = true;
+ _col = true;
+ } else {
+ if (getRandomNumber(1, 3) == 2) {
+ _hiddenHero = false;
+ _crep = 137;
+ goto L1;
+ } else {
+ repon(2, 136);
+ int rand = (getRandomNumber(0, 4)) - 2;
+ _speechManager.startSpeech(3, rand, 1);
+ clearScreenType2();
+ displayAloneText();
+ resetRoomVariables(MANOR_FRONT);
+ affrep();
+ }
+ }
+ if (_menu._menuDisplayed)
+ _menu.drawMenu();
+}
+
+void MortevielleEngine::tsuiv() {
+ int tbcl;
+ int cy = kAcha + ((_mchai - 1) * 10) - 1;
+ int cx = 0;
+ do {
+ ++cx;
+ ++_cs;
+ int cl = cy + _cs;
+ tbcl = _tabdon[cl];
+ } while ((tbcl == 0) && (_cs <= 9));
+
+ if ((tbcl != 0) && (_cs < 11)) { // 2nd check useless as _cs is <= 10
+ ++_is;
+ _caff = tbcl;
+ _crep = _caff + 400;
+ if (_currBitIndex != 0)
+ _coreVar._faithScore += 2;
+ } else {
+ affrep();
+ endSearch();
+ if (cx > 9)
+ _crep = 131;
+ }
+}
+
+void MortevielleEngine::tfleche() {
+ bool qust;
+ char touch;
+
+ if (_num == 9999)
+ return;
+
+ fenat(chr(152));
+ bool inRect = false;
+ do {
+ touch = '\0';
+
+ do {
+ _mouse.moveMouse(qust, touch);
+ CHECK_QUIT;
+
+ if (getMouseClick())
+ inRect = (_mouse._pos.x < 256 * _res) && (_mouse._pos.y < 176) && (_mouse._pos.y > 12);
+ prepareRoom();
+ } while (!(qust || inRect || _anyone));
+
+ if (qust && (touch == '\103'))
+ Alert::show(_hintPctMessage, 1);
+ } 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] * _res;
+ sy = _tabdon[(a + cb + 1)];
+ cb += 2;
+ ix = _tabdon[a + cb] * _res;
+ 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;
+}
+
+void MortevielleEngine::treg(int objId) {
+ int mdes = _caff;
+ _caff = objId;
+
+ if (((_caff > 29) && (_caff < 33)) || (_caff == 144) || (_caff == 147) || (_caff == 149) || (_msg[4] == OPCODE_SLOOK)) {
+ afdes();
+ if ((_caff > 29) && (_caff < 33))
+ repon(2, _caff);
+ else
+ repon(2, _caff + 400);
+ testKey(true);
+ _caff = mdes;
+ _msg[3] = 0;
+ _crep = 998;
+ } else {
+ _obpart = true;
+ _crep = _caff + 400;
+ _menu.setSearchMenu();
+ }
+}
+
+void MortevielleEngine::avpoing(int &objId) {
+ _crep = 999;
+ if (_coreVar._selectedObjectId != 0)
+ ajjer(_coreVar._selectedObjectId);
+
+ if (_crep != 139) {
+ displayItemInHand(objId + 400);
+ _coreVar._selectedObjectId = objId;
+ objId = 0;
+ }
+}
+
+void MortevielleEngine::rechai(int &ch) {
+ int tmpPlace = _coreVar._currPlace;
+
+ if (_coreVar._currPlace == CRYPT)
+ tmpPlace = CELLAR;
+ ch = _tabdon[kAchai + (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;
+}
+
+void MortevielleEngine::fenat(char ans) {
+ int coul;
+
+ _mouse.hideMouse();
+ if (_currGraphicalDevice == MODE_CGA)
+ coul = 2;
+ else if (_currGraphicalDevice == MODE_HERCULES)
+ coul = 1;
+ else
+ coul = 12;
+
+ _screenSurface.writeCharacter(Common::Point(306, 193), ord(ans), coul);
+ _screenSurface.drawBox(300, 191, 16, 8, 15);
+ _mouse.showMouse();
+}
+
+/**
+ * Test Keyboard
+ * @remarks Originally called 'teskbd'
+ */
+void MortevielleEngine::testKeyboard() {
+ if (keyPressed())
+ testou();
+}
+
+int MortevielleEngine::testou() {
+ 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 ord(ch);
+}
+
+void MortevielleEngine::sauvecr(int y, int dy) {
+// _mouse.hideMouse();
+// _mouse.showMouse();
+}
+
+void MortevielleEngine::charecr(int y, int dy) {
+// _mouse.hideMouse();
+// _mouse.showMouse();
+}
+
+} // End of namespace Mortevielle
diff --git a/engines/mortevielle/mortevielle.h b/engines/mortevielle/mortevielle.h
new file mode 100644
index 0000000000..ec7e86f1fa
--- /dev/null
+++ b/engines/mortevielle/mortevielle.h
@@ -0,0 +1,540 @@
+/* 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
+ */
+
+#ifndef MORTEVIELLE_H
+#define MORTEVIELLE_H
+
+#include "common/events.h"
+#include "common/file.h"
+#include "common/random.h"
+#include "common/rect.h"
+#include "common/stack.h"
+#include "engines/advancedDetector.h"
+#include "engines/engine.h"
+#include "common/error.h"
+#include "graphics/surface.h"
+#include "mortevielle/graphics.h"
+#include "mortevielle/menu.h"
+#include "mortevielle/mouse.h"
+#include "mortevielle/saveload.h"
+#include "mortevielle/sound.h"
+#include "mortevielle/speech.h"
+#include "mortevielle/outtext.h"
+
+namespace Mortevielle {
+
+/*---------------------------------------------------------------------------*/
+/*------------------- MEMORY MAP ------------------------*/
+/*---------------------------------------------------------------------------*/
+/* The following is a list of physical addresses in memory currently used
+ * by the game.
+ *
+ * Address
+ * -------
+ * 5000:0 - Music data
+ * 6000:0 - Decompressed current image
+ * 7000:0+ - Compressed images
+ * 7000:2 - 16 words representing palette map
+ * 7000:4138 - width, height, x/y offset of decoded image
+ */
+
+#define ord(v) ((int) v)
+#define chr(v) ((unsigned char) v)
+#define lo(v) ((v) & 0xff)
+#define hi(v) (((v) >> 8) & 0xff)
+#define swap(v) (((lo(v)) << 8) | ((hi(v)) >> 8))
+#define odd(v) (((v) % 2) == 1)
+
+// Debug channels
+enum {
+ kMortevielleCore = 1 << 0,
+ kMortevielleGraphics = 1 << 1
+};
+
+// Game languages
+enum {
+ LANG_FRENCH = 0,
+ LANG_ENGLISH = 1,
+ LANG_GERMAN = 2
+};
+
+// Static string list
+enum {
+ S_YES_NO = 0, S_GO_TO = 1, S_SOMEONE_ENTERS = 2, S_COOL = 3, S_LOURDE = 4,
+ S_MALSAINE = 5, S_IDEM = 6, S_YOU = 7, S_ARE = 8, S_ALONE = 9,
+ S_HEAR_NOISE = 10, S_SHOULD_HAVE_NOTICED = 11, S_NUMBER_OF_HINTS = 12,
+ S_WANT_TO_WAKE_UP = 13, S_OK = 14, S_SAVE_LOAD = 15, S_RESTART = 18, S_F3 = 19,
+ S_F8 = 20, S_HIDE_SELF = 21, S_TAKE = 22, S_PROBE = 23, S_RAISE = 24, S_SUITE = 25,
+ S_STOP = 26, S_USE_DEP_MENU = 27, S_LIFT = 28, S_READ = 29,
+ S_LOOK = 30, S_SEARCH = 31, S_OPEN = 32, S_PUT = 33, S_TURN = 34, S_TIE = 35, S_CLOSE = 36,
+ S_HIT = 37, S_POSE = 38, S_SMASH = 39,
+
+ S_SMELL = 40, S_SCRATCH = 41, S_PROBE2 = 42, S_BEFORE_USE_DEP_MENU = 43, S_DAY = 44
+};
+
+enum DataType {
+ kStaticStrings = 0,
+ kGameStrings = 1
+};
+
+#define SCREEN_WIDTH 640
+#define SCREEN_HEIGHT 400
+#define SCREEN_ORIG_HEIGHT 200
+#define MORT_DAT_REQUIRED_VERSION 1
+#define MORT_DAT "mort.dat"
+#define GAME_FRAME_DELAY (1000 / 50)
+
+const int kTime1 = 410;
+const int kTime2 = 250;
+
+const int kAcha = 492;
+const int kAdrDes = 0x7000;
+const int kFleche = 1758;
+
+const int kAsoul = 154;
+const int kAouvr = 282;
+const int kAchai = 387;
+const int kArcf = 1272;
+const int kArep = 1314;
+const int kAmzon = 1650;
+const int kArega = 0;
+
+const int kMaxTi = 7975;
+const int kMaxTd = 600;
+
+const int kDescriptionStringIndex = 0; // Unused
+const int kInventoryStringIndex = 186;
+const int kQuestionStringIndex = 247;
+const int kDialogStringIndex = 292;
+const int kMenuPlaceStringIndex = 435;
+const int kMenuActionStringIndex = 476;
+const int kMenuSelfStringIndex = 497;
+const int kMenuSayStringIndex = 502;
+const int kSecretPassageQuestionStringIndex = 510; // Unusued?
+const int kMaxPatt = 20;
+
+const int OPCODE_NONE = 0;
+enum verbs {OPCODE_ATTACH = 0x301, OPCODE_WAIT = 0x302, OPCODE_FORCE = 0x303, OPCODE_SLEEP = 0x304, OPCODE_LISTEN = 0x305,
+ OPCODE_ENTER = 0x306, OPCODE_CLOSE = 0x307, OPCODE_SEARCH = 0x308, OPCODE_KNOCK = 0x309, OPCODE_SCRATCH = 0x30a,
+ OPCODE_READ = 0x30b, OPCODE_EAT = 0x30c, OPCODE_PLACE = 0x30d, OPCODE_OPEN = 0x30e, OPCODE_TAKE = 0x30f,
+ OPCODE_LOOK = 0x310, OPCODE_SMELL = 0x311, OPCODE_SOUND = 0x312, OPCODE_LEAVE = 0x313, OPCODE_LIFT = 0x314,
+ OPCODE_TURN = 0x315, OPCODE_SHIDE = 0x401, OPCODE_SSEARCH = 0x402, OPCODE_SREAD = 0x403, OPCODE_SPUT = 0x404,
+ OPCODE_SLOOK = 0x405};
+
+static const int _actionMenu[12] = { OPCODE_NONE,
+ OPCODE_SHIDE, OPCODE_ATTACH, OPCODE_FORCE, OPCODE_SLEEP,
+ OPCODE_ENTER, OPCODE_CLOSE, OPCODE_KNOCK, OPCODE_EAT,
+ OPCODE_PLACE, OPCODE_OPEN, OPCODE_LEAVE
+};
+
+/*
+9 "A glance at the forbidden$",
+18 "It's already open$",
+26 "A photograph$",
+27 "The coat of arms$",
+*/
+enum Places {
+ OWN_ROOM = 0, GREEN_ROOM = 1, PURPLE_ROOM = 2, TOILETS = 3, DARKBLUE_ROOM = 4,
+ BLUE_ROOM = 5, RED_ROOM = 6, BATHROOM = 7, GREEN_ROOM2 = 8, ROOM9 = 9,
+ DINING_ROOM = 10, BUREAU = 11, KITCHEN = 12, ATTIC = 13, CELLAR = 14,
+ LANDING = 15, CRYPT = 16, SECRET_PASSAGE = 17, ROOM18 = 18, MOUNTAIN = 19,
+ CHAPEL = 20, MANOR_FRONT = 21, MANOR_BACK = 22, INSIDE_WELL = 23, WELL = 24,
+ DOOR = 25, ROOM26 = 26, ROOM27 = 27
+};
+
+enum GraphicModes { MODE_AMSTRAD1512 = 0, MODE_CGA = 1, MODE_EGA = 2, MODE_HERCULES = 3, MODE_TANDY = 4 };
+
+struct nhom {
+ byte _id; /* number between 0 and 32 */
+ byte _hom[4];
+};
+
+struct CgaPalette {
+ byte _p;
+ nhom _a[16];
+};
+
+struct Pattern {
+ byte _tay, _tax;
+ byte _des[kMaxPatt + 1][kMaxPatt + 1];
+};
+
+struct SaveStruct {
+ int _faithScore;
+ byte _pourc[11];
+ byte _teauto[43];
+ byte _sjer[31];
+ int _currPlace;
+ int _atticBallHoleObjectId;
+ int _atticRodHoleObjectId;
+ int _cellarObjectId;
+ int _secretPassageObjectId;
+ int _wellObjectId;
+ int _selectedObjectId;
+ int _purpleRoomObjectId;
+ int _cryptObjectId;
+ bool _alreadyEnteredManor;
+ byte _fullHour;
+};
+
+struct Hint {
+ int _hintId;
+ byte _point;
+};
+
+class MortevielleEngine : public Engine {
+private:
+ const ADGameDescription *_gameDescription;
+ Common::Stack<int> _keypresses;
+ uint32 _lastGameFrame;
+ Common::Point _mousePos;
+ Common::StringArray _engineStrings;
+ Common::StringArray _gameStrings;
+
+ Pattern _patternArr[15];
+ int _menuOpcode;
+
+ bool _mouseClick;
+ bool _inMainGameLoop; // Flag when the main game loop is active
+ bool _quitGame; // Quit game flag. Originally called 'arret'
+ bool _endGame; // End game flag. Originally called 'solu'
+ bool _loseGame; // Lose game flag. Originally called 'perdu'
+ bool _txxFileFl; // Flag used to determine if texts are from the original files or from a DAT file
+ bool _roomPresenceLuc;
+ bool _roomPresenceIda;
+ bool _purpleRoomPresenceLeo;
+ bool _roomPresenceGuy;
+ bool _roomPresenceEva;
+ bool _roomPresenceMax;
+ bool _roomPresenceBob;
+ bool _roomPresencePat;
+ bool _toiletsPresenceBobMax;
+ bool _bathRoomPresenceBobMax;
+ bool _room9PresenceLeo;
+ bool _hiddenHero;
+ bool _heroSearching;
+ bool _keyPressedEsc;
+ bool _reloadCFIEC;
+ bool _col;
+ bool _syn;
+ bool _obpart;
+ bool _anyone;
+ bool _brt;
+
+ int _textColor;
+ int _place;
+ int _manorDistance;
+ int _currBitIndex;
+ int _currDay;
+ int _currHour;
+ int _currHalfHour;
+ int _day;
+ int _hour;
+ int _minute;
+ int _mchai;
+ int _controlMenu;
+ int _startHour;
+ int _endHour;
+ Common::Point _stdPal[91][17];
+ CgaPalette _cgaPal[91];
+
+ int _x26KeyCount;
+ int _roomDoorId;
+ int _openObjCount;
+ int _takeObjCount;
+ int _num;
+ int _cs;
+ int _prebru;
+ int _t;
+ int _x;
+ int _y;
+ int _jh;
+ int _mh;
+
+
+ Common::String _hintPctMessage;
+ byte *_cfiecBuffer;
+ int _cfiecBufferSize;
+ byte _touv[8];
+ int _nbrep[9];
+ int _nbrepm[9];
+ uint16 _inpBuffer[kMaxTi + 1];
+ Hint _ntpBuffer[kMaxTd + 1];
+
+ Common::ErrorCode initialise();
+ Common::ErrorCode loadMortDat();
+ void readStaticStrings(Common::File &f, int dataSize, DataType dataType);
+ void loadFont(Common::File &f);
+ bool handleEvents();
+ void addKeypress(Common::Event &evt);
+ void initMouse();
+ void showIntroduction();
+ void mainGame();
+ void playGame();
+ void handleAction();
+ void displayCGAPattern(int n, Pattern p, nhom *pal);
+ void loadPalette();
+ void loadTexts();
+ void loadBRUIT5();
+ void loadCFIEC();
+ void loadCFIPH();
+ void showTitleScreen();
+ int readclock();
+ void palette(int v1);
+ int checkLeoMaxRandomPresence();
+ void interactNPC();
+ void initCaveOrCellar();
+ void displayControlMenu();
+ void displayItemInHand(int objId);
+ void resetRoomVariables(int roomId);
+ int getPresenceStats(int &rand, int cf, int roomId);
+ void setPresenceFlags(int roomId);
+ void testKey(bool d);
+ void exitRoom();
+ void getReadDescription(int objId);
+ void getSearchDescription(int objId);
+ int checkLeaveSecretPassage();
+ void changeGraphicalDevice(int newDevice);
+ void startDialog(int16 rep);
+ void endSearch();
+ int convertCharacterIndexToBitIndex(int characterIndex);
+ int convertBitIndexToCharacterIndex(int bitIndex);
+ void clearScreenType1();
+ void clearScreenType2();
+ void clearScreenType3();
+ void clearScreenType10();
+ int getRandomNumber(int minval, int maxval);
+ void showMoveMenuAlert();
+ void showConfigScreen();
+ void decodeNumber(byte *pStart, int count);
+ void resetVariables();
+ void music();
+ void drawRightFrame();
+ void prepareRoom();
+ void drawClock();
+ void checkManorDistance();
+ void gotoManorFront();
+ void gotoManorBack();
+ void gotoDiningRoom();
+ bool checkInventory(int objectId);
+ void loseGame();
+ void floodedInWell();
+ void displayDiningRoom();
+ void startMusicOrSpeech(int so);
+ void setTextColor(int col);
+ void prepareScreenType1();
+ void prepareScreenType2();
+ void prepareScreenType3();
+ void updateHour(int &day, int &hour, int &minute);
+ void getKnockAnswer();
+ int getPresenceStatsGreenRoom();
+ int getPresenceStatsPurpleRoom();
+ int getPresenceStatsToilets();
+ int getPresenceStatsBlueRoom();
+ int getPresenceStatsRedRoom();
+ int getPresenceStatsDiningRoom(int &hour);
+ int getPresenceStatsBureau(int &hour);
+ int getPresenceStatsKitchen();
+ int getPresenceStatsAttic();
+ int getPresenceStatsLanding();
+ int getPresenceStatsChapel(int &hour);
+ int getPresenceBitIndex(int roomId);
+ void setPresenceGreenRoom(int roomId);
+ void setPresencePurpleRoom();
+ void setPresenceBlueRoom();
+ void setPresenceRedRoom(int roomId);
+ int setPresenceDiningRoom(int hour);
+ int setPresenceBureau(int hour);
+ int setPresenceKitchen();
+ int setPresenceLanding();
+ int setPresenceChapel(int hour);
+ void setRandomPresenceGreenRoom(int cf);
+ void setRandomPresencePurpleRoom(int cf);
+ void setRandomPresenceBlueRoom(int cf);
+ void setRandomPresenceRedRoom(int cf);
+ void setRandomPresenceRoom9(int cf);
+ void setRandomPresenceDiningRoom(int cf);
+ void setRandomPresenceBureau(int cf);
+ void setRandomPresenceKitchen(int cf);
+ void setRandomPresenceAttic(int cf);
+ void setRandomPresenceLanding(int cf);
+ void setRandomPresenceChapel(int cf);
+ void loadPlaces();
+ void resetPresenceInRooms(int roomId);
+ void showPeoplePresent(int bitIndex);
+ int selectCharacters(int min, int max);
+ void fctMove();
+ void fctTake();
+ void fctInventoryTake();
+ void fctLift();
+ void fctRead();
+ void fctSelfRead();
+ void fctLook();
+ void fctSelftLook();
+ void fctSearch();
+ void fctSelfSearch();
+ void fctOpen();
+ void fctPlace();
+ void fctTurn();
+ void fctSelfHide();
+ void fctAttach();
+ void fctClose();
+ void fctKnock();
+ void fctSelfPut();
+ void fctListen();
+ void fctEat();
+ void fctEnter();
+ void fctSleep();
+ void fctForce();
+ void fctLeave();
+ void fctWait();
+ void fctSound();
+ void fctDiscuss();
+ void fctSmell();
+ void fctScratch();
+ void endGame();
+ void askRestart();
+ void delay(int amount);
+ void handleOpcode();
+
+ void cinq_huit(char &c, int &idx, byte &pt, bool &the_end);
+ void copcha();
+ void adzon();
+ void text1(int x, int y, int nb, int m);
+ void modif(int &nu);
+ void initouv();
+ void phaz(int &rand, int &p, int cf);
+ void writetp(Common::String s, int t);
+ void premtet();
+ void ajchai();
+ void tfleche();
+ void setCoordinates(int sx);
+ void ecr2(Common::String text);
+ void ecr3(Common::String text);
+ void init_nbrepm();
+ void aniof(int ouf, int num);
+ void dessin();
+ void afdes();
+ void tlu(int af, int ob);
+ void affrep();
+ void mennor();
+ void ajjer(int ob);
+ void tsuiv();
+ void treg(int objId);
+ void avpoing(int &objId);
+ void rechai(int &ch);
+ void fenat(char ans);
+
+public:
+ Common::Point _prevPos;
+ int _msg[5];
+ int _fxxBuffer[108];
+ byte _tabdon[4001];
+ bool _soundOff;
+ bool _blo;
+ bool _okdes;
+ bool _largestClearScreen;
+ int _currGraphicalDevice;
+ int _newGraphicalDevice;
+ float _addFix;
+ int _savedBitIndex;
+ int _numpal;
+ int _key;
+ SaveStruct _coreVar, _saveStruct;
+
+ int _maff;
+ int _res;
+ int _caff;
+ int _crep;
+ byte _is;
+ byte _v_lieu[7][25];
+
+ // TODO: Replace the following with proper implementations, or refactor out the code using them
+ byte _mem[65536 * 16];
+
+ ScreenSurface _screenSurface;
+ PaletteManager _paletteManager;
+ GfxSurface _backgroundSurface;
+ Common::RandomSource _randomSource;
+ SoundManager _soundManager;
+ SavegameManager _savegameManager;
+ SpeechManager _speechManager;
+ Menu _menu;
+ MouseHandler _mouse;
+ TextHandler _text;
+
+ MortevielleEngine(OSystem *system, const ADGameDescription *gameDesc);
+ ~MortevielleEngine();
+ virtual bool hasFeature(EngineFeature f) const;
+ virtual bool canLoadGameStateCurrently();
+ virtual bool canSaveGameStateCurrently();
+ virtual Common::Error loadGameState(int slot);
+ virtual Common::Error saveGameState(int slot, const Common::String &desc);
+ virtual Common::Error run();
+ uint32 getGameFlags() const;
+ Common::Language getLanguage() const;
+
+ int getChar();
+ bool keyPressed();
+ Common::Point getMousePos() const { return _mousePos; }
+ void setMousePos(const Common::Point &pt);
+ bool getMouseClick() const { return _mouseClick; }
+ void setMouseClick(bool v) { _mouseClick = v; }
+ Common::String getEngineString(int idx) const { return _engineStrings[idx]; }
+ Common::String getGameString(int idx) const { return _gameStrings[idx]; }
+
+ void gameLoaded();
+ void initGame();
+ void displayAloneText();
+ void draw(int ad, int x, int y);
+ void charToHour();
+ void hourToChar();
+ Common::String getString(int num);
+ void setPal(int n);
+ Common::String copy(const Common::String &s, int idx, size_t size);
+ void testKeyboard();
+ int getPresence(int roomId);
+ void displayEmptyHand();
+
+ void hirs();
+ int testou();
+ void repon(int f, int m);
+ int animof(int ouf, int num);
+ void pictout(int seg, int dep, int x, int y);
+ void sauvecr(int y, int dy);
+ void charecr(int y, int dy);
+
+};
+
+extern MortevielleEngine *g_vm;
+
+#define CHECK_QUIT if (g_vm->shouldQuit()) { return; }
+#define CHECK_QUIT0 if (g_vm->shouldQuit()) { return 0; }
+
+} // End of namespace Mortevielle
+
+#endif
diff --git a/engines/mortevielle/mouse.cpp b/engines/mortevielle/mouse.cpp
new file mode 100644
index 0000000000..598677f762
--- /dev/null
+++ b/engines/mortevielle/mouse.cpp
@@ -0,0 +1,420 @@
+/* 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 "common/endian.h"
+#include "common/rect.h"
+#include "mortevielle/mouse.h"
+#include "mortevielle/mortevielle.h"
+
+namespace Mortevielle {
+
+/**
+ * Initialize the mouse
+ * @remarks Originally called 'init_mouse'
+ */
+void MouseHandler::initMouse() {
+ _counter = 0;
+ _pos = Common::Point(0, 0);
+
+ g_vm->setMouseClick(false);
+}
+
+/**
+ * Hide the mouse
+ * @remarks Originally called 'hide_mouse'
+ */
+void MouseHandler::hideMouse() {
+ --_counter;
+ if (_counter == 0) {
+ int j = 0;
+ switch (g_vm->_currGraphicalDevice) {
+ case MODE_CGA: {
+ int k = 0;
+ j = ((uint)_pos.y >> 1) * 80 + ((uint)_pos.x >> 2);
+ do {
+ WRITE_LE_UINT16(&g_vm->_mem[0xb000 * 16 + j], s_s[0][k]);
+ WRITE_LE_UINT16(&g_vm->_mem[0xb800 * 16 + j + 2], s_s[1][k]);
+ WRITE_LE_UINT16(&g_vm->_mem[0xba00 * 16 + j], s_s[2][k]);
+ WRITE_LE_UINT16(&g_vm->_mem[0xba00 * 16 + j + 2], s_s[3][k]);
+ j += 80;
+ ++k;
+ } while (k < 5);
+ }
+ break;
+ case MODE_AMSTRAD1512: {
+ bool imp = odd(_pos.y);
+ for (int i = 0; i <= 3; ++i) {
+ int k = 0;
+ j = 0;
+ do {
+ if (imp) {
+ WRITE_LE_UINT16(&g_vm->_mem[0xb800 * 16 + j], s_s[i][k]);
+ j += 80 - 0x2000;
+ } else {
+ WRITE_LE_UINT16(&g_vm->_mem[0xb800 * 16 + j], s_s[i][k]);
+ j += 0x2000;
+ }
+ imp = !imp;
+ ++k;
+ } while (k < 8);
+ }
+ break;
+ }
+ case MODE_EGA: {
+ int i = 0;
+ do {
+ int k = 0;
+ j = 0;
+ do {
+ // Useless ?
+ // ps = mem[0xa000 * 16 + j];
+ g_vm->_mem[0xa000 * 16 + j] = lo(s_s[i][k]);
+
+ // Useless ??
+ // ps = mem[0xa000 * 16 + j + 1];
+ g_vm->_mem[0xa000 * 16 + j + 1] = hi(s_s[i][k]);
+ j += 80;
+ ++k;
+ } while (k < 8);
+ ++i;
+ } while (i != 4);
+ }
+ break;
+ case MODE_HERCULES:
+ j = ((uint)_pos.y >> 1) * 80 + ((uint)_pos.x >> 3);
+ for (int i = 0; i <= 5; ++i) {
+ for (int k = 0; k <= 3; ++k)
+ WRITE_LE_UINT16(&g_vm->_mem[0xb000 * 16 + k * 0x200 + j], s_s[i][k]);
+ j += 80;
+ }
+ break;
+ case MODE_TANDY: {
+ j = ((uint)_pos.y >> 2) * 160 + ((uint)_pos.x >> 1);
+ int k = 0;
+ do {
+ for (int i = 0; i <= 3; ++i) {
+ WRITE_LE_UINT16(&g_vm->_mem[0xb800 * 16 + 0x200 * i + j], s_s[k][i + (k << 2)]);
+ WRITE_LE_UINT16(&g_vm->_mem[0xb800 * 16 + 0x200 * i + j + 2], s_s[k + 3][i + (k << 2)]);
+ }
+ j += 160;
+ ++k;
+ } while (k != 3);
+ }
+ break;
+ default:
+ break;
+ } // case Gd
+ }
+}
+
+/**
+ * Show mouse
+ * @remarks Originally called 'show_mouse'
+ */
+void MouseHandler::showMouse() {
+ int k, l;
+
+ ++_counter;
+ if (_counter != 1)
+ return;
+ int j = 0;
+ int i = _pos.x & 7;
+ switch (g_vm->_currGraphicalDevice) {
+ case MODE_CGA:
+ k = 0;
+ j = ((uint)_pos.y >> 1) * 80 + ((uint)_pos.x >> 2);
+ do {
+ s_s[0][k] = READ_LE_UINT16(&g_vm->_mem[0xb800 * 16 + j]);
+ s_s[1][k] = READ_LE_UINT16(&g_vm->_mem[0xb800 * 16 + j + 2]);
+ s_s[2][k] = READ_LE_UINT16(&g_vm->_mem[0xba00 * 16 + j]);
+ s_s[3][k] = READ_LE_UINT16(&g_vm->_mem[0xba00 * 16 + j + 2]);
+ j += 80;
+ ++k;
+ } while (k < 5);
+ break;
+ case MODE_AMSTRAD1512: {
+ bool imp = odd(_pos.y);
+ for (i = 0; i <= 3; ++i) {
+ j = 0;
+ imp = odd(_pos.y);
+ k = 0;
+ do {
+ if (imp) {
+ s_s[i][k] = READ_LE_UINT16(&g_vm->_mem[0xb800 * 16 + j]);
+ j += 80 - 0x2000;
+ } else {
+ s_s[i][k] = READ_LE_UINT16(&g_vm->_mem[0xb800 * 16 + j]);
+ j += 0x2000;
+ }
+ imp = !imp;
+ ++k;
+ } while (k < 8);
+ }
+ break;
+ }
+ case MODE_EGA:
+ l = 0;
+ do {
+ k = 0;
+ j = 0;
+ do {
+ s_s[l][k] = g_vm->_mem[0xa000 * 16 + j] + (g_vm->_mem[(0xa000 * 16) + j + 1] << 8);
+ j += 80;
+ ++k;
+ } while (k < 8);
+ ++l;
+ } while (l != 4);
+ break;
+ case MODE_HERCULES:
+ j = ((uint)_pos.y >> 1) * 80 + ((uint)_pos.x >> 3);
+ for (i = 0; i <= 5; ++i) {
+ for (k = 0; k <= 3; ++k)
+ s_s[i][k] = READ_LE_UINT16(&g_vm->_mem[0xb000 * 16 + k * 0x200 + j]);
+ j += 80;
+ }
+ break;
+ case MODE_TANDY:
+ j = ((uint)_pos.y >> 2) * 160 + ((uint)_pos.x >> 1);
+ k = 0;
+ do {
+ for (i = 0; i <= 3; ++i) {
+ s_s[k][i + (k << 2)] = READ_LE_UINT16(&g_vm->_mem[0xb800 * 16 + 0x200 * i + j]);
+ s_s[k + 3][i + (k << 2)] = READ_LE_UINT16(&g_vm->_mem[0xb800 * 16 + 0x200 * i + j + 2]);
+ }
+ j += 160;
+ ++k;
+ } while (k != 3);
+ break;
+ default:
+ break;
+ } // case Gd
+}
+
+/**
+ * Set mouse position
+ * @remarks Originally called 'pos_mouse'
+ */
+void MouseHandler::setMousePosition(Common::Point newPos) {
+ if (newPos.x > 314 * g_vm->_res)
+ newPos.x = 314 * g_vm->_res;
+ else if (newPos.x < 0)
+ newPos.x = 0;
+ if (newPos.y > 199)
+ newPos.y = 199;
+ else if (newPos.y < 0)
+ newPos.y = 0;
+ if (newPos == _pos)
+ return;
+
+ // Set the new position
+ g_vm->setMousePos(newPos);
+}
+
+/**
+ * Get mouse poisition
+ * @remarks Originally called 'read_pos_mouse'
+ */
+void MouseHandler::getMousePosition(int &x, int &y, int &c) {
+ x = g_vm->getMousePos().x;
+ y = g_vm->getMousePos().y;
+ c = g_vm->getMouseClick() ? 1 : 0;
+}
+
+/**
+ * Move mouse
+ * @remarks Originally called 'mov_mouse'
+ */
+void MouseHandler::moveMouse(bool &funct, char &key) {
+ bool p_key;
+ char in1, in2;
+ int cx, cy, cd;
+
+ // Set defaults and check pending events
+ funct = false;
+ key = '\377';
+ p_key = g_vm->keyPressed();
+
+ // If mouse button clicked, return it
+ if (g_vm->getMouseClick())
+ return;
+
+ // Handle any pending keypresses
+ while (p_key) {
+ CHECK_QUIT;
+
+ in1 = g_vm->getChar();
+ getMousePosition(cx, cy, cd);
+ switch (toupper(in1)) {
+ case '4':
+ cx -= 8;
+ break;
+ case '2':
+ cy += 8;
+ break;
+ case '6':
+ cx += 8;
+ break;
+ case '8':
+ cy -= 8;
+ break;
+ case '7':
+ cy = 1;
+ cx = 1;
+ break;
+ case '1':
+ cx = 1;
+ cy = 190;
+ break;
+ case '9':
+ cx = 315 * g_vm->_res;
+ cy = 1;
+ break;
+ case '3':
+ cy = 190;
+ cx = 315 * g_vm->_res;
+ break;
+ case '5':
+ cy = 100;
+ cx = 155 * g_vm->_res;
+ break;
+ case ' ':
+ case '\15':
+ g_vm->setMouseClick(true);
+ return;
+ break;
+ case '\33':
+ p_key = g_vm->keyPressed();
+
+ if (p_key) {
+ in2 = g_vm->getChar();
+
+ if ((in2 >= ';') && (in2 <= 'D')) {
+ funct = true;
+ key = in2;
+ return;
+ } else {
+ switch (in2) {
+ case 'K':
+ --cx;
+ break;
+ case 'P':
+ ++cy;
+ break;
+ case 'M':
+ cx += 2;
+ break;
+ case 'H':
+ --cy;
+ break;
+ case 'G':
+ --cx;
+ --cy;
+ break;
+ case 'I':
+ ++cx;
+ --cy;
+ break;
+ case 'O':
+ --cx;
+ ++cy;
+ break;
+ case 'Q':
+ ++cx;
+ ++cy;
+ break;
+ default:
+ break;
+ } // case
+ }
+ }
+ break;
+ case 'I':
+ cx = g_vm->_res * 32;
+ cy = 8;
+ break;
+ case 'D':
+ cx = 80 * g_vm->_res;
+ cy = 8;
+ break;
+ case 'A':
+ cx = 126 * g_vm->_res;
+ cy = 8;
+ break;
+ case 'S':
+ cx = 174 * g_vm->_res;
+ cy = 8;
+ break;
+ case 'P':
+ cx = 222 * g_vm->_res;
+ cy = 8;
+ break;
+ case 'F':
+ cx = g_vm->_res * 270;
+ cy = 8;
+ break;
+ case '\23':
+ g_vm->_soundOff = !g_vm->_soundOff;
+ return;
+ break;
+ case '\24': // ^T => mode tandy
+ funct = true;
+ key = '\11';
+ break;
+ case '\10': // ^H => mode Hercule
+ funct = true;
+ key = '\7';
+ break;
+ case '\1':
+ case '\3':
+ case '\5':
+ funct = true;
+ key = in1;
+ break;
+ default:
+ break;
+ }
+
+ setMousePosition(Common::Point(cx, cy));
+ p_key = g_vm->keyPressed();
+ }
+}
+
+/**
+ * Mouse function : Is mouse in a given rect?
+ * @remarks Originally called 'dans_rect'
+ */
+bool MouseHandler::isMouseIn(Common::Rect r) {
+ int x, y, c;
+
+ getMousePosition(x, y, c);
+ if ((x > r.left) && (x < r.right) && (y > r.top) && (y < r.bottom))
+ return true;
+
+ return false;
+}
+
+} // End of namespace Mortevielle
diff --git a/engines/mortevielle/mouse.h b/engines/mortevielle/mouse.h
new file mode 100644
index 0000000000..abfc315677
--- /dev/null
+++ b/engines/mortevielle/mouse.h
@@ -0,0 +1,52 @@
+/* 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
+ */
+
+#ifndef MORTEVIELLE_MOUSE_H
+#define MORTEVIELLE_MOUSE_H
+
+#include "common/rect.h"
+
+namespace Mortevielle {
+
+class MouseHandler {
+private:
+ int s_s[12][6];
+ int _counter;
+public:
+ Common::Point _pos;
+
+ void initMouse();
+ void hideMouse();
+ void showMouse();
+ void setMousePosition(Common::Point newPos);
+ void getMousePosition(int &x, int &y, int &c);
+ void moveMouse(bool &funct, char &key);
+ bool isMouseIn(Common::Rect r);
+};
+
+} // End of namespace Mortevielle
+#endif
diff --git a/engines/mortevielle/outtext.cpp b/engines/mortevielle/outtext.cpp
new file mode 100644
index 0000000000..9e903bb133
--- /dev/null
+++ b/engines/mortevielle/outtext.cpp
@@ -0,0 +1,359 @@
+/* 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 "common/file.h"
+#include "common/str.h"
+#include "mortevielle/mouse.h"
+#include "mortevielle/outtext.h"
+#include "mortevielle/graphics.h"
+#include "mortevielle/mortevielle.h"
+
+namespace Mortevielle {
+
+/**
+ * Next word
+ * @remarks Originally called 'l_motsuiv'
+ */
+int TextHandler::nextWord(int p, const char *ch, int &tab) {
+ int c = p;
+
+ while ((ch[p] != ' ') && (ch[p] != '$') && (ch[p] != '@'))
+ ++p;
+
+ return tab * (p - c);
+}
+
+/**
+ * Engine function - Display Text
+ * @remarks Originally called 'afftex'
+ */
+void TextHandler::displayStr(Common::String inputStr, int x, int y, int dx, int dy, int typ) {
+ int tab;
+ Common::String s;
+ int i, j;
+
+ // Safeguard: add $ just in case
+ inputStr += '$';
+
+ g_vm->_screenSurface.putxy(x, y);
+ if (g_vm->_res == 1)
+ tab = 10;
+ else
+ tab = 6;
+ dx *= 6;
+ dy *= 6;
+ int xc = x;
+ int yc = y;
+ int xf = x + dx;
+ int yf = y + dy;
+ int p = 0;
+ bool stringParsed = (inputStr[p] == '$');
+ s = "";
+ while (!stringParsed) {
+ switch (inputStr[p]) {
+ case '@':
+ g_vm->_screenSurface.drawString(s, typ);
+ s = "";
+ ++p;
+ xc = x;
+ yc += 6;
+ g_vm->_screenSurface.putxy(xc, yc);
+ break;
+ case ' ':
+ s += ' ';
+ xc += tab;
+ ++p;
+ if (nextWord(p, inputStr.c_str(), tab) + xc > xf) {
+ g_vm->_screenSurface.drawString(s, typ);
+ s = "";
+ xc = x;
+ yc += 6;
+ if (yc > yf) {
+ while (!g_vm->keyPressed())
+ ;
+ i = y;
+ do {
+ j = x;
+ do {
+ g_vm->_screenSurface.putxy(j, i);
+ g_vm->_screenSurface.drawString(" ", 0);
+ j += 6;
+ } while (j <= xf);
+ i += 6;
+ } while (i <= yf);
+ yc = y;
+ }
+ g_vm->_screenSurface.putxy(xc, yc);
+ }
+ break;
+ case '$':
+ stringParsed = true;
+ g_vm->_screenSurface.drawString(s, typ);
+ break;
+ default:
+ s += inputStr[p];
+ ++p;
+ xc += tab;
+ break;
+ }
+ }
+}
+
+/**
+ * Load DES file
+ * @remarks Originally called 'chardes'
+ */
+void TextHandler::loadDesFile(Common::String filename, int32 skipSize, int length) {
+ Common::File f;
+ if (!f.open(filename))
+ error("Missing file %s", filename.c_str());
+
+ int skipBlock = 0;
+ while (skipSize > 127) {
+ ++skipBlock;
+ skipSize -= 128;
+ }
+ if (skipBlock != 0)
+ f.seek(skipBlock * 0x80);
+
+ int remainingSkipSize = abs(skipSize);
+ int totalLength = length + remainingSkipSize;
+ int memIndx = 0x6000 * 16;
+ while (totalLength > 0) {
+ f.read(&g_vm->_mem[memIndx], 128);
+ totalLength -= 128;
+ memIndx += 128;
+ }
+ f.close();
+
+ for (int i = remainingSkipSize; i <= length + remainingSkipSize; ++i)
+ g_vm->_mem[0x7000 * 16 + i - remainingSkipSize] = g_vm->_mem[0x6000 * 16 + i];
+}
+
+/**
+ * Load ANI file
+ * @remarks Originally called 'charani'
+ */
+void TextHandler::loadAniFile(Common::String filename, int32 skipSize, int length) {
+ Common::File f;
+ if (!f.open(filename))
+ error("Missing file - %s", filename.c_str());
+
+ int skipBlock = 0;
+ while (skipSize > 127) {
+ skipSize = skipSize - 128;
+ ++skipBlock;
+ }
+ if (skipBlock != 0)
+ f.seek(skipBlock * 0x80);
+
+ int remainingSkipSize = abs(skipSize);
+ int fullLength = length + remainingSkipSize;
+ int memIndx = 0x6000 * 16;
+ while (fullLength > 0) {
+ f.read(&g_vm->_mem[memIndx], 128);
+ fullLength -= 128;
+ memIndx += 128;
+ }
+ f.close();
+
+ for (int i = remainingSkipSize; i <= length + remainingSkipSize; ++i)
+ g_vm->_mem[kAdrAni * 16 + i - remainingSkipSize] = g_vm->_mem[0x6000 * 16 + i];
+}
+
+void TextHandler::taffich() {
+ static const byte _rang[16] = {15, 14, 11, 7, 13, 12, 10, 6, 9, 5, 3, 1, 2, 4, 8, 0};
+
+ byte tran1[] = { 121, 121, 138, 139, 120 };
+ byte tran2[] = { 150, 150, 152, 152, 100, 110, 159, 100, 100 };
+
+ int cx, handle, npal;
+ int32 lgt;
+ int alllum[16];
+
+ int a = g_vm->_caff;
+ if ((a >= 153) && (a <= 161))
+ a = tran2[a - 153];
+ else if ((a >= 136) && (a <= 140))
+ a = tran1[a - 136];
+ int b = a;
+ if (g_vm->_maff == a)
+ return;
+
+ switch (a) {
+ case 16:
+ g_vm->_coreVar._pourc[9] = '*';
+ g_vm->_coreVar._teauto[42] = '*';
+ break;
+ case 20:
+ g_vm->_coreVar._teauto[39] = '*';
+ if (g_vm->_coreVar._teauto[36] == '*') {
+ g_vm->_coreVar._pourc[3] = '*';
+ g_vm->_coreVar._teauto[38] = '*';
+ }
+ break;
+ case 24:
+ g_vm->_coreVar._teauto[37] = '*';
+ break;
+ case 30:
+ g_vm->_coreVar._teauto[9] = '*';
+ break;
+ case 31:
+ g_vm->_coreVar._pourc[4] = '*';
+ g_vm->_coreVar._teauto[35] = '*';
+ break;
+ case 118:
+ g_vm->_coreVar._teauto[41] = '*';
+ break;
+ case 143:
+ g_vm->_coreVar._pourc[1] = '*';
+ break;
+ case 150:
+ g_vm->_coreVar._teauto[34] = '*';
+ break;
+ case 151:
+ g_vm->_coreVar._pourc[2] = '*';
+ break;
+ default:
+ break;
+ }
+
+ g_vm->_okdes = true;
+ g_vm->_mouse.hideMouse();
+ lgt = 0;
+ Common::String filename;
+
+ if ((a != 50) && (a != 51)) {
+ int m = a + 2000;
+ if ((m > 2001) && (m < 2010))
+ m = 2001;
+ if (m == 2011)
+ m = 2010;
+ if (a == 32)
+ m = 2034;
+ if ((a == 17) && (g_vm->_maff == 14))
+ m = 2018;
+
+ if (a > 99) {
+ if ((g_vm->_is == 1) || (g_vm->_is == 0))
+ m = 2031;
+ else
+ m = 2032;
+ }
+
+ if (((a > 69) && (a < 80)) || (a == 30) || (a == 31) || (a == 144) || (a == 147) || (a == 149))
+ m = 2030;
+
+ if (((a < 27) && (((g_vm->_maff > 69) && (!g_vm->_coreVar._alreadyEnteredManor)) || (g_vm->_maff > 99))) || ((g_vm->_maff > 29) && (g_vm->_maff < 33)))
+ m = 2033;
+
+ g_vm->_maff = a;
+ if (a == 159)
+ a = 86;
+ else if (a > 140)
+ a -= 67;
+ else if (a > 137)
+ a -= 66;
+ else if (a > 99)
+ a -= 64;
+ else if (a > 69)
+ a -= 42;
+ else if (a > 29)
+ a -= 5;
+ else if (a == 26)
+ a = 24;
+ else if (a > 18)
+ --a;
+ npal = a;
+
+ for (cx = 0; cx <= (a - 1); ++cx)
+ lgt += g_vm->_fxxBuffer[cx];
+ handle = g_vm->_fxxBuffer[a];
+
+ filename = "DXX.mor";
+ } else {
+ if (g_vm->getLanguage() == Common::DE_DEU)
+ filename = "DZZALL";
+ else
+ filename = "DZZ.mor";
+
+ handle = g_vm->_fxxBuffer[87];
+ if (a == 51) {
+ lgt = handle;
+ handle = g_vm->_fxxBuffer[88];
+ }
+ g_vm->_maff = a;
+ npal = a + 37;
+ }
+ loadDesFile(filename, lgt, handle);
+ if (g_vm->_currGraphicalDevice == MODE_HERCULES) {
+ for (int i = 0; i <= 15; ++i) {
+ int palh = READ_LE_UINT16(&g_vm->_mem[(0x7000 * 16) + ((i + 1) << 1)]);
+ alllum[i] = (palh & 15) + (((uint)palh >> 12) & 15) + (((uint)palh >> 8) & 15);
+ }
+ for (int i = 0; i <= 15; ++i) {
+ int k = 0;
+ for (int j = 0; j <= 15; ++j)
+ if (alllum[j] > alllum[k])
+ k = j;
+ g_vm->_mem[(0x7000 * 16) + 2 + (k << 1)] = _rang[i];
+ alllum[k] = -1;
+ }
+ }
+ g_vm->_numpal = npal;
+ g_vm->setPal(npal);
+
+ if ((b < 15) || (b == 16) || (b == 17) || (b == 24) || (b == 26) || (b == 50)) {
+ lgt = 0;
+ if ((b < 15) || (b == 16) || (b == 17) || (b == 24) || (b == 26)) {
+ if (b == 26)
+ b = 18;
+ else if (b == 24)
+ b = 17;
+ else if (b > 15)
+ --b;
+ for (cx = 0; cx <= (b - 1); ++cx)
+ lgt += g_vm->_fxxBuffer[cx + 89];
+ handle = g_vm->_fxxBuffer[b + 89];
+ filename = "AXX.mor";
+ } else if (b == 50) {
+ filename = "AZZ.mor";
+ handle = 1260;
+ }
+ loadAniFile(filename, lgt, handle);
+ }
+ g_vm->_mouse.showMouse();
+ if ((a < 27) && ((g_vm->_maff < 27) || (g_vm->_coreVar._currPlace == LANDING)) && (g_vm->_msg[4] != OPCODE_ENTER)) {
+ if ((a == 13) || (a == 14))
+ g_vm->displayAloneText();
+ else if (!g_vm->_blo)
+ g_vm->getPresence(g_vm->_coreVar._currPlace);
+ g_vm->_savedBitIndex = 0;
+ }
+}
+
+} // End of namespace Mortevielle
diff --git a/engines/mortevielle/outtext.h b/engines/mortevielle/outtext.h
new file mode 100644
index 0000000000..25162981c6
--- /dev/null
+++ b/engines/mortevielle/outtext.h
@@ -0,0 +1,48 @@
+/* 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
+ */
+
+#ifndef MORTEVIELLE_OUTTEXT_H
+#define MORTEVIELLE_OUTTEXT_H
+
+#include "common/str.h"
+
+namespace Mortevielle {
+
+const int kAdrAni = 0x7314;
+
+class TextHandler {
+private:
+ int nextWord(int p, const char *ch, int &tab);
+public:
+ void displayStr(Common::String inputStr, int x, int y, int dx, int dy, int typ);
+ void loadDesFile(Common::String filename, int32 passe, int long_);
+ void loadAniFile(Common::String filename, int32 skipSize, int length);
+ void taffich();
+};
+
+} // End of namespace Mortevielle
+#endif
diff --git a/engines/mortevielle/saveload.cpp b/engines/mortevielle/saveload.cpp
new file mode 100644
index 0000000000..48ae04678d
--- /dev/null
+++ b/engines/mortevielle/saveload.cpp
@@ -0,0 +1,317 @@
+/* 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 "common/file.h"
+#include "common/system.h"
+#include "mortevielle/dialogs.h"
+#include "mortevielle/mortevielle.h"
+#include "mortevielle/mouse.h"
+#include "mortevielle/saveload.h"
+
+namespace Mortevielle {
+
+static const char SAVEGAME_ID[4] = { 'M', 'O', 'R', 'T' };
+
+Common::String SavegameManager::generateSaveName(int slotNumber) {
+ return Common::String::format("sav%d.mor", slotNumber);
+}
+
+/**
+ * Handle saving or loading savegame data
+ */
+void SavegameManager::sync_save(Common::Serializer &sz) {
+ sz.syncAsSint16LE(g_vm->_saveStruct._faithScore);
+ for (int i = 0; i < 11; ++i)
+ sz.syncAsByte(g_vm->_saveStruct._pourc[i]);
+ for (int i = 0; i < 43; ++i)
+ sz.syncAsByte(g_vm->_saveStruct._teauto[i]);
+ for (int i = 0; i < 31; ++i)
+ sz.syncAsByte(g_vm->_saveStruct._sjer[i]);
+
+ sz.syncAsSint16LE(g_vm->_saveStruct._currPlace);
+ sz.syncAsSint16LE(g_vm->_saveStruct._atticBallHoleObjectId);
+ sz.syncAsSint16LE(g_vm->_saveStruct._atticRodHoleObjectId);
+ sz.syncAsSint16LE(g_vm->_saveStruct._cellarObjectId);
+ sz.syncAsSint16LE(g_vm->_saveStruct._secretPassageObjectId);
+ sz.syncAsSint16LE(g_vm->_saveStruct._wellObjectId);
+ sz.syncAsSint16LE(g_vm->_saveStruct._selectedObjectId);
+ sz.syncAsSint16LE(g_vm->_saveStruct._purpleRoomObjectId);
+ sz.syncAsSint16LE(g_vm->_saveStruct._cryptObjectId);
+ sz.syncAsByte(g_vm->_saveStruct._alreadyEnteredManor);
+ sz.syncAsByte(g_vm->_saveStruct._fullHour);
+
+ sz.syncBytes(_tabdonSaveBuffer, 391);
+}
+
+/**
+ * Inner code for loading a saved game
+ * @remarks Originally called 'takesav'
+ */
+void SavegameManager::loadSavegame(int n) {
+ // -- Load the file
+ Common::String filename = generateSaveName(n);
+
+ // Try loading first from the save area
+ Common::SeekableReadStream *stream = g_system->getSavefileManager()->openForLoading(filename);
+
+ // If not present, try loading from the program folder
+ Common::File f;
+ if (stream == NULL) {
+ if (!f.open(filename))
+ error("Unable to open save file '%s'", filename.c_str());
+
+ stream = f.readStream(f.size());
+ f.close();
+ }
+
+ // Check whether it's a ScummVM saved game
+ char buffer[4];
+ stream->read(buffer, 4);
+ if (!strncmp(&buffer[0], &SAVEGAME_ID[0], 4)) {
+ // Yes, it is, so skip over the savegame header
+ SavegameHeader header;
+ readSavegameHeader(stream, header);
+ delete header.thumbnail;
+ } else {
+ stream->seek(0);
+ }
+
+ // Read the game contents
+ Common::Serializer sz(stream, NULL);
+ sync_save(sz);
+
+ g_vm->_coreVar = g_vm->_saveStruct;
+ for (int i = 0; i <= 389; ++i)
+ g_vm->_tabdon[i + kAcha] = _tabdonSaveBuffer[i];
+
+ // Close the stream
+ delete stream;
+}
+
+/**
+ * Load a saved game
+ */
+Common::Error SavegameManager::loadGame(int n) {
+ g_vm->_mouse.hideMouse();
+ g_vm->displayEmptyHand();
+ loadSavegame(n);
+
+ /* Initialization */
+ g_vm->charToHour();
+ g_vm->initGame();
+ g_vm->gameLoaded();
+ g_vm->_mouse.showMouse();
+ return Common::kNoError;
+}
+
+/**
+ * Save the game
+ */
+Common::Error SavegameManager::saveGame(int n, const Common::String &saveName) {
+ Common::OutSaveFile *f;
+ int i;
+
+ g_vm->_mouse.hideMouse();
+ g_vm->hourToChar();
+
+ for (i = 0; i <= 389; ++i)
+ _tabdonSaveBuffer[i] = g_vm->_tabdon[i + kAcha];
+ g_vm->_saveStruct = g_vm->_coreVar;
+ if (g_vm->_saveStruct._currPlace == ROOM26)
+ g_vm->_saveStruct._currPlace = LANDING;
+
+ Common::String filename = generateSaveName(n);
+ f = g_system->getSavefileManager()->openForSaving(filename);
+
+ // Write out the savegame header
+ f->write(&SAVEGAME_ID[0], 4);
+
+ // Write out the header
+ SavegameHeader header;
+ writeSavegameHeader(f, saveName);
+
+ // Write out the savegame contents
+ Common::Serializer sz(NULL, f);
+ sync_save(sz);
+
+ // Close the save file
+ f->finalize();
+ delete f;
+
+ // Skipped: dialog asking to swap floppy
+
+ g_vm->_mouse.showMouse();
+ return Common::kNoError;
+}
+
+void SavegameManager::writeSavegameHeader(Common::OutSaveFile *out, const Common::String &saveName) {
+ // Write out a savegame header
+ out->writeByte(SAVEGAME_VERSION);
+
+ // Write savegame name
+ out->writeString(saveName);
+ out->writeByte(0);
+
+ // Get the active palette
+ uint8 thumbPalette[256 * 3];
+ g_system->getPaletteManager()->grabPalette(thumbPalette, 0, 256);
+
+ // Create a thumbnail and save it
+ Graphics::Surface *thumb = new Graphics::Surface();
+ Graphics::Surface s = g_vm->_screenSurface.lockArea(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+
+ ::createThumbnail(thumb, (const byte *)s.pixels, SCREEN_WIDTH, SCREEN_HEIGHT, thumbPalette);
+ Graphics::saveThumbnail(*out, *thumb);
+ thumb->free();
+ delete thumb;
+
+ // Write out the save date/time
+ TimeDate td;
+ g_system->getTimeAndDate(td);
+ out->writeSint16LE(td.tm_year + 1900);
+ out->writeSint16LE(td.tm_mon + 1);
+ out->writeSint16LE(td.tm_mday);
+ out->writeSint16LE(td.tm_hour);
+ out->writeSint16LE(td.tm_min);
+}
+
+bool SavegameManager::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) {
+ header.thumbnail = NULL;
+
+ // Get the savegame version
+ header.version = in->readByte();
+
+ // Read in the save name
+ header.saveName.clear();
+ char ch;
+ while ((ch = (char)in->readByte()) != '\0')
+ header.saveName += ch;
+
+ // Get the thumbnail
+ header.thumbnail = Graphics::loadThumbnail(*in);
+ if (!header.thumbnail)
+ return false;
+
+ // Read in save date/time
+ header.saveYear = in->readSint16LE();
+ header.saveMonth = in->readSint16LE();
+ header.saveDay = in->readSint16LE();
+ header.saveHour = in->readSint16LE();
+ header.saveMinutes = in->readSint16LE();
+
+ return true;
+}
+
+SaveStateList SavegameManager::listSaves(const char *target) {
+ Common::String pattern = "sav*.mor";
+ Common::StringArray files = g_system->getSavefileManager()->listSavefiles(pattern);
+ sort(files.begin(), files.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = files.begin(); file != files.end(); ++file) {
+ // Obtain the last 3 digits of the filename, since they correspond to the save slot
+ const Common::String &fname = *file;
+ int slotNumber = atoi(fname.c_str() + 3);
+
+ Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fname);
+ if (in) {
+ // There can be two types of savegames: original interpreter savegames, and ScummVM savegames.
+ // Original interpreter savegames are 497 bytes, and still need to be supported because the
+ // initial game state is stored as a savegame
+ bool validFlag = false;
+ Common::String saveDescription;
+
+ char buffer[4];
+ in->read(buffer, 4);
+ if (!strncmp(&buffer[0], &SAVEGAME_ID[0], 4)) {
+ // ScummVm savegame. Read in the header to get the savegame name
+ SavegameHeader header;
+ validFlag = readSavegameHeader(in, header);
+
+ if (validFlag) {
+ delete header.thumbnail;
+ saveDescription = header.saveName;
+ }
+ } else if (file->size() == 497) {
+ // Form an appropriate savegame name
+ saveDescription = (slotNumber == 0) ? "Initial game state" :
+ Common::String::format("Savegame #%d", slotNumber);
+ validFlag = true;
+ }
+
+ if (validFlag)
+ // Got a valid savegame
+ saveList.push_back(SaveStateDescriptor(slotNumber, saveDescription));
+
+ delete in;
+ }
+ }
+
+ return saveList;
+}
+
+SaveStateDescriptor SavegameManager::querySaveMetaInfos(int slot) {
+ Common::String fileName = Mortevielle::SavegameManager::generateSaveName(slot);
+ Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
+
+ if (f) {
+ // Check to see if it's a ScummVM savegame or not
+ char buffer[4];
+ f->read(buffer, 4);
+
+ bool hasHeader = !strncmp(&buffer[0], &SAVEGAME_ID[0], 4);
+
+ if (!hasHeader) {
+ // Original savegame perhaps?
+ delete f;
+
+ SaveStateDescriptor desc(slot, Common::String::format("Savegame #%d", slot));
+ desc.setDeletableFlag(slot != 0);
+ desc.setWriteProtectedFlag(slot == 0);
+ return desc;
+ } else {
+ // Get the savegame header information
+ SavegameHeader header;
+ readSavegameHeader(f, header);
+ delete f;
+
+ // Create the return descriptor
+ SaveStateDescriptor desc(slot, header.saveName);
+ desc.setDeletableFlag(true);
+ desc.setWriteProtectedFlag(false);
+ desc.setThumbnail(header.thumbnail);
+ desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay);
+ desc.setSaveTime(header.saveHour, header.saveMinutes);
+
+ return desc;
+ }
+ }
+
+ return SaveStateDescriptor();
+}
+
+} // End of namespace Mortevielle
diff --git a/engines/mortevielle/saveload.h b/engines/mortevielle/saveload.h
new file mode 100644
index 0000000000..21522102a2
--- /dev/null
+++ b/engines/mortevielle/saveload.h
@@ -0,0 +1,68 @@
+/* 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
+ */
+
+#ifndef MORTEVIELLE_SAVELOAD_H
+#define MORTEVIELLE_SAVELOAD_H
+
+#include "common/savefile.h"
+#include "common/serializer.h"
+#include "graphics/palette.h"
+#include "graphics/scaler.h"
+#include "graphics/thumbnail.h"
+
+#define SAVEGAME_VERSION 1
+
+namespace Mortevielle {
+
+struct SavegameHeader {
+ uint8 version;
+ Common::String saveName;
+ Graphics::Surface *thumbnail;
+ int saveYear, saveMonth, saveDay;
+ int saveHour, saveMinutes;
+ int totalFrames;
+};
+
+class SavegameManager {
+private:
+ byte _tabdonSaveBuffer[391];
+
+ void sync_save(Common::Serializer &sz);
+public:
+ void loadSavegame(int n);
+ Common::Error loadGame(int n);
+ Common::Error saveGame(int n, const Common::String &saveName);
+
+ static void writeSavegameHeader(Common::OutSaveFile *out, const Common::String &saveName);
+ static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header);
+ static Common::String generateSaveName(int slotNumber);
+ static SaveStateList listSaves(const char *target);
+ static SaveStateDescriptor querySaveMetaInfos(int slot);
+};
+
+} // End of namespace Mortevielle
+#endif
diff --git a/engines/mortevielle/sound.cpp b/engines/mortevielle/sound.cpp
new file mode 100644
index 0000000000..bb85221d75
--- /dev/null
+++ b/engines/mortevielle/sound.cpp
@@ -0,0 +1,201 @@
+/* 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 "common/scummsys.h"
+#include "mortevielle/sound.h"
+#include "mortevielle/mortevielle.h"
+
+namespace Mortevielle {
+
+/**
+ * Constructor
+ */
+PCSpeaker::PCSpeaker(int rate) {
+ _rate = rate;
+ _oscLength = 0;
+ _oscSamples = 0;
+ _remainingSamples = 0;
+ _volume = 255;
+}
+
+/**
+ * Destructor
+ */
+PCSpeaker::~PCSpeaker() {
+}
+
+/**
+ * Adds a new note to the queue of notes to be played.
+ */
+void PCSpeaker::play(int freq, uint32 length) {
+ assert((freq > 0) && (length > 0));
+ Common::StackLock lock(_mutex);
+
+ _pendingNotes.push(SpeakerNote(freq, length));
+}
+
+/**
+ * Stops the currently playing song
+ */
+void PCSpeaker::stop() {
+ Common::StackLock lock(_mutex);
+
+ _remainingSamples = 0;
+ _pendingNotes.clear();
+}
+
+void PCSpeaker::setVolume(byte volume) {
+ _volume = volume;
+}
+
+/**
+ * Return true if a song is currently playing
+ */
+bool PCSpeaker::isPlaying() const {
+ return !_pendingNotes.empty() || (_remainingSamples != 0);
+}
+
+/**
+ * Method used by the mixer to pull off pending samples to play
+ */
+int PCSpeaker::readBuffer(int16 *buffer, const int numSamples) {
+ Common::StackLock lock(_mutex);
+
+ int i;
+
+ for (i = 0; (_remainingSamples || !_pendingNotes.empty()) && (i < numSamples); ++i) {
+ if (!_remainingSamples)
+ // Used up the current note, so queue the next one
+ dequeueNote();
+
+ buffer[i] = generateSquare(_oscSamples, _oscLength) * _volume;
+ if (_oscSamples++ >= _oscLength)
+ _oscSamples = 0;
+
+ _remainingSamples--;
+ }
+
+ // Clear the rest of the buffer
+ if (i < numSamples)
+ memset(buffer + i, 0, (numSamples - i) * sizeof(int16));
+
+ return numSamples;
+}
+
+/**
+ * Dequeues a note from the pending note list
+ */
+void PCSpeaker::dequeueNote() {
+ SpeakerNote note = _pendingNotes.pop();
+
+ _oscLength = _rate / note.freq;
+ _oscSamples = 0;
+ _remainingSamples = (_rate * note.length) / 1000000;
+ assert((_oscLength > 0) && (_remainingSamples > 0));
+}
+
+/**
+ * Support method for generating a square wave
+ */
+int8 PCSpeaker::generateSquare(uint32 x, uint32 oscLength) {
+ return (x < (oscLength / 2)) ? 127 : -128;
+}
+
+/*-------------------------------------------------------------------------*/
+
+const int tab[16] = { -96, -72, -48, -32, -20, -12, -8, -4, 0, 4, 8, 12, 20, 32, 48, 72 };
+
+// The PC timer chip works at a frequency of 1.19318Mhz
+#define TIMER_FREQUENCY 1193180
+
+SoundManager::SoundManager(Audio::Mixer *mixer) {
+ _mixer = mixer;
+ _speakerStream = new PCSpeaker(mixer->getOutputRate());
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, &_speakerHandle,
+ _speakerStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+SoundManager::~SoundManager() {
+ _mixer->stopHandle(_speakerHandle);
+ delete _speakerStream;
+
+}
+
+/**
+ * Decode music data
+ */
+void SoundManager::decodeMusic(const byte *PSrc, byte *PDest, int NbreSeg) {
+ int seed = 128;
+ int v;
+
+ for (int idx1 = 0; idx1 < (NbreSeg * 2); ++idx1) {
+ for (int idx2 = 0; idx2 < 64; ++idx2) {
+ byte srcByte = *PSrc++;
+ v = tab[srcByte >> 4];
+ seed += v;
+ *PDest++ = seed & 0xff;
+
+ v = tab[srcByte & 0xf];
+ seed += v;
+ *PDest++ = seed & 0xff;
+ }
+ }
+}
+
+void SoundManager::litph(tablint &t, int typ, int tempo) {
+ return;
+}
+
+void SoundManager::playNote(int frequency, int32 length) {
+ _speakerStream->play(frequency, length);
+}
+
+
+void SoundManager::musyc(tablint &tb, int nbseg, int att) {
+#ifdef DEBUG
+ const byte *pSrc = &mem[0x5000 * 16];
+
+ // Convert the countdown amount to a tempo rate, and then to note length in microseconds
+ int tempo = TIMER_FREQUENCY / att;
+ int length = 1000000 / tempo;
+
+ for (int noteIndex = 0; noteIndex < (nbseg * 16); ++noteIndex) {
+ int lookupValue = *pSrc++;
+ int noteCountdown = tb[lookupValue];
+ int noteFrequency = TIMER_FREQUENCY / noteCountdown;
+
+ playNote(noteFrequency, length);
+ }
+
+ // Keep waiting until the song has been finished
+ while (_speakerStream->isPlaying() && !g_vm->shouldQuit()) {
+ g_vm->delay(10);
+ }
+#endif
+}
+
+} // End of namespace Mortevielle
diff --git a/engines/mortevielle/sound.h b/engines/mortevielle/sound.h
new file mode 100644
index 0000000000..675fc78f78
--- /dev/null
+++ b/engines/mortevielle/sound.h
@@ -0,0 +1,112 @@
+/* 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
+ */
+
+#ifndef MORTEVIELLE_SOUND_H
+#define MORTEVIELLE_SOUND_H
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "common/mutex.h"
+#include "common/queue.h"
+
+namespace Mortevielle {
+
+typedef int tablint[256];
+
+/**
+ * Structure used to store pending notes to play
+ */
+struct SpeakerNote {
+ int freq;
+ uint32 length;
+
+ SpeakerNote(int noteFreq, uint32 noteLength) {
+ freq = noteFreq;
+ length = noteLength;
+ }
+};
+
+/**
+ * This is a modified PC Speaker class that allows the queueing of an entire song
+ * sequence one note at a time.
+ */
+class PCSpeaker : public Audio::AudioStream {
+private:
+ Common::Queue<SpeakerNote> _pendingNotes;
+ Common::Mutex _mutex;
+
+ int _rate;
+ uint32 _oscLength;
+ uint32 _oscSamples;
+ uint32 _remainingSamples;
+ uint32 _mixedSamples;
+ byte _volume;
+
+ void dequeueNote();
+protected:
+ static int8 generateSquare(uint32 x, uint32 oscLength);
+public:
+ PCSpeaker(int rate = 44100);
+ ~PCSpeaker();
+
+ /** Play a note for length microseconds.
+ */
+ void play(int freq, uint32 length);
+ /** Stop the currently playing sequence */
+ void stop();
+ /** Adjust the volume. */
+ void setVolume(byte volume);
+
+ bool isPlaying() const;
+
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const { return false; }
+ bool endOfData() const { return false; }
+ bool endOfStream() const { return false; }
+ int getRate() const { return _rate; }
+};
+
+class SoundManager {
+private:
+ Audio::Mixer *_mixer;
+ PCSpeaker *_speakerStream;
+ Audio::SoundHandle _speakerHandle;
+public:
+ SoundManager(Audio::Mixer *mixer);
+ ~SoundManager();
+
+ void playNote(int frequency, int32 length);
+
+ void decodeMusic(const byte *PSrc, byte *PDest, int NbreSeg);
+ void litph(tablint &t, int typ, int tempo);
+ void musyc(tablint &tb, int nbseg, int att);
+};
+
+} // End of namespace Mortevielle
+
+#endif
diff --git a/engines/mortevielle/speech.cpp b/engines/mortevielle/speech.cpp
new file mode 100644
index 0000000000..853d6baae0
--- /dev/null
+++ b/engines/mortevielle/speech.cpp
@@ -0,0 +1,598 @@
+/* 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 "common/endian.h"
+#include "common/file.h"
+#include "mortevielle/speech.h"
+#include "mortevielle/sound.h"
+#include "mortevielle/mortevielle.h"
+
+namespace Mortevielle {
+
+const byte _tnocon[364] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+const byte _intcon[26] = {1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0};
+const byte _typcon[26] = {0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3};
+const byte _tabdph[16] = {0, 10, 2, 0, 2, 10, 3, 0, 3, 7, 5, 0, 6, 7, 7, 10};
+const byte _tabdbc[18] = {7, 23, 7, 14, 13, 9, 14, 9, 5, 12, 6, 12, 13, 4, 0, 4, 5, 9};
+
+SpeechManager::SpeechManager() {
+ _typlec = 0;
+ _phonemeNumb = 0;
+
+ for (int i = 0; i < 3; i++) {
+ _queue[i]._val = 0;
+ _queue[i]._code = 0;
+ _queue[i]._acc = 0;
+ _queue[i]._freq = 0;
+ _queue[i]._rep = 0;
+ }
+}
+
+void SpeechManager::spfrac(int wor) {
+ _queue[2]._rep = (uint)wor >> 12;
+ if ((_typlec == 0) && (_queue[2]._code != 9))
+ if (((_queue[2]._code > 4) && (_queue[2]._val != 20) && (_queue[2]._rep != 3) && (_queue[2]._rep != 6) && (_queue[2]._rep != 9)) ||
+ ((_queue[2]._code < 5) && ((_queue[2]._val != 19) && (_queue[2]._val != 22) && (_queue[2]._rep != 4) && (_queue[2]._rep != 9)))) {
+ ++_queue[2]._rep;
+ }
+
+ _queue[2]._freq = ((uint)wor >> 6) & 7;
+ _queue[2]._acc = ((uint)wor >> 9) & 7;
+}
+
+void SpeechManager::charg_car(int &currWordNumb) {
+ int wor = swap(READ_LE_UINT16(&g_vm->_mem[kAdrWord + currWordNumb]));
+ int int_ = wor & 0x3f; // 63
+
+ if ((int_ >= 0) && (int_ <= 13)) {
+ _queue[2]._val = int_;
+ _queue[2]._code = 5;
+ } else if ((int_ >= 14) && (int_ <= 21)) {
+ _queue[2]._val = int_;
+ _queue[2]._code = 6;
+ } else if ((int_ >= 22) && (int_ <= 47)) {
+ int_ = int_ - 22;
+ _queue[2]._val = int_;
+ _queue[2]._code = _typcon[int_];
+ } else if ((int_ >= 48) && (int_ <= 56)) {
+ _queue[2]._val = int_ - 22;
+ _queue[2]._code = 4;
+ } else {
+ switch (int_) {
+ case 60:
+ _queue[2]._val = 32; /* " " */
+ _queue[2]._code = 9;
+ break;
+ case 61:
+ _queue[2]._val = 46; /* "." */
+ _queue[2]._code = 9;
+ break;
+ case 62:
+ _queue[2]._val = 35; /* "#" */
+ _queue[2]._code = 9;
+ default:
+ break;
+ }
+ }
+
+ spfrac(wor);
+ currWordNumb += 2;
+}
+
+
+void SpeechManager::entroct(byte o) {
+ g_vm->_mem[kAdrTroct * 16 + _ptr_oct] = o;
+ ++_ptr_oct;
+}
+
+void SpeechManager::veracf(byte b) {
+ ;
+}
+
+void SpeechManager::cctable(tablint &t) {
+ float tb[257];
+
+ tb[0] = 0;
+ for (int k = 0; k <= 255; ++k) {
+ tb[k + 1] = g_vm->_addFix + tb[k];
+ t[255 - k] = abs((int)tb[k] + 1);
+ }
+}
+
+void SpeechManager::regenbruit() {
+ int i = kOffsetB3 + 8590;
+ int j = 0;
+ do {
+ _cfiphBuffer[j] = READ_LE_UINT16(&g_vm->_mem[kAdrNoise3 + i]);
+ i += 2;
+ ++j;
+ } while (i < kOffsetB3 + 8790);
+}
+
+/**
+ * Load sonmus.mor file
+ * @remarks Originally called 'charge_son'
+ */
+void SpeechManager::loadMusicSound() {
+ Common::File f;
+
+ if (!f.open("sonmus.mor"))
+ error("Missing file - sonmus.mor");
+
+ f.read(&g_vm->_mem[0x7414 * 16 + 0], 273);
+
+ g_vm->_soundManager.decodeMusic(&g_vm->_mem[0x7414 * 16], &g_vm->_mem[kAdrNoise * 16], 273);
+ f.close();
+}
+
+/**
+ * Load phoneme sound file
+ * @remarks Originally called 'charge_phbruit'
+ */
+void SpeechManager::loadPhonemeSounds() {
+ Common::File f;
+
+ if (!f.open("phbrui.mor"))
+ error("Missing file - phbrui.mor");
+
+ for (int i = 1; i <= 3; ++i)
+ _cfiphBuffer[i] = f.readSint16LE();
+
+ f.close();
+}
+
+/**
+ * Speech function - Load Noise file
+ * @remarks Originally called 'charge_bruit'
+ */
+void SpeechManager::loadNoise() {
+ Common::File f;
+ int i;
+
+ if (!f.open("bruits")) //Translation: "noise"
+ error("Missing file - bruits");
+
+ f.read(&g_vm->_mem[kAdrNoise * 16 + 0], 250);
+ for (i = 0; i <= 19013; ++i)
+ g_vm->_mem[kAdrNoise * 16 + 32000 + i] = g_vm->_mem[kAdrNoise5 + i];
+ f.read(&g_vm->_mem[kAdrNoise1 * 16 + kOffsetB1], 149);
+
+ f.close();
+}
+
+void SpeechManager::trait_car() {
+ byte d3;
+ int d2, i;
+
+ switch (_queue[1]._code) {
+ case 9:
+ if (_queue[1]._val != ord('#'))
+ for (i = 0; i <= _queue[1]._rep; ++i)
+ entroct(_queue[1]._val);
+ break;
+ case 5:
+ case 6:
+ if (_queue[1]._code == 6)
+ d3 = _tabdph[(_queue[1]._val - 14) << 1];
+ else
+ d3 = kNullValue;
+ if (_queue[0]._code >= 5) {
+ veracf(_queue[1]._acc);
+ if (_queue[0]._code == 9) {
+ entroct(4);
+ if (d3 == kNullValue)
+ entroct(_queue[1]._val);
+ else
+ entroct(d3);
+ entroct(22);
+ }
+ }
+
+ switch (_queue[1]._rep) {
+ case 0:
+ entroct(0);
+ entroct(_queue[1]._val);
+ if (d3 == kNullValue)
+ if (_queue[2]._code == 9)
+ entroct(2);
+ else
+ entroct(4);
+ else if (_queue[2]._code == 9)
+ entroct(0);
+ else
+ entroct(1);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ if (_queue[1]._rep != 4) {
+ i = _queue[1]._rep - 5;
+ do {
+ --i;
+ entroct(0);
+ if (d3 == kNullValue)
+ entroct(_queue[1]._val);
+ else
+ entroct(d3);
+ entroct(3);
+ } while (i >= 0);
+ }
+ if (d3 == kNullValue) {
+ entroct(4);
+ entroct(_queue[1]._val);
+ entroct(0);
+ } else {
+ entroct(0);
+ entroct(_queue[1]._val);
+ entroct(3);
+ }
+ break;
+ case 7:
+ case 8:
+ case 9:
+ if (_queue[1]._rep != 7) {
+ i = _queue[1]._rep - 8;
+ do {
+ --i;
+ entroct(0);
+ if (d3 == kNullValue)
+ entroct(_queue[1]._val);
+ else
+ entroct(d3);
+ entroct(3);
+ } while (i >= 0);
+ }
+ if (d3 == kNullValue) {
+ entroct(0);
+ entroct(_queue[1]._val);
+ entroct(2);
+ } else {
+ entroct(0);
+ entroct(_queue[1]._val);
+ entroct(0);
+ }
+ break;
+ case 1:
+ case 2:
+ case 3:
+ if (_queue[1]._rep != 1) {
+ i = _queue[1]._rep - 2;
+ do {
+ --i;
+ entroct(0);
+ if (d3 == kNullValue)
+ entroct(_queue[1]._val);
+ else
+ entroct(d3);
+ entroct(3);
+ } while (i >= 0);
+ }
+ entroct(0);
+ entroct(_queue[1]._val);
+ if (_queue[2]._code == 9)
+ entroct(0);
+ else
+ entroct(1);
+ break;
+ default:
+ break;
+ } // switch c2.rep
+ break;
+
+ case 2:
+ case 3:
+ d3 = _queue[1]._code + 5; // 7 ou 8 => Corresponding vowel
+ if (_queue[0]._code > 4) {
+ veracf(_queue[1]._acc);
+ if (_queue[0]._code == 9) {
+ entroct(4);
+ entroct(d3);
+ entroct(22);
+ }
+ }
+ i = _queue[1]._rep;
+ assert(i >= 0);
+ if (i != 0) {
+ do {
+ --i;
+ entroct(0);
+ entroct(d3);
+ entroct(3);
+ } while (i > 0);
+ }
+ veracf(_queue[2]._acc);
+ if (_queue[2]._code == 6) {
+ entroct(4);
+ entroct(_tabdph[(_queue[2]._val - 14) << 1]);
+ entroct(_queue[1]._val);
+ } else {
+ entroct(4);
+ if (_queue[2]._val == 4)
+ entroct(3);
+ else
+ entroct(_queue[2]._val);
+ entroct(_queue[1]._val);
+ }
+ break;
+ case 0:
+ case 1:
+ veracf(_queue[1]._acc);
+ switch (_queue[2]._code) {
+ case 2:
+ d2 = 7;
+ break;
+ case 3:
+ d2 = 8;
+ break;
+ case 6:
+ d2 = _tabdph[(_queue[2]._val - 14) << 1];
+ break;
+ case 5:
+ d2 = _queue[2]._val;
+ break;
+ default:
+ d2 = 10;
+ break;
+ } // switch c3._code
+ d2 = (d2 * 26) + _queue[1]._val;
+ if (_tnocon[d2] == 0)
+ d3 = 2;
+ else
+ d3 = 6;
+ if (_queue[1]._rep >= 5) {
+ _queue[1]._rep -= 5;
+ d3 = 8 - d3; // Swap 2 and 6
+ }
+ if (_queue[1]._code == 0) {
+ i = _queue[1]._rep;
+ if (i != 0) {
+ do {
+ --i;
+ entroct(d3);
+ entroct(_queue[1]._val);
+ entroct(3);
+ } while (i > 0);
+ }
+ entroct(d3);
+ entroct(_queue[1]._val);
+ entroct(4);
+ } else {
+ entroct(d3);
+ entroct(_queue[1]._val);
+ entroct(3);
+ i = _queue[1]._rep;
+ if (i != 0) {
+ do {
+ --i;
+ entroct(d3);
+ entroct(_queue[1]._val);
+ entroct(4);
+ } while (i > 0);
+ }
+ }
+ if (_queue[2]._code == 9) {
+ entroct(d3);
+ entroct(_queue[1]._val);
+ entroct(5);
+ } else if ((_queue[2]._code != 0) && (_queue[2]._code != 1) && (_queue[2]._code != 4)) {
+ veracf(_queue[2]._acc);
+ switch (_queue[2]._code) {
+ case 3:
+ d2 = 8;
+ break;
+ case 6:
+ d2 = _tabdph[(_queue[2]._val - 14) << 1];
+ break;
+ case 5:
+ d2 = _queue[2]._val;
+ break;
+ default:
+ d2 = 7;
+ break;
+ } // switch c3._code
+ if (d2 == 4)
+ d2 = 3;
+
+ if (_intcon[_queue[1]._val] != 0)
+ ++_queue[1]._val;
+
+ if ((_queue[1]._val == 17) || (_queue[1]._val == 18))
+ _queue[1]._val = 16;
+
+ entroct(4);
+ entroct(d2);
+ entroct(_queue[1]._val);
+ }
+
+ break;
+ case 4:
+ veracf(_queue[1]._acc);
+ i = _queue[1]._rep;
+ if (i != 0) {
+ do {
+ --i;
+ entroct(2);
+ entroct(_queue[1]._val);
+ entroct(3);
+ } while (i > 0);
+ }
+ entroct(2);
+ entroct(_queue[1]._val);
+ entroct(4);
+ if (_queue[2]._code == 9) {
+ entroct(2);
+ entroct(_queue[1]._val);
+ entroct(5);
+ } else if ((_queue[2]._code != 0) && (_queue[2]._code != 1) && (_queue[2]._code != 4)) {
+ veracf(_queue[2]._acc);
+ switch (_queue[2]._code) {
+ case 3:
+ d2 = 8;
+ break;
+ case 6:
+ d2 = _tabdph[(_queue[2]._val - 14) << 1];
+ break;
+ case 5:
+ d2 = _queue[2]._val;
+ break;
+ default:
+ d2 = 7;
+ break;
+ } // switch c3._code
+
+ if (d2 == 4)
+ d2 = 3;
+
+ if (_intcon[_queue[1]._val] != 0)
+ ++_queue[1]._val;
+
+ entroct(4);
+ entroct(d2);
+ entroct(_tabdbc[((_queue[1]._val - 26) << 1) + 1]);
+ }
+
+ break;
+ default:
+ break;
+ } // switch c2.code
+}
+
+/**
+ * Make the queue evolve by 1 value
+ * @remarks Originally called 'rot_chariot'
+ */
+void SpeechManager::moveQueue() {
+ _queue[0] = _queue[1];
+ _queue[1] = _queue[2];
+ _queue[2]._val = 32;
+ _queue[2]._code = 9;
+}
+
+/**
+ * initialize the queue
+ * @remarks Originally called 'init_chariot'
+ */
+void SpeechManager::initQueue() {
+ _queue[2]._rep = 0;
+ _queue[2]._freq = 0;
+ _queue[2]._acc = 0;
+ moveQueue();
+ moveQueue();
+}
+
+/**
+ * Handle a phoneme
+ * @remarks Originally called 'trait_ph'
+ */
+void SpeechManager::handlePhoneme() {
+ const int deca[3] = {300, 30, 40};
+
+ int startPos = swap(_cfiphBuffer[_phonemeNumb - 1]) + deca[_typlec];
+ int endPos = swap(_cfiphBuffer[_phonemeNumb]) + deca[_typlec];
+ int wordCount = endPos - startPos;
+ for (int i = (uint)startPos >> 1, currWord = 0; i < (int)((uint)endPos >> 1); i++, currWord += 2)
+ WRITE_LE_UINT16(&g_vm->_mem[kAdrWord + currWord], _cfiphBuffer[i]);
+
+ _ptr_oct = 0;
+ int currWord = 0;
+ initQueue();
+
+ do {
+ moveQueue();
+ charg_car(currWord);
+ trait_car();
+ } while (currWord < wordCount);
+
+ moveQueue();
+ trait_car();
+ entroct(ord('#'));
+}
+
+/**
+ * Start speech
+ * @remarks Originally called 'parole'
+ */
+void SpeechManager::startSpeech(int rep, int ht, int typ) {
+ int savph[501];
+ int tempo;
+
+ if (g_vm->_soundOff)
+ return;
+
+ _phonemeNumb = rep;
+ int haut = ht;
+ _typlec = typ;
+ if (_typlec != 0) {
+ for (int i = 0; i <= 500; ++i)
+ savph[i] = _cfiphBuffer[i];
+ tempo = kTempoNoise;
+ } else if (haut > 5)
+ tempo = kTempoF;
+ else
+ tempo = kTempoM;
+ g_vm->_addFix = (float)((tempo - 8)) / 256;
+ cctable(_tbi);
+ switch (typ) {
+ case 1:
+ loadNoise();
+ /*if zuul then zzuul(kAdrNoise,0,1095);*/
+ regenbruit();
+ break;
+ case 2:
+ loadMusicSound();
+ loadPhonemeSounds();
+ break;
+ default:
+ break;
+ }
+ handlePhoneme();
+ g_vm->_soundManager.litph(_tbi, typ, tempo);
+ if (_typlec != 0)
+ for (int i = 0; i <= 500; ++i) {
+ _cfiphBuffer[i] = savph[i];
+ _mlec = _typlec;
+ }
+ g_vm->setPal(g_vm->_numpal);
+}
+
+} // End of namespace Mortevielle
diff --git a/engines/mortevielle/speech.h b/engines/mortevielle/speech.h
new file mode 100644
index 0000000000..c27f2bfc15
--- /dev/null
+++ b/engines/mortevielle/speech.h
@@ -0,0 +1,99 @@
+/* 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
+ */
+
+#ifndef MORTEVIELLE_PAROLE_H
+#define MORTEVIELLE_PAROLE_H
+
+#include "common/scummsys.h"
+#include "mortevielle/sound.h"
+
+namespace Mortevielle {
+
+const int kAdrNoise = 0x5cb0;/*2C00;*/
+const int kAdrNoise1 = 0x6924;
+const int kAdrNoise3 = 0x6ba6;/*3AF6;*/
+const int kAdrNoise5 = 0x3b50;
+const int kAdrTroct = 0x406b;
+const int kAdrWord = 0x4000;
+const int kOffsetB1 = 6;
+const int kOffsetB3 = 6;
+
+const float freq0 = 1.19318e6;
+const int kNullValue = 255;
+const int kTempoMusic = 71;
+const int kTempoNoise = 78;
+const int kTempoF = 80;
+const int kTempoM = 89;
+
+// Useless constants
+//const int segdon = 0x6c00;
+//const int adbruit2 = 0x6b30;/*3A80;*/
+//const int adson2 = 0x60b0;/*3000;*/
+//const int seg_syst = 0x6fed;
+//const int offsetb2 = 4;
+
+struct SpeechQueue {
+ int _val;
+ int _code;
+ int _acc;
+ int _freq;
+ int _rep;
+};
+
+class SpeechManager {
+private:
+ int _typlec;
+ int _phonemeNumb;
+
+ SpeechQueue _queue[3];
+ int _ptr_oct;
+
+public:
+ int16 *_cfiphBuffer;
+ int _tbi[256];
+ int _mlec;
+
+ SpeechManager();
+ void spfrac(int wor);
+ void charg_car(int &currWordNumb);
+ void entroct(byte o);
+ void veracf(byte b);
+ void cctable(tablint &t);
+ void regenbruit();
+ void loadMusicSound();
+ void loadPhonemeSounds();
+ void loadNoise();
+ void trait_car();
+
+ void moveQueue();
+ void initQueue();
+ void handlePhoneme();
+ void startSpeech(int rep, int ht, int typ);
+};
+
+} // End of namespace Mortevielle
+#endif