aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--devtools/create_mortdat/create_mortdat.cpp163
-rw-r--r--devtools/create_mortdat/create_mortdat.h97
-rw-r--r--devtools/create_mortdat/enginetext.h189
-rw-r--r--devtools/create_mortdat/gametext.h1794
-rw-r--r--devtools/create_mortdat/module.mk11
-rw-r--r--devtools/extract_mort/extract_mort.cpp387
-rw-r--r--devtools/extract_mort/module.mk11
-rw-r--r--dists/engine-data/mort.datbin0 -> 79412 bytes
-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
31 files changed, 13598 insertions, 0 deletions
diff --git a/devtools/create_mortdat/create_mortdat.cpp b/devtools/create_mortdat/create_mortdat.cpp
new file mode 100644
index 0000000000..6fe115d5c4
--- /dev/null
+++ b/devtools/create_mortdat/create_mortdat.cpp
@@ -0,0 +1,163 @@
+/* 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 is a utility for extracting needed resource data from different language
+ * version of the Lure of the Temptress lure.exe executable files into a new file
+ * lure.dat - this file is required for the ScummVM Lure of the Temptress module
+ * to work properly
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+// HACK to allow building with the SDL backend on MinGW
+// see bug #1800764 "TOOLS: MinGW tools building broken"
+#ifdef main
+#undef main
+#endif // main
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common/endian.h"
+#include "create_mortdat.h"
+#include "enginetext.h"
+#include "gametext.h"
+
+/*-------------------------------------------------------------------------*/
+
+void openOutputFile(const char *outFilename) {
+ outputFile.open(outFilename, kFileWriteMode);
+
+ // Write header
+ outputFile.write("MORT", 4);
+ outputFile.writeByte(VERSION_MAJOR);
+ outputFile.writeByte(VERSION_MINOR);
+}
+
+/**
+ * Write out the data for the font
+ */
+void writeFontBlock() {
+ const int knownAddr[2] = {0x36b0, 0x36c0};
+ byte checkBuffer[7];
+ byte fontBuffer[121 * 6];
+
+ // Move to just prior the font data and verify that we're reading the known mort.com
+ for (int i = 0; i <= 2; ++i) {
+ if ( i == 2) {
+ printf("Invalid mort.com input file");
+ exit(0);
+ }
+
+ mortCom.seek(knownAddr[i]);
+ mortCom.read(checkBuffer, 7);
+
+ if ((checkBuffer[0] == 0x59) && (checkBuffer[1] == 0x5B) && (checkBuffer[2] == 0x58) ||
+ (checkBuffer[3] == 0xC3) && (checkBuffer[4] == 0xE8) && (checkBuffer[5] == 0xD6) ||
+ (checkBuffer[6] == 0x02)) {
+ break;
+ }
+ }
+
+ // Read in the data
+ mortCom.read(fontBuffer, 121 * 6);
+
+ // Write out a section header to the output file and the font data
+ const char fontHeader[4] = { 'F', 'O', 'N', 'T' };
+ outputFile.write(fontHeader, 4); // Section Id
+ outputFile.writeWord(121 * 6); // Section size
+
+ outputFile.write(fontBuffer, 121 * 6);
+}
+
+void writeStaticStrings(const char **strings, DataType dataType, int languageId) {
+ // Write out a section header
+ const char sStaticStrings[4] = { 'S', 'S', 'T', 'R' };
+ const char sGameStrings[4] = { 'G', 'S', 'T', 'R' };
+
+ if (dataType == kStaticStrings)
+ outputFile.write(sStaticStrings, 4);
+ else if (dataType == kGameStrings)
+ outputFile.write(sGameStrings, 4);
+
+ // Figure out the block size
+ int blockSize = 1;
+ const char **s = &strings[0];
+ while (*s) {
+ blockSize += strlen(*s) + 1;
+ ++s;
+ }
+
+ outputFile.writeWord(blockSize);
+
+ // Write out a byte indicating the language for this block
+ outputFile.writeByte(languageId);
+
+ // Write out each of the strings
+ s = &strings[0];
+ while (*s) {
+ outputFile.writeString(*s);
+ ++s;
+ }
+}
+
+/**
+ * Write out the strings previously hard-coded into the engine
+ */
+void writeEngineStrings() {
+ writeStaticStrings(engineDataEn, kStaticStrings, 1);
+ writeStaticStrings(engineDataFr, kStaticStrings, 0);
+ writeStaticStrings(engineDataDe, kStaticStrings, 2);
+}
+
+/**
+ * Write out the strings used in the game
+ */
+void writeGameStrings() {
+ writeStaticStrings(gameDataEn, kGameStrings, 1);
+ writeStaticStrings(gameDataFr, kGameStrings, 0);
+ writeStaticStrings(gameDataDe, kGameStrings, 2);
+}
+
+void process() {
+ writeFontBlock();
+ writeGameStrings();
+ writeEngineStrings();
+}
+
+/**
+ * Main method
+ */
+int main(int argc, char *argv[]) {
+ if (argc != 2) {
+ printf("Usage:\n%s input_filename\nWhere input_filename is the name of the Mortevielle DOS executable", argv[0]);
+ exit(0);
+ }
+
+ mortCom.open(argv[1], kFileReadMode);
+ openOutputFile(MORT_DAT);
+
+ process();
+
+ mortCom.close();
+ outputFile.close();
+}
diff --git a/devtools/create_mortdat/create_mortdat.h b/devtools/create_mortdat/create_mortdat.h
new file mode 100644
index 0000000000..0aa6b529df
--- /dev/null
+++ b/devtools/create_mortdat/create_mortdat.h
@@ -0,0 +1,97 @@
+/* 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 is a utility for extracting needed resource data from different language
+ * version of the Mortevielle executable files into a new file mort.dat - this
+ * is required for the ScummVM Mortevielle module to work properly
+ */
+
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 0
+
+enum AccessMode {
+ kFileReadMode = 1,
+ kFileWriteMode = 2
+};
+
+enum DataType {
+ kStaticStrings = 0,
+ kGameStrings = 1
+};
+
+#define MORT_DAT "mort.dat"
+
+class File {
+private:
+ FILE *f;
+public:
+ bool open(const char *filename, AccessMode mode = kFileReadMode) {
+ f = fopen(filename, (mode == kFileReadMode) ? "rb" : "wb");
+ return (f != NULL);
+ }
+ void close() {
+ fclose(f);
+ f = NULL;
+ }
+ int seek(int32 offset, int whence = SEEK_SET) {
+ return fseek(f, offset, whence);
+ }
+ long read(void *buffer, int len) {
+ return fread(buffer, 1, len, f);
+ }
+ void write(const void *buffer, int len) {
+ fwrite(buffer, 1, len, f);
+ }
+ byte readByte() {
+ byte v;
+ read(&v, sizeof(byte));
+ return v;
+ }
+ uint16 readWord() {
+ uint16 v;
+ read(&v, sizeof(uint16));
+ return FROM_LE_16(v);
+ }
+ uint32 readLong() {
+ uint32 v;
+ read(&v, sizeof(uint32));
+ return FROM_LE_32(v);
+ }
+ void writeByte(byte v) {
+ write(&v, sizeof(byte));
+ }
+ void writeWord(uint16 v) {
+ uint16 vTemp = TO_LE_16(v);
+ write(&vTemp, sizeof(uint16));
+ }
+ void writeLong(uint32 v) {
+ uint32 vTemp = TO_LE_32(v);
+ write(&vTemp, sizeof(uint32));
+ }
+ void writeString(const char *s) {
+ write(s, strlen(s) + 1);
+ }
+ uint32 pos() {
+ return ftell(f);
+ }
+};
+
+File outputFile, mortCom;
+
diff --git a/devtools/create_mortdat/enginetext.h b/devtools/create_mortdat/enginetext.h
new file mode 100644
index 0000000000..c4929141d3
--- /dev/null
+++ b/devtools/create_mortdat/enginetext.h
@@ -0,0 +1,189 @@
+/* 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 is a utility for extracting needed resource data from different language
+ * version of the Mortevielle executable files into a new file mort.dat - this
+ * is required for the ScummVM Mortevielle module to work properly
+ */
+
+#ifndef ENGINEDATA_H
+#define ENGINEDATA_H
+
+const char *engineDataEn[] = {
+ "[2][ ][YES][NO]",
+ "Go to",
+ "Someone enters, looks surised, but says nothing",
+ " Cool ",
+ "Oppressive",
+ " Tense ",
+ "Idem",
+ "You",
+ "are",
+ "Alone",
+
+ "Gosh! You hear some noise...",
+ " | You should have noticed, | ",
+ "% of hints...",
+ "Do you want to wake up?",
+ "OK",
+ "",
+ " Save",
+
+ " Load",
+ " Restart",
+ "F3: Repeat",
+ "F8: Proceed",
+ "Hide self",
+ "take",
+ " probe ",
+ " raise ",
+ " -MORE- ",
+ " -STOP- ",
+ "[1] [So, use the DEP menu] [Ok]",
+ "lift",
+ "read",
+
+ "look",
+ "search",
+ "open",
+ "put",
+ "turn",
+ "tie",
+ "close",
+ "hit",
+ "pose",
+ "smash",
+
+ "smell",
+ "scratch",
+ "probe",
+ "[1] [ | Before, use the DEP menu...] [Ok]",
+ "& day",
+ NULL
+};
+
+const char *engineDataFr[] = {
+ "[2][ ][OUI][NON]",
+ "aller",
+ "quelqu'un entre, parait ‚tonn‚ mais ne dit rien",
+ "Cool",
+ " Lourde ",
+ "Malsaine",
+ "Idem",
+ "Vous",
+ "ˆtes",
+ "SEUL",
+
+ "Mince! Vous entendez du bruit...",
+ " | Vous devriez avoir remarqu‚| ",
+ "% des indices...",
+ "D‚sirez-vous vous r‚veiller?",
+ "OK",
+ "",
+ " Sauvegarde",
+
+ " Chargement",
+ " Recommence ",
+ "F3: Encore",
+ "F8: Suite",
+ "se cacher",
+
+ "prendre",
+ "sonder",
+ "soulever",
+ " -SUITE- ",
+ " -STOP- ",
+ "[1][Alors, utilisez le menu DEP...][ok]",
+ "soulever",
+ "lire",
+
+ "regarder",
+ "fouiller",
+ "ouvrir",
+ "mettre",
+ "tourner",
+ "attacher",
+ "fermer",
+ "frapper",
+ "poser",
+ "d‚foncer",
+
+ "sentir",
+ "gratter",
+ "sonder",
+ "[1][ | Avant, utilisez le menu DEP...][ok]",
+ "& jour",
+ NULL
+};
+
+const char *engineDataDe[] = {
+ "[2][ ][JA][NEIN]",
+ "Go to",
+ "Someone enters, looks surised, but says nothing",
+ "Cool",
+ "Schwer",
+ "Ungesund",
+ "Idem",
+ "You",
+ "are",
+ "Alone",
+
+ "Verdammt! Sie hoeren ein Geraeush...",
+ "Sie haetten ",
+ "% der Hinweise| bemerken muessen...",
+ "Do you want to wake up?",
+ "OK",
+ "",
+ " schreiben",
+
+ " lesen",
+ " Restart ",
+ "F3 nochmals",
+ "F8: stop",
+ "Hide self",
+ "take",
+ " probe ",
+ " raise ",
+ " -WEITER- ",
+ " -STOP- ",
+ "[1][ Benutzen Sie jetzt das Menue DEP...][OK]",
+ "lift",
+ "read",
+
+ "look",
+ "search",
+ "open",
+ "put",
+ "turn",
+ "tie",
+ "close",
+ "hit",
+ "pose",
+ "smash",
+
+ "smell",
+ "scratch",
+ "probe",
+ "[1][ Benutzen Sie jetzt das Menue DEP...][OK]",
+ "& tag",
+ NULL
+};
+
+#endif
diff --git a/devtools/create_mortdat/gametext.h b/devtools/create_mortdat/gametext.h
new file mode 100644
index 0000000000..0e0948e95f
--- /dev/null
+++ b/devtools/create_mortdat/gametext.h
@@ -0,0 +1,1794 @@
+/* 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 is a utility for extracting needed resource data from different language
+ * version of the Mortevielle executable files into a new file mort.dat - this
+ * is required for the ScummVM Mortevielle module to work properly
+ */
+
+#ifndef GAMEDATA_H
+#define GAMEDATA_H
+
+const char *gameDataEn[] = {
+ "Calm within the storm$",
+ "Discussed in colours$",
+ "Your mauve!$",
+ "Be kind enough to leave the room...$",
+ "If you're NOT overdrawn...$",
+ "If you're feeling blue...$",
+ "Read what's on the walls?$",
+ "Water sports$",
+ "Room for envy?$",
+ "A glance at the forbidden$",
+ "Smell of a woodfire and tobacco$",
+ "Tobacco and old books$",
+ "Onions, cinnamon and spirits$",
+ "A place seldom visited$",
+ "Humidity and decay$",
+ "Sorry, no ""door to door""$",
+ "Rotting corpse: deady cryptomania$",
+ "And what's more, there are disused traps$",
+ "It's already open$",
+ "Danger: avalanches$",
+ "Proper Charlie's place?$",
+ "An imposing building$",
+ "The other side of the mystery$",
+ "Strange horoscope$",
+ "Look out... but she wishes well?$",
+ "An oak door$",
+ "A photograph$",
+ "The coat of arms$",
+ "$",
+ "Max, the servant, welcomes you and shows you to your room$",
+ "Mortville 6/2/51@ My dear Jerome@Regarding my telegram, I must tell you the reason for my wor-@ries. A year ago, Murielle, my lady companion, disappeared. The de@part may have had something to do@with the financial success of themanor, or... A silence hard to un@derstand for my son Guy. Not ha@ving been able to see the light of day over this affair, I count @on you to sort things out. If my state of health doesn't improve, @take the decisions that you feel @are apropriate.@ Kind regards, Julia DEFRANCK$",
+ "Later, Guy will inform you of Leo's suicide after a@heavy bet at the races$",
+ "F3: AGAIN F8: STOP$",
+ "The master of the premises$",
+ "The future heir$",
+ "JULIA's son$",
+ "A pretty picture$",
+ "Superman!$",
+ "Ida's husband$",
+ "Interesting remarks?$",
+ "Service included!$",
+ "Nothing underneath$",
+ "You could hear a pin drop$",
+ "Half an hour passes: nothing! Wait any longer?$",
+ "Admire! Contemplate!$",
+ "No! Nothing!$",
+ "Impossible$",
+ "That stains!$",
+ "A treatise on the history of the area$",
+ "A few coins...$",
+ "First commandment...$",
+ "Pleasing to the nostrils!$",
+ "Spades, Hearts...$",
+ "Just a spoonful of sugar...$",
+ "A romantic novel$",
+ "Worth more than a penny, (whistle)$",
+ "Just needs a little patience$",
+ "Watch the sharp bends$",
+ "Deep and dark$",
+ "Normal sensations$",
+ "Sniff!$",
+ "Not discreet! Be content to watch!$",
+ "Bless you! Dusty!$",
+ "The canvas is signed, the wallpaper is not!$",
+ "Nothing, Unlucky!$",
+ "Be more discreet!$",
+ "The shutters are closed$",
+ "Snow! And more snow!$",
+ "Brilliant! The work of a master!$",
+ "No doubt at all! A genuine fake!$",
+ "Hmm! A cheap reproduction!$",
+ "A rare and valuable piece$",
+ "Nothing special$",
+ "Linen, personal belongings...$",
+ "Not just anywhere!$",
+ "It's not time!$",
+ "One doesn't speak with ones mouth full!$",
+ "Someone comes in, messes about then goes out again$",
+ "Someone's approaching your hiding-place$",
+ "Someone surprises you!$",
+ "Impossible! You're too loaded!$",
+ "Try again!$",
+ "Still puzzled!?$",
+ "You leave Mortville.In Paris a message awaits you...$",
+ "You hurt yourself$",
+ "Nothing more here$",
+ "The sound seems normal$",
+ "It doesn't move$",
+ "You are answered$",
+ "Not the right moment!$",
+ "The same matter, from another angle!$",
+ "The reflection is tarnished, but the frame is gold!$",
+ "Bric-a-brac$",
+ "Face to face with failure!$",
+ "Smells like something you'd rather not see!$",
+ "Cleaning products$",
+ "Got an itch?$",
+ "It's stuck, frozen. Brrr!$",
+ "All the locks are jammed!$",
+ "Papers$",
+ "No! Father christmas hasn't got himself stuck!$",
+ "It leads onto a corridor$",
+ "China, silverware...$",
+ "No! It's not Julia's remains!$",
+ "An old engraving$",
+ "You find a deep diamond-shaped opening$",
+ "The wall slides open! A passage! Do you follow it?$",
+ "The passageway closes$",
+ "A secret drawer: a notebook! Do you read it?$",
+ "The drawer shuts$",
+ "Nothing! Flesh and blood stuck to the stone$",
+ "Certain details lead you to believe death was not immediate!$",
+ "A rotten affair!$",
+ "Did she cling to dear life with just one finger?$",
+ "Has the treasure packed its trunk?$",
+ "A slot the size of a coin$",
+ "Part of the stone wall pivots.A crypt! Do you enter?$",
+ "The ring turns, the wall closes$",
+ "A stone column behind the altar$",
+ "There is a noise!$",
+ "Occupied!$",
+ "Take another chance?$",
+ "Too deep!$",
+ "The cellar wall pivots$",
+ "Nothing$",
+ "The one and only!$",
+ "The object slides to the bottom$",
+ "You have nothing in hand$",
+ "It is not open$",
+ "There is already something$",
+ "The door is locked$",
+ "No reply$",
+ "A solid wooden ball$",
+ "There's no more space$",
+ "A wooden ball pierced through the side$",
+ "? ?$",
+ "Your move$",
+ "OK !$",
+ "Suddenly Max arrives with your suitcase: \"Thank you for your @visit!\".Mister discreet \"private eye\" (in need of a private optici@an!). Thoroughly demoralised, you@leave the manor. You are useless!$",
+ "Leo interrupts: \"The storm has died down,I am going into town in@1 hour. Get ready\". You have lost@time...but not your life$",
+ "Congestion, the deadly flu... You@are stuck here! Your whole case@sinks slowly beneath the water$",
+ "The water is rising fast,freezing your last illusions. Before you@have time to react...you are dead$",
+ "As soon as you reach the bottom of the well, a hand cuts the rope@Farewell sweet life!$",
+ "The storm covers your footprints.A wall of silence falls heavily@on your shoulders. Slowly you succumb to frosbite...$",
+ "You're not completely alone! A cold blade plunges into your backup@In future, be more care!$",
+ "You don't know what implication Leo may have had in Murielle's@death. Was she dead outright? In@any case,the family problems thatyou have uncovered in the course@of your enquiries would explain Leo's behaviour. You're not sure@that's the reason Julia had asked@for your help, but that's reason enough for you!Out of respect for@her, after taking certain precau-@tions you have a revealing talk with Leo.$",
+ "$",
+ "You don't have the keys to the manor. Your cries rest unheard@You're going to catch... your death!$",
+ "With a circular movement, the sword slices across you. Guts and@intestines spill out all over. A sorry state of affairs!$",
+ "Home, Sweet home !$",
+ "The mystery behind a closed door$",
+ "Bewitching charm of these old rooms$",
+ "An empty stomach$",
+ "Closer to heaven? Not so sure$",
+ "Afraid of the dark?$",
+ "Old rugs and a glint of gold$",
+ "Anguish!$",
+ "Safe? Perhaps not!$",
+ "A little ill at ease, eh!?$",
+ "Always further$",
+ "Your way of the cross!$",
+ "On the trail of...$",
+ "Watch what's hiding$",
+ "The road down to hell$",
+ "Feeling well? You look a little pale$",
+ "What lurks behind...?$",
+ "Close-up on:$",
+ "You notice, amongst other things$",
+ "And...$",
+ "That's all!$",
+ "A bit of reading$",
+ "The adventure awaits, you set off!$",
+ "Don't mess up YOUR next ADVENTURE!$",
+ "I don't understand$",
+ "There is an easier way$",
+ "No, not just now$",
+ "Too late$",
+ "$",
+ "Like a deep stony stare, a solitary eye that points towards the@stars; the artery that links hea-ven and hell. You must fathom@these depths keeping hold of that@which is, and will become. Monday, Tuesday, Wednesday, Sunday, from@Monday 1st to Sunday 1st,plunging from one day to the next your@\"IS\" or \"WILL BECOME\". Carrying your burden with love and light,@the smallest oversight will seal your fate.$",
+ "10/1/51: We think we've solved the mystery of the manuscript and@located the crypt. Is it the idea@of success in what seems like a dream that disturbs me so? I feel@I have committed myself rather too much, as far as Leo is concer@ned... No! I should go on. @I should have put Guy in the pic-ture but for a week now, I've had@no news of him$",
+ "Take your prayers as you would to the holy place. From the pillar@of wisdom, bring the sun to his@knees. Thus will it show you the place to offer alms of another@kind and like young Arthur, open the way of darkness.White is your@colour, golden your hearth. So@advance with caution Orpheus and light your way unto the sad@virgin. Offer her the circle of the man with three faces. That he@may regain the world and turn with it to its original@inglory!$",
+ "The mountains are the fangs in a monstrous mouth opening on the@finity of a celestial orgy, grin-ding the stars as we grind our@teeth into dust. You will drop your chord of stone at your feet.@The laugh of silence at the@highest pitch, and in your right hand, the measure of genius. Thus@will you pass between the two crescents beyond the abyss of the@wall of silence. The key to the melody is within your grasp. It@suffices to find the note that clashes.$",
+ " 9/12 INTER. 518 3/13 EXPENS. 23@ 9/12 SALES 1203 7/12 CHEQUE 1598@ TOTAL 1721 TOTAL 1721$",
+ " 5/1/51@@ Luc, my love@ Guy knows about us. After an argument I told him everything! I@think only of you. Max keeps pes-tering me, but it's finished with @him. He should stick to his pots and pans! When can you and I be alone together? For you I would@get a divorce.@I love you.@ Eva$",
+ " Mortville, 10/2/51@@ Pat@ I recall you owe me 50000 frs that I lent you for your business@I need that money, can you repay me quickly?@ Guy$",
+ " Mortville, 15/2/51@ Dear Sir@ I am writing to you on the sub-ject of our business deal. I have@decided to go all the way in the certainty that my partner, Pat@DEFRANCK, has been forging the accounts. @In spite of$",
+ " A pipe$",
+ " A pen$",
+ " A lighter$",
+ " A retort$",
+ " A shaving brush$",
+ " A tin of paint$",
+ " A flute$",
+ " An expensive ring$",
+ " A reel of thread$",
+ " An old book$",
+ " A wallet$",
+ " A dagger$",
+ " A pistol$",
+ " A bible$",
+ " A candle$",
+ " A jewellery box$",
+ " An iron$",
+ " A photo$",
+ " A pocket watch$",
+ " A rope$",
+ " Keys$",
+ " A pearl necklace$",
+ " A bottle of perfume$",
+ " Binoculars$",
+ " Glasses$",
+ " A leather purse$",
+ " A tennis ball$",
+ " Ammunition$",
+ " A cut-throat razor$",
+ " A hairbrush$",
+ " A clothes brush$",
+ " A pack of cards$",
+ " A shoe horn$",
+ " A screwdriver$",
+ " A hammer$",
+ " Keys$",
+ " Keys$",
+ " An ashtray$",
+ " A paintbrush$",
+ " A rope$",
+ " A wooden object$",
+ " Sleeping pills$",
+ " A gold ring$",
+ " A jewellery box$",
+ " An alarm clock$",
+ " A coat of armour$",
+ " A candlestick$",
+ " A pair of gloves$",
+ " A engraved goblet$",
+ " A parchment$",
+ " A dagger$",
+ " A dossier$",
+ " A parchment$",
+ " A parchment$",
+ " A dossier$",
+ " A dossier$",
+ " A letter$",
+ " A novel$",
+ " A wooden rod$",
+ " An envelope$",
+ " A letter$",
+ " An envelope$",
+ "Julia$",
+ "Julia's death$",
+ "Julia's relationships$",
+ "A message from Julia$",
+ "Julia's inheritance$",
+ "Julia's final actions$",
+ "Julia's gifts$",
+ "Julia's bedroom$",
+ "The photo at Julia's home$",
+ "Julia and yourself...$",
+ "L‚o's occupations$",
+ "Pat's occupations$",
+ "Guy's occupations$",
+ "Bob's occupations$",
+ "Eva's occupations$",
+ "Luc's occupations$",
+ "Ida's occupations$",
+ "Max's occupations$",
+ "Your occupations$",
+ "L‚o's relationships$",
+ "Pat's relationships$",
+ "Guy's relationships$",
+ "Bob's relationships$",
+ "Eva's relationships$",
+ "Luc's relationships$",
+ "Ida's relationships$",
+ "Max's relationships$",
+ "Your relationships$",
+ "Murielle$",
+ "Murielle's relationships$",
+ "Murielle and yourself...$",
+ "Murielle's disappearance$",
+ "The wall of silence$",
+ "The manuscripts$",
+ "The coat of arms$",
+ "Engravings in the cellar$",
+ "The well$",
+ "The secret passages$",
+ "The chapel$",
+ "The paintings$",
+ "The photo of the attic$",
+ "The body in the crypt$",
+ "$",
+ "$",
+ "END OF THE CONVERSATION$",
+ "TBT - Les vieux appelaient ainsi la chaine de montagne qui se dresse au pied du manoir !$",
+ "TBT - C'est le massif montagneux que l'on aper‡oit devant le manoir$",
+ "TBT - Je n'en sais rien !$",
+ "TBT - Elle est morte d'une embolie pulmonaire$",
+ "TBT - Ma m‚re est morte soudainement . Son ‚tat semblait pourtant s'ˆtre am‚lior‚$",
+ "TBT - Madame DEFRANCK est morte d'un coup de froid$",
+ "TBT - Elle est morte d'une embolie pulmonaire$",
+ "TBT - Pardonnez moi mais je pr‚fŠre, actuellement garder le silence$",
+ "TBT - Ce sont toujours les meilleurs qui partent les premiers$",
+ "TBT - J'aimais beaucoup ma mŠre . Je regrette seulement qu'elle soit morte dans le manoir des DEFRANCK$",
+ "TBT - C'est une r‚gion qui a un pass‚ charg‚ et j'ai largement de quoi m'occuper . Et puis j'aime beaucoup les chevaux..$",
+ "TBT - C'est un passionn‚ d'histoire et un joueur inv‚t‚r‚ . D'ailleurs, voici un an il a gagn‚ une grosse somme$",
+ "TBT - Il a d‚j… beaucoup a faire avec la gestion et l'entretien du manoir ...$",
+ "TBT - Je suis PDG d'une petite soci‚t‚ de parfums . Mais quand je suis ici, je me repose$",
+ "TBT - C'est un homme dynamique qui a r‚ussi dans le parfum$",
+ "TBT - Lui ! C'est un arriviste v‚reux ! Les parfums ont du endormir son bon sens . D'ailleurs ici il passe ses soir‚es dans sa chambre$",
+ "TBT - J'ai ‚t‚ trŠs pr‚occup‚ par la sant‚ de ma mŠre, et maintenant je n'ai plus go–t … rien$",
+ "TBT - Il aurait mieux fait de s'occuper un peu plus de moi et un peu moins de sa mŠre$",
+ "TBT - Ce sont ses affaires ...$",
+ "TBT - Il n'a pas trop de chance en ce moment bien que ses affaires soient satisfaisantes$",
+ "TBT - Je travaille avec Pat mais ‡a ne va pas trŠs fort en ce moment$",
+ "TBT - Ah oui ?! Il a des occupations ? Il ferait bien de s'en occuper s‚rieusement alors$",
+ "TBT - Lui et Pat sont associ‚s . Je crois que ‡a ne va pas trop mal$",
+ "TBT - Je m'occupe de moi et c'est d‚j… beaucoup . Et vous ?$",
+ "TBT - Oh ‡a ! Je lui fais confiance . Elle sait s'occuper$",
+ "TBT - Mais ! Vous n'avez pas encore d‚couvert son occupation principale ..?$",
+ "TBT - Elle fait dans la d‚coration avec beaucoup dego–t d'ailleurs . Elle est toujours trŠs bien habill‚e$",
+ "TBT - Si les bijoux vous interessent, j'ai quelques affaires interessantes … saisir rapidement$",
+ "TBT - Les bijoux ...$",
+ "TBT - Je ne sais pas, mais j'aimerais bien qu'il s'occupe un peu moins de mes affaires !$",
+ "TBT - Quand on est une femme d'int‚rieur on trouve toujours de quoi s'occuper...$",
+ "TBT - Elle pourrait rester sans rien faire, mais non ! Elle coud, elle lit ...$",
+ "TBT - Elle n'a s–rement pas des occupations trŠs ‚panouissantes ...$",
+ "TBT - Une femme comme il n'y en a plus : Elle s'interesse a tout !$",
+ "TBT - Entre la cuisine et le m‚nage, je n'ai pas beaucoup de temps … vous accorder$",
+ "TBT - Je ne sais pas comment il s'y prend pour tout faire . C'est merveilleux !$",
+ "TBT - Il en ferait plus si il s'occupait moins des rag“ts et de la bouteille$",
+ "TBT - Je suis trŠs ind‚pendant . Tant qu'on ne s'occupe pas de mes affaires : Pas de problŠme$",
+ "TBT - C'est un ‚go‹ste . Je me demande si il aime autre chose que ses chevaux et ses grimoires$",
+ "TBT - Je crois qu'il s'entend bien avec tout le monde, mis … part, peut ˆtre, avec Guy$",
+ "TBT - C'est un homme de caractŠre . Il faut savoir le prendre ..$",
+ "TBT - Les affaires sont les affaires . Quant … la famille, je la laisse pour ce qu'elle est ...$",
+ "TBT - Relations ? Relations amicales ? Relations financiŠres sans doute$",
+ "TBT - Moi je n'ai rien … lui reprocher$",
+ "TBT - C'est un homme d'affaire d‚brouillard . Il nage parfois … contre-courant mais ... il s'en sortira toujours$",
+ "TBT - Ils m'ennuient tous .. Non ! Ce n'est mˆme pas ‡a .. Quoique .. certains ..$",
+ "TBT - A l'inverse de sa mŠre, c'est une personne trŠs renferm‚e ! Alors question relations ..$",
+ "TBT - Il doit sans doute faire beaucoup d'effort pour rester agr‚able malgr‚ tous ses ennuis$",
+ "TBT - Ses relations amoureuses : C'est termin‚ . Ses relations avec moi : Pas vraiment commenc‚es . Quant aux autres : Je ne suis pas les \"autres\"$",
+ "TBT - J'aime bien tout le monde, tant qu'on ne m'escroque pas$",
+ "TBT - Il ne suffit pas d'avoir un peu d'argent et d'ˆtre beau parleur pour plaire … tout le monde$",
+ "TBT - Sans histoire .. C'est quelqu'un d'agr‚able et g‚n‚reux . De plus, il ne manque pas d'humour$",
+ "TBT - Actuellement je m'entends plut“t bien avec tout le monde . Mais, ici, je ne vais pas m'‚tendre sur le sujet$",
+ "TBT - Beau plumage, mais ‡a ne vole pas haut ... Parlez en … son mari$",
+ "TBT - C'est pour un rendez-vous ?$",
+ "TBT - Elle est trŠs vivante ! Elle ne s'embarrasse pas de pr‚jug‚s stupides$",
+ "TBT - Dans mon m‚tier, on c“toit surtout des belles femmes et des truands$",
+ "TBT - La seule valeur s–re chez lui, c'est ses bijoux .. Et sa femme, mais ‡a il ne s'en rend pas compte$",
+ "TBT - C'est quelqu'un d'interessant . De pas toujours facile … comprendre, mais qui m‚rite le d‚tour$",
+ "TBT - Je ne d‚teste personne, mais j'aime les choses et les gens quand ils sont … leur place$",
+ "TBT - C'est entre nous . Mais voyez : quand je parle avec elle, je me sens vite … l'‚troit !$",
+ "TBT - Pour ne pas s'entendre avec elle, faut y mettre de la mauvaise volont‚$",
+ "TBT - Vous savez dans mon m‚tier on entend tout mais on ne retient rien, et le service est bien fait$",
+ "TBT - C'est un hypocrite, un larbin ! Personnellement je ne lui fais pas confiance$",
+ "TBT - Je ne connait pas le fond de sa pens‚e mais c'est quelqu'un de toujours trŠs correct et impeccable$",
+ "TBT - C'‚tait une personne qui a v‚cu au manoir, il y a un an .. peut ˆtre plus$",
+ "TBT - C'‚tait plus qu'une amie pour ma mŠre . En ces moments, j'aurais aim‚ qu'elle soit … mes cot‚s$",
+ "TBT - Murielle a ‚t‚ la dame de compagnie de Julia$",
+ "TBT - Elle aussi, faisait des recherches ...$",
+ "TBT - C'‚tait une femme trŠs cultiv‚e . Son brusque d‚part, il y a un an, m'a surpris et beaucoup chagrin‚$",
+ "TBT - Elle partageait avec L‚o sa passion de l'histoire et de la r‚gion$",
+ "TBT - Je crois que tout le monde l'aimait bien$",
+ "TBT - Elle s'entendait bien avec tout le monde . Elle aimait beaucoup son fils . Quant aux relations belle-mŠre, belle-fille ..$",
+ "TBT - A part L‚o, elle avait de trŠs bon rapport avec Max ...$",
+ "TBT - Bien que vos relations furent peu soutenues, J‚r“me, elle vous portait toujours dans son coeur ...$",
+ "TBT - A part sa famille, pas grand monde$",
+ "TBT - Ah oui ! Je crois qu'elle a beaucoup regrett‚ le d‚part de cette amie .. euh ! Marielle .. ou Mireille ...$",
+ "TBT - Non rien !$",
+ "TBT - Non ... Pas que le sache$",
+ "TBT - J'ai connu Julia en achetant le manoir . C'‚tait son seul bien . Mais toute ma fortune ‚tait la sienne ...$",
+ "TBT - Si ce n'est quelques objets personnels, je crois qu'elle n'avait plus rien … elle$",
+ "TBT - Je crois que toute sa fortune venait de L‚o . Alors, Pfuuut !$",
+ "TBT - A part la lettre pour vous que j'ai post‚, rien de bien important !$",
+ "TBT - J'ai ‚t‚ trŠs heureuse qu'elle m'offre sa bible reli‚e$",
+ "TBT - Ca a ‚t‚ rapide et elle n'a pas eu le temps de prendre des dispositions particuliŠres$",
+ "TBT - Son dernier pr‚sent m'a surpris$",
+ "TBT - Quel cadeau ?$",
+ "TBT - Un chandellier ...$",
+ "TBT - Oui, j'ai eu un cadeau . Ma femme a mˆme eu une bible$",
+ "TBT - Et bien oui ! Comme tout le monde, je crois$",
+ "TBT - Un poignard$",
+ "TBT - Je n'ai jamais ‚t‚ fouiller dans le grenier !$",
+ "TBT - Vous avez un don de double-vue ou un passe-partout$",
+ "TBT - Le portrait d'une jeune fille : C'est Murielle ...$",
+ "TBT - Vous savez, je la connaissais assez peu$",
+ "TBT - Elle ‚tait trŠs charmante, mais c'‚tait surtout la dame de compagnie de Julia$",
+ "TBT - C'est la seule femme vraiment interessante que j'ai rencontr‚$",
+ "TBT - Elle avait de grandes connaissances historiques, et la consulter ‚tait trŠs enrichissant$",
+ "TBT - Je me suis toujours demand‚ ce que certains pouvaient lui trouver !$",
+ "TBT - Si la chambre est ferm‚e, demandez … L‚o$",
+ "TBT - J'ai ferm‚ sa chambre aprŠs sa mort et j'aimerais qu'il en soit ainsi encore un certain temps$",
+ "TBT - Vous savez ce que c'est : Des relations familiales$",
+ "TBT - Durant toutes ces ann‚es, je ne l'ai jamais servie … contre-coeur$",
+ "TBT - Je l'aimais autant qu'elle m'aimais, je crois$",
+ "TBT - De quel droit avez-vous p‚n‚tr‚ dans la chambre de ma femme ?!!$",
+ "TBT - C'est sans doute la photo de Murielle avec le filleul de Julia$",
+ "TBT - Je ne me rappelle pas$",
+ "TBT - C'est Murielle . C'est moi qui l'ai prise. et d'ailleurs elle est tir‚e … l'envers$",
+ "TBT - Vous ˆtes bien curieux !... C'est sans valeur$",
+ "TBT - Grimoires, parchemins et manuscrits : C'est le domaine de L‚o$",
+ "TBT - Dommage que la devise soit manquante ...$",
+ "TBT - C'est trŠs beau ... Et trŠs vieux ...$",
+ "TBT - Tiens ! C'est un endroit que je n'ai jamais visit‚$",
+ "TBT - D'apr‚s L‚o, il semblerait que les Lunes soient plus r‚centes$",
+ "TBT - Mˆme par ce temps, vous avez d‚nich‚ un soleil ...$",
+ "TBT - Profond et inqui‚tant : Le progrŠs a du bon$",
+ "TBT - Ca reste pour moi le plus grand des mystŠres$",
+ "TBT - Les derniers temps elle parlait d'un voyage . Et puis ...$",
+ "TBT - Il y a un peu plus d'un an, un soir, elle a d‚cid‚ de partir ...$",
+ "TBT - De toutes fa‡ons elle n'‚tait pas faite pour vivre ici$",
+ "TBT - Quoi ?! Quel corps ? Quel crypte ?$",
+ "TBT - Si il y en a, je ne les ai jamais trouv‚ ...$",
+ "TBT - Bien s–r ! ... Et des fant“mes aussi ...$",
+ "TBT - C'est la plus vielle de la r‚gion : Elle date du XI eme siŠcle$",
+ "TBT - Elle fut l‚gŠrement restaur‚e aprŠs la r‚volution$",
+ "TBT - Julia aimait beaucoup la peinture$",
+ "TBT - Ils ont diff‚rents styles, mais n'ont pas tous une trŠs grande valeur$",
+ "TBT - Que faites-vous l… ?$",
+ "TBT - Je suis s–r que vous cherchez quelque chose ici$",
+ "TBT - Je vous ‚coute$",
+ "TBT - Que d‚sirez-vous ?$",
+ "TBT - Oui ?$",
+ "TBT - Je suis … vous ...$",
+ "TBT - C'est pourquoi ?$",
+ "TBT - Allez-y$",
+ "TBT - C'est … quel sujet ?$",
+ "TBT - Max : … votre service, monsieur$",
+ "TBT - De toutes fa‡ons vous n'avez rien … faire ici ! Sortez !!$",
+ "TBT - Vous ˆtes trop curieux !$",
+ "TBT - J‚r“me ! Il y a longtemps ... Quelle tristesse, Julia est morte . Sa famille est ici : Guy, son fils . Eva, sa brue . L‚o, son mari bien s–r . Son beau fils, Pat . Des cousins : Bob, Ida, Luc . La tempŠte redouble, il vous faut rester . Les repas sont … 12h et 19h et il y a un recueillement … la chapelle tous les jours … 10h$",
+ "TBT - En vous voyant j'ai compris que vous decouvririez la v‚rit‚ ... Car je savais pourquoi vous veniez : J'avais retrouv‚ le brouillon de la lettre de Julia . Mais je suis trŠs joueur, alors ... Elle n'avait pas voulu que votre tƒche soit trop facile, pour me prot‚ger, sans doute, mais elle n'a pu mourir avec cette incertitude sur la conscience . Avez vous d‚couvert que le mur du silence est le nom que les ma‡ons ont donn‚ au mur qui porte ce blason, lors de la construction du manoir ? .. Et ces cadeaux que Julia a laiss‚ avant de mourir ‚taient autant de faux indices qui ne servaient qu'… faire ressortir l'importance des parchemins ... Effectivement, il y a plus d'un an, je travailais avec Murielle au d‚cryptage de ces manuscrits que je venais de trouver . Ma femme a fait la relation entre notre travail et la disparition de Murielle mais elle n'a jamais eu de preuves . Si ce n'est cette bague qu'elle a retrouv‚ un jour dans mes affaires . Une nuit, nous nous sommes aventur‚s dans le passage secret que nous avions d‚couvert . Murielle est morte par accident dans la piŠce de la vierge . J'ai r‚cup‚r‚ la bague rapidement, trouv‚ le tr‚sor et me suis enfuis . Je ne pensais pas qu'elle vivait encore, et je n'ai rien dit car j'avais besoin d'argent . J'ai fait passer cette somme sur le compte des courses de chevaux ...Partez maintenant, puisque vous n'ˆtes pas de la police . Laissez moi seul !$",
+ "TBT - F‚vrier 1951 ... Profession : detective priv‚ . Le froid figeait Paris et mes affaires lorsque ...$",
+ "TBT - Une lettre, un appel, des souvenirs d'une enfance encore proche . Que de jeux dans les piŠces d‚labr‚es du manoir de Mortevielle . Julia, une vieille femme a pr‚sent .$",
+ " to the bureau$",
+ " to the kitchen$",
+ " to the cellar$",
+ " to the landing$",
+ " outside$",
+ " to the dining room$",
+ " inside the manor$",
+ " front of the manor$",
+ " to the chapel$",
+ " to the weel$",
+ " north$",
+ " behind the manor$",
+ " south$",
+ " east$",
+ " west$",
+ " towards the manor$",
+ " further$",
+ " in the water$",
+ " out of the weel$",
+ " in the weel$",
+ " choice on screen$",
+ "In the MYSTERY series...$",
+ "MORTVILLE MANOR$",
+ "$",
+ "From an original idea of...$",
+ "Bernard GRELAUD and Bruno GOURIER$",
+ "$",
+ "Directed by: KYILKHOR CREATION and LANGLOIS$",
+ "$",
+ "With the cooperation of...$",
+ "B‚atrice et Jean_Luc LANGLOIS$",
+ "for the music and the voices,$",
+ "Bernard GRELAUD for the graphic conception,$",
+ "MARIA-DOLORES for the graphic direction,$",
+ "Bruno GOURIER for the technical direction,$",
+ "Mick ANDON for the translation. $",
+ "$",
+ "Publisher: KYILKHOR and B&JL LANGLOIS $",
+ "COPYRIGHT 1987: KYILKHOR and B&JL LANGLOIS$",
+ "$",
+ "YOUR MOVE$",
+ " attach$",
+ " wait$",
+ " force$",
+ " sleep$",
+ " listen$",
+ " enter$",
+ " close$",
+ " search$",
+ " knock$",
+ " scratch$",
+ " read$",
+ " eat$",
+ " place$",
+ " open$",
+ " take$",
+ " look$",
+ " smell$",
+ " sound$",
+ " leave$",
+ " lift$",
+ " turn$",
+ " hide yourself$",
+ " search$",
+ " read$",
+ " put$",
+ " look$",
+ " Leo$",
+ " Pat$",
+ " Guy$",
+ " Eva$",
+ " Bob$",
+ " Luc$",
+ " Ida$",
+ " Max$",
+ "JULIA...$",
+ "- Did she commit suicide?$",
+ "- Was she murdered?$",
+ "- Did she die by accident?$",
+ "- Did she die of natural causes?$",
+ "Where did the money come from@for the restoration of the manor?$",
+ "- Blackmail$",
+ "- Honest work$",
+ "- Inheritance$",
+ "- Races$",
+ "- Rents$",
+ "- Hold-up$",
+ "- Other$",
+ "What is Leo's hobby?$",
+ "- Historical research$",
+ "- Politics$",
+ "- Painting$",
+ "- Drugs$",
+ "- Occult sciences$",
+ "- Management of a sect$",
+ "Julia left several clues that are@represented in one place. Which?$",
+ "- Chapel$",
+ "- Outside$",
+ "- Cellar$",
+ "- Attic$",
+ "- Kitchen$",
+ "- Dining room$",
+ "- Julia's room$",
+ "- Leo's room$",
+ "- Pat's room$",
+ "- Bob's room$",
+ "- Max's room$",
+ "- Luc/Ida's room$",
+ "- Guy/Eva's room$",
+ "The main clue that leads you@to the underground door is:$",
+ "- A dagger$",
+ "- A ring$",
+ "- A book$",
+ "- A parchment$",
+ "- A letter$",
+ "- A pendulum$",
+ "How many parchments were there in the manor?$",
+ "- 0$",
+ "- 1$",
+ "- 2$",
+ "- 3$",
+ "- 4$",
+ "- 5$",
+ "How many persons are involved in@this story?@(Julia included, but not yourself)$",
+ "- 9$",
+ "- 10$",
+ "- 11$",
+ "What was the first name@of the unknown character?$",
+ "- Mireille$",
+ "- Fran‡oise$",
+ "- Maguy$",
+ "- Emilie$",
+ "- Murielle$",
+ "- Sophie$",
+ "Wo did Murielle have an affair with?$",
+ "- Bob$",
+ "- Luc$",
+ "- Guy$",
+ "- Leo$",
+ "- Max$",
+ "Murielle shared an occupation@with one other person. Who?$",
+ "[1][You realize that certain elements of|this investigation remain a mystery for you.|Therefore, you decide first to learn|more before undertaking new risks..][ok]$",
+ "[3][ | insert disk 1 | in drive A ][ok]$",
+ "[1][ | Disk error | All stop... ][ok]$",
+ "[1][ | You should have noticed |00% of the clues ][ok]$",
+ "[3][ | insert disk 2 | in drive A ][ok]$",
+ "TBT - [1][ |Avant d'aller plus loin, vous faites|un point sur l'‚tat de vos connaissances][ok]$",
+ "TBT - MASTER .$",
+ "TBT - rorL$",
+ NULL
+};
+
+const char *gameDataFr[] = {
+ "Le calme dans la tourmente$",
+ "Des go—ts et des couleurs!$",
+ "Mauve qui peut!$",
+ "PriŠre de laisser en sortant...$",
+ "Trou noir troublant$",
+ "Bleu... comme \"peur bleue\"!$",
+ "Chambre de \"Saigneur\"!$",
+ "Histoire d'eaux$",
+ "Vert nid$",
+ "Coup d'oeil sur l'interdit$",
+ "Odeur de feux de bois et de tabac$",
+ "Tabac et vieux bouquins$",
+ "Oignons, cannelle et spiritueux$",
+ "Un endroit bien peu visit‚$",
+ "Humidit‚ et moisissure$",
+ "Avis aux colporteurs...$",
+ "Corps putr‚fi‚ : cryptomanie mortelle!$",
+ "Et en plus... des piŠges d‚samorc‚s!$",
+ "C'est d‚j… ouvert$",
+ "Danger : avalanches$",
+ "Une odeur de saintet‚!$",
+ "Une bƒtisse imposante$",
+ "L'envers du mystŠre!$",
+ "Dr“le d'horoscope!$",
+ "Tant va la cruche...$",
+ "Une porte en chŠne$",
+ "Une photo$",
+ "Les armoiries$",
+ "$",
+ "Max, le domestique, vous accueille puis vous conduit … votre chambre$",
+ "Mortevielle, le 16/2/51@ Mon cher J‚r“me,@ Suite … mon t‚l‚gramme, je vous fais part des raisons de mon inqui‚tude :il y a un an, Murielle, ma dame de compagnie, disparaissait . D‚part ayant rapport avec le renouveau financier du Manoir, ou... Silence difficile … comprendre, surtout pour mon fils Guy . N'ayant pu jusqu'… pr‚sent, faire le jour sur cette affaire, je compte sur vous pour la mener … bien . Si mon ‚tat de sant‚ ne s'am‚liorait pas, prenez les d‚cisions qui vous sembleront le plus appropri‚es...@ Amiti‚s. JULIA DEFRANCK$",
+ "Plus tard, Guy vous apprendra le suicide de L‚o... aprŠs un pari insens‚ aux courses!$",
+ "F3: encore@F8: suite$",
+ "Le maŒtre des lieux$",
+ "Le futur h‚ritier$",
+ "Le fils de JULIA$",
+ "Joli brin!!!$",
+ "Superman!$",
+ "Le mari d'Ida$",
+ "Propos int‚ressants?$",
+ "Service compris...$",
+ "Rien dessous!$",
+ "Un ange passe...$",
+ "Une 1/2 h passe: rien! Attendez-vous encore?$",
+ "Admirez! Contemplez!$",
+ "Non ! Rien !$",
+ "Impossible$",
+ "‡a tache !$",
+ "Un trait‚ sur l'histoire de la r‚gion$",
+ "Quelques piŠces$",
+ "Premier commandement...$",
+ "Des p‚tales plein les narines !$",
+ "Pique, Coeur...$",
+ "‡a ne manque pas de cachets !$",
+ "Un roman d'amour$",
+ "Souffler n'est pas jouer$",
+ "Pas une r‚ussite!$",
+ "Gare aux rebondissements !$",
+ "Sombre et profond...$",
+ "Sensations normales$",
+ "Sniff!$",
+ "Pas discret ! Contentez-vous de regarder !$",
+ "Atchoum! De la p... poussiŠre$",
+ "La toile est sign‚e... pas le papier peint !$",
+ "Pas de chance, rien !$",
+ "Soyez plus discret !$",
+ "Les volets sont clos$",
+ "De la neige, encore de la neige !$",
+ "G‚nial : une toile de maŒtre !$",
+ "Aucun doute : une v‚ritable imitation$",
+ "Hum ! Vous tiquez : de l'antique en toc !$",
+ "Une piŠce rare de valeur !$",
+ "Rien de remarquable$",
+ "Linge, objets personnels...$",
+ "Pas n'importe o— !$",
+ "Ce n'est pas l'heure !$",
+ "On ne parle pas la bouche pleine ! Donc, une fois le repas termin‚...$",
+ "Quelqu'un entre, s'affaire, ressort...$",
+ "On s'approche de votre cachette !$",
+ "On vous surprend !$",
+ "Non : vous ˆtes trop charg‚ !$",
+ "Essayez de nouveau$",
+ "Vous restez perplexe !?$",
+ "Vous quittez le Manoir. A Paris, un message vous attend...$",
+ "A‹e, a‹e, a‹e !$",
+ "Rien de plus$",
+ "Le son paraŒt normal$",
+ "Ca ne bouge pas$",
+ "On vous r‚pond$",
+ "Pas le moment !$",
+ "Mˆme matiŠre, autre face !$",
+ "Le reflet est piqu‚, mais le cadre est d'or$",
+ "Bibelots, babioles...$",
+ "Vous essuyez un ‚chec !$",
+ "Il est des odeurs... qu'il vaut mieux ne pas voir !$",
+ "Des produits m‚nagers$",
+ "‡a vous d‚mange ?$",
+ "C'est coinc‚, gel‚ ! Brrrr...$",
+ "Les huisseries sont bloqu‚es !$",
+ "Des papiers...$",
+ "Non ! Le pŠre Noˆl n'est pas coinc‚ !$",
+ "‡a donne sur un couloir$",
+ "Vaisselle, argenterie...$",
+ "Non ! Ce ne sont pas les restes de Julia !$",
+ "Une gravure ancienne$",
+ "Il y a une profonde ouverture en losange$",
+ "Le mur coulisse... Un passage ! L'empruntez-vous ?$",
+ "Le passage se ferme$",
+ "Un tiroir secret... Un livret ! Le lisez-vous ?$",
+ "Le tiroir se referme$",
+ "Rien ! Sang et chairs collent … la pierre !$",
+ "Des d‚tails vous font supposer que... la mort ne fut pas imm‚diate !$",
+ "Des projets v‚reux ?$",
+ "Sa vie n'aurait-elle tenu qu'… un doigt ?$",
+ "Un tr‚sor se serait-il fait la malle ?$",
+ "Une fente de la taille d'une piŠce !$",
+ "Quelques pierres pivotent... Une crypte ! Y p‚n‚trez-vous ?$",
+ "La bague tourne, le mur se referme...$",
+ "Une colonne de pierres derriŠre l'autel$",
+ "Il y a du bruit...$",
+ "Occup‚ !$",
+ "Retentez-vous votre chance ?$",
+ "Trop profond !$",
+ "Le mur de la cave pivote$",
+ "Nothing !$",
+ "L'unique !$",
+ "L'objet glisse au fond...$",
+ "Vous n'avez rien en main$",
+ "Ce n'est pas ouvert$",
+ "Il y a d‚j… quelque chose$",
+ "La porte est ferm‚e$",
+ "Pas de r‚ponse$",
+ "Une boule de bois pleine$",
+ "Il n'y a plus de place$",
+ "Une boule de bois perc‚e par le travers$",
+ "? ?$",
+ "A vous de jouer$",
+ "OK !$",
+ "Soudain Max survient avec votre valise : \"Merci de votre visite ! D‚tective \"priv‚\"... de bon sens et de discr‚tion sans doute\" . D‚‡u d‚moralis‚, vous quittez le manoir@Vous Štes NUL !$",
+ "L‚o vous interrompt : \"la tempŠte est calm‚e. Je pars en ville dans 1 heure. Tenez-vous prˆt!\"... Bon... Vous avez perdu du temps... mais pas la vie$",
+ "Congestion, grippe fatale : vous y restez ! Votre enquŠte tombe … l'eau$",
+ "L'eau monte trŠs vite et refroidit vos derniŠres illusions... Avant que vous n'ayez eu le temps de r‚agir, vous Štes mort!$",
+ "A peine Štes-vous au fond du puits qu'une main tranche la corde... Adieu la vie!$",
+ "La tempŠte recouvre vos traces . Un mur de silence s'abat sur vos ‚paules . Lentement vous succombez … la morsure du froid !$",
+ "Pas si seul que ‡a ! Une lame glac‚e s'enfonce dans votre dos. A l'avenir, soyez plus prudent!$",
+ "Vous ignorez la responsabilit‚ exacte de L‚o dans la mort de Murielle... Est-elle morte sur le coup ? De toutes fa‡ons les problŠmes familiaux d‚couverts lors de votre enquŠte justifient l'attitude de L‚o... Vous n'Štes pas s—r que Julia vous ait appel‚ pour ‡a mais c'est suffisant pour vous ! Par respect pour elle, et aprŠs certaines pr‚cautions, vous avez une entrevue r‚v‚latrice avec L‚o$",
+ "$",
+ "Vous n'avez pas les clefs du Manoir . Vos appels restent sans r‚ponse . Vous allez attraper... la mort !$",
+ "D'un mouvement circulaire, l'‚p‚e vous fend par le travers : tripes et boyaux … l'air, bonjour les vers!$",
+ "Home, Sweet home !$",
+ "MystŠre d'une porte close$",
+ "Charme envo—tant de vieilles piŠces$",
+ "La faim au ventre$",
+ "Plus prŠs du ciel? Pas s—r !$",
+ "Peur du noir?$",
+ "Vieux tapis et reflets d'or$",
+ "Angoisse !$",
+ "Sauv‚ ? Pas certain !$",
+ "Mal … l'aise, hein !$",
+ "Toujours plus loin !$",
+ "Votre chemin de croix !$",
+ "A la d‚couverte de...$",
+ "Attention … ce que cache...$",
+ "Une descente aux Enfers !$",
+ "Si ce n'est pas dans vos cordes :@ ne soyez pas sot!$",
+ "Avant la mise en piŠce !$",
+ "Gros plan sur :$",
+ "Vous remarquez particuliŠrement...$",
+ "Et encore...$",
+ "C'est fini !$",
+ "Un peu de lecture$",
+ "L'aventure vous attend, vous partez...$",
+ "Ne ratez pas VOTRE prochaine AVENTURE...$",
+ "Je ne comprends pas$",
+ "Il y a plus simple$",
+ "Non ! Pas ce coup-ci$",
+ "Trop tard$",
+ "$",
+ "Comme un regard profond tout couvert de peaux-pierres, pointant son oeil obscur aux astres de lumiŠre, il est la gorge reliant le ciel et les enfers . Il faut aller au fond de cette artŠre comme un rat au coeur mˆme de la terre !@Lundi, Mardi, Mercredi, Dimanche du 1e lundi au 1e dimanche, tu installeras \"ce rat\" entre chacun des jours . N'omets rien car ta venue serait ta retenue !@Porte ton fardeau comme un oeuf nouveau et donne lui le jour avec force et amour.$",
+ "10/1/50: Nous avons r‚solu le mystŠre du manuscrit et localis‚ la crypte . Est-ce l'id‚e d'aboutir dans ce qui n'‚tait qu'un \"rŠve\" qui me rend si anxieuse ?@Je regrette de m'ˆtre engag‚e vis … vis de L‚o . Non! je dois continuer ! J'aurais d— mettre Guy au courant... mais, depuis une semaine, je n'ai aucune nouvelle .$",
+ "Porte ta priŠre au lieu saint qui se doit, changes-en l'air, tu auras la matiŠre !@Du pilier de la haute sagesse, le soleil aux genoux te montrera l'espace par lequel ton …me s'ouvrira un chemin et gagnera son Šre . Avance comme un Orph‚e peu soucieux des t‚nŠbres : le blanc est ta couleur, l'or ta demeure . Eclaire ton chemin jusqu'… la myst‚rieuse . Offre-lui le cercle de l'homme aux trois facettes . Qu'il regagne le monde et qu'il tourne avec lui dans la richesse premiŠre.$",
+ "Les montagnes sont les crocs d'une gueule dantesque ouverte … l'infini de quelqu' orgie c‚leste, mastiquant des ‚toiles comme nous broyons du noir .@Tu d‚poseras l'accord de pierre … tes pieds, le rire du silence sur la gamme d'en haut et dans ta main droite, une toile d'un mŠtre . Tu passeras ainsi entre les deux croissants, par del… les abŒmes du Mur du Silence . La Cl‚ des champs est … ta port‚e, tu n'as qu'… retrouver la note qui d‚note.$",
+ " DECEMBRE@ 9 REMISE 518 13 AGIOS 23@ 19 VIREMENT 1203 17 TRESOR 1598@ TOTAL 1721 TOTAL 1721$",
+ " Le 5/01/51@@ Luc, mon amour@ Guy connaŒt notre liaison . A la suite d'une dispute, je lui ai tout dit . Je ne pense qu'… toi ! Max me relance mais j'ai d‚finitivement rompu avec lui . Qu'il reste … ses gamelles . Quand pourrons-nous nous voir seuls ? Pour toi je divorcerai... Je t'aime .@ ton Eva$",
+ " Mortevielle, le 10/2/51@@ Pat,@ Je te rappelle que tu me dois 50000 F que je t'ai pr‚t‚s pour ton affaire . J'en ai besoin, peux-tu me les rendre assez vite?@ Guy$",
+ " Mortevielle, le 15/2/51@ MaŒtre,@ Je vous ‚cris au sujet de notre affaire. Je suis d‚cid‚ … aller jusqu'au bout, certain que mon associ‚, Pat DEFRANCK, a falsifi‚ un livre de comptes . Malgr‚$",
+ " Une pipe$",
+ " Un stylo … plume$",
+ " Un briquet … essence$",
+ " Une cornue$",
+ " Un blaireau$",
+ " Un pot de peinture$",
+ " Une flute$",
+ " Une bague de valeur$",
+ " Une bobine de fil$",
+ " Un vieux bouquin$",
+ " Un porte-monnaie$",
+ " Un poignard$",
+ " Un r‚volver$",
+ " Une bible$",
+ " Une bougie$",
+ " Un coffret … bijoux$",
+ " Un fer … repasser$",
+ " Une photo$",
+ " Une montre … gousset$",
+ " Une corde$",
+ " Des clefs$",
+ " Un collier de perles$",
+ " Un flacon de parfum$",
+ " Des jumelles$",
+ " Des lunettes$",
+ " Une bourse en cuir$",
+ " Une balle de tennis$",
+ " Des munitions$",
+ " Un rasoir … main$",
+ " Une brosse … cheveux$",
+ " Une brosse … linge$",
+ " Un jeu de cartes$",
+ " Un chausse pied$",
+ " Un tournevis$",
+ " Un marteau$",
+ " Des clefs$",
+ " Des clefs$",
+ " Un cendrier$",
+ " Un pinceau$",
+ " Une corde$",
+ " Un objet en bois$",
+ " Des somnifŠres$",
+ " Une bague en or$",
+ " Un coffret … bijoux$",
+ " Un r‚veil matin$",
+ " Une cotte de mailles$",
+ " Un chandellier$",
+ " Une paire de gants$",
+ " Une coupe cisel‚e$",
+ " Un parchemin$",
+ " Un poignard$",
+ " Un dossier$",
+ " Un parchemin$",
+ " Un parchemin$",
+ " Un dossier$",
+ " Un dossier$",
+ " Une lettre$",
+ " Un roman$",
+ " Une baguette en bois$",
+ " Une enveloppe$",
+ " Une lettre$",
+ " Une enveloppe$",
+ "Julia$",
+ "La mort de Julia$",
+ "Les relations de Julia$",
+ "Un message de Julia$",
+ "L'h‚ritage de Julia$",
+ "Derniers actes de Julia$",
+ "Les cadeaux de Julia$",
+ "La chambre de Julia$",
+ "La photo chez Julia$",
+ "Julia et vous...$",
+ "Les occupations de L‚o$",
+ "Les occupations de Pat$",
+ "Les occupations de Guy$",
+ "Les occupations de Bob$",
+ "Les occupations d'Eva$",
+ "Les occupations de Luc$",
+ "Les occupations d'Ida$",
+ "Les occupations de Max$",
+ "Vos occupations$",
+ "Les relations de L‚o$",
+ "Les relations de Pat$",
+ "Les relations de Guy$",
+ "Les relations de Bob$",
+ "Les relations d'Eva$",
+ "Les relations de Luc$",
+ "Les relations d'Ida$",
+ "Les relations de Max$",
+ "Vos relations$",
+ "Murielle$",
+ "Les relations de Murielle$",
+ "Murielle et vous...$",
+ "Disparition de Murielle$",
+ "Le mur du silence$",
+ "Les manuscrits$",
+ "Le blason$",
+ "Les gravures dans la cave$",
+ "Le puits$",
+ "Les passages secrets$",
+ "La chapelle$",
+ "Les tableaux$",
+ "La photo du grenier$",
+ "Le corps dans la crypte$",
+ "$",
+ "$",
+ "FIN DE LA CONVERSATION$",
+ "Les vieux appelaient ainsi la chaine de montagne qui se dresse au pied du manoir !$",
+ "C'est le massif montagneux que l'on aper‡oit devant le manoir$",
+ "Je n'en sais rien !$",
+ "Elle est morte d'une embolie pulmonaire$",
+ "Ma m‚re est morte soudainement . Son ‚tat semblait pourtant s'ˆtre am‚lior‚$",
+ "Madame DEFRANCK est morte d'un coup de froid$",
+ "Elle est morte d'une embolie pulmonaire$",
+ "Pardonnez moi mais je pr‚fŠre, actuellement garder le silence$",
+ "Ce sont toujours les meilleurs qui partent les premiers$",
+ "J'aimais beaucoup ma mŠre . Je regrette seulement qu'elle soit morte dans le manoir des DEFRANCK$",
+ "C'est une r‚gion qui a un pass‚ charg‚ et j'ai largement de quoi m'occuper . Et puis j'aime beaucoup les chevaux..$",
+ "C'est un passionn‚ d'histoire et un joueur inv‚t‚r‚ . D'ailleurs, voici un an il a gagn‚ une grosse somme$",
+ "Il a d‚j… beaucoup a faire avec la gestion et l'entretien du manoir ...$",
+ "Je suis PDG d'une petite soci‚t‚ de parfums . Mais quand je suis ici, je me repose$",
+ "C'est un homme dynamique qui a r‚ussi dans le parfum$",
+ "Lui ! C'est un arriviste v‚reux ! Les parfums ont du endormir son bon sens . D'ailleurs ici il passe ses soir‚es dans sa chambre$",
+ "J'ai ‚t‚ trŠs pr‚occup‚ par la sant‚ de ma mŠre, et maintenant je n'ai plus go–t … rien$",
+ "Il aurait mieux fait de s'occuper un peu plus de moi et un peu moins de sa mŠre$",
+ "Ce sont ses affaires ...$",
+ "Il n'a pas trop de chance en ce moment bien que ses affaires soient satisfaisantes$",
+ "Je travaille avec Pat mais ‡a ne va pas trŠs fort en ce moment$",
+ "Ah oui ?! Il a des occupations ? Il ferait bien de s'en occuper s‚rieusement alors$",
+ "Lui et Pat sont associ‚s . Je crois que ‡a ne va pas trop mal$",
+ "Je m'occupe de moi et c'est d‚j… beaucoup . Et vous ?$",
+ "Oh ‡a ! Je lui fais confiance . Elle sait s'occuper$",
+ "Mais ! Vous n'avez pas encore d‚couvert son occupation principale ..?$",
+ "Elle fait dans la d‚coration avec beaucoup dego–t d'ailleurs . Elle est toujours trŠs bien habill‚e$",
+ "Si les bijoux vous interessent, j'ai quelques affaires interessantes … saisir rapidement$",
+ "Les bijoux ...$",
+ "Je ne sais pas, mais j'aimerais bien qu'il s'occupe un peu moins de mes affaires !$",
+ "Quand on est une femme d'int‚rieur on trouve toujours de quoi s'occuper...$",
+ "Elle pourrait rester sans rien faire, mais non ! Elle coud, elle lit ...$",
+ "Elle n'a s–rement pas des occupations trŠs ‚panouissantes ...$",
+ "Une femme comme il n'y en a plus : Elle s'interesse a tout !$",
+ "Entre la cuisine et le m‚nage, je n'ai pas beaucoup de temps … vous accorder$",
+ "Je ne sais pas comment il s'y prend pour tout faire . C'est merveilleux !$",
+ "Il en ferait plus si il s'occupait moins des rag“ts et de la bouteille$",
+ "Je suis trŠs ind‚pendant . Tant qu'on ne s'occupe pas de mes affaires : Pas de problŠme$",
+ "C'est un ‚go‹ste . Je me demande si il aime autre chose que ses chevaux et ses grimoires$",
+ "Je crois qu'il s'entend bien avec tout le monde, mis … part, peut ˆtre, avec Guy$",
+ "C'est un homme de caractŠre . Il faut savoir le prendre ..$",
+ "Les affaires sont les affaires . Quant … la famille, je la laisse pour ce qu'elle est ...$",
+ "Relations ? Relations amicales ? Relations financiŠres sans doute$",
+ "Moi je n'ai rien … lui reprocher$",
+ "C'est un homme d'affaire d‚brouillard . Il nage parfois … contre-courant mais ... il s'en sortira toujours$",
+ "Ils m'ennuient tous .. Non ! Ce n'est mˆme pas ‡a .. Quoique .. certains ..$",
+ "A l'inverse de sa mŠre, c'est une personne trŠs renferm‚e ! Alors question relations ..$",
+ "Il doit sans doute faire beaucoup d'effort pour rester agr‚able malgr‚ tous ses ennuis$",
+ "Ses relations amoureuses : C'est termin‚ . Ses relations avec moi : Pas vraiment commenc‚es . Quant aux autres : Je ne suis pas les \"autres\"$",
+ "J'aime bien tout le monde, tant qu'on ne m'escroque pas$",
+ "Il ne suffit pas d'avoir un peu d'argent et d'ˆtre beau parleur pour plaire … tout le monde$",
+ "Sans histoire .. C'est quelqu'un d'agr‚able et g‚n‚reux . De plus, il ne manque pas d'humour$",
+ "Actuellement je m'entends plut“t bien avec tout le monde . Mais, ici, je ne vais pas m'‚tendre sur le sujet$",
+ "Beau plumage, mais ‡a ne vole pas haut ... Parlez en … son mari$",
+ "C'est pour un rendez-vous ?$",
+ "Elle est trŠs vivante ! Elle ne s'embarrasse pas de pr‚jug‚s stupides$",
+ "Dans mon m‚tier, on c“toit surtout des belles femmes et des truands$",
+ "La seule valeur s–re chez lui, c'est ses bijoux .. Et sa femme, mais ‡a il ne s'en rend pas compte$",
+ "C'est quelqu'un d'interessant . De pas toujours facile … comprendre, mais qui m‚rite le d‚tour$",
+ "Je ne d‚teste personne, mais j'aime les choses et les gens quand ils sont … leur place$",
+ "C'est entre nous . Mais voyez : quand je parle avec elle, je me sens vite … l'‚troit !$",
+ "Pour ne pas s'entendre avec elle, faut y mettre de la mauvaise volont‚$",
+ "Vous savez dans mon m‚tier on entend tout mais on ne retient rien, et le service est bien fait$",
+ "C'est un hypocrite, un larbin ! Personnellement je ne lui fais pas confiance$",
+ "Je ne connait pas le fond de sa pens‚e mais c'est quelqu'un de toujours trŠs correct et impeccable$",
+ "C'‚tait une personne qui a v‚cu au manoir, il y a un an .. peut ˆtre plus$",
+ "C'‚tait plus qu'une amie pour ma mŠre . En ces moments, j'aurais aim‚ qu'elle soit … mes cot‚s$",
+ "Murielle a ‚t‚ la dame de compagnie de Julia$",
+ "Elle aussi, faisait des recherches ...$",
+ "C'‚tait une femme trŠs cultiv‚e . Son brusque d‚part, il y a un an, m'a surpris et beaucoup chagrin‚$",
+ "Elle partageait avec L‚o sa passion de l'histoire et de la r‚gion$",
+ "Je crois que tout le monde l'aimait bien$",
+ "Elle s'entendait bien avec tout le monde . Elle aimait beaucoup son fils . Quant aux relations belle-mŠre, belle-fille ..$",
+ "A part L‚o, elle avait de trŠs bon rapport avec Max ...$",
+ "Bien que vos relations furent peu soutenues, J‚r“me, elle vous portait toujours dans son coeur ...$",
+ "A part sa famille, pas grand monde$",
+ "Ah oui ! Je crois qu'elle a beaucoup regrett‚ le d‚part de cette amie .. euh ! Marielle .. ou Mireille ...$",
+ "Non rien !$",
+ "Non ... Pas que le sache$",
+ "J'ai connu Julia en achetant le manoir . C'‚tait son seul bien . Mais toute ma fortune ‚tait la sienne ...$",
+ "Si ce n'est quelques objets personnels, je crois qu'elle n'avait plus rien … elle$",
+ "Je crois que toute sa fortune venait de L‚o . Alors, Pfuuut !$",
+ "A part la lettre pour vous que j'ai post‚, rien de bien important !$",
+ "J'ai ‚t‚ trŠs heureuse qu'elle m'offre sa bible reli‚e$",
+ "Ca a ‚t‚ rapide et elle n'a pas eu le temps de prendre des dispositions particuliŠres$",
+ "Son dernier pr‚sent m'a surpris$",
+ "Quel cadeau ?$",
+ "Un chandellier ...$",
+ "Oui, j'ai eu un cadeau . Ma femme a mˆme eu une bible$",
+ "Et bien oui ! Comme tout le monde, je crois$",
+ "Un poignard$",
+ "Je n'ai jamais ‚t‚ fouiller dans le grenier !$",
+ "Vous avez un don de double-vue ou un passe-partout$",
+ "Le portrait d'une jeune fille : C'est Murielle ...$",
+ "Vous savez, je la connaissais assez peu$",
+ "Elle ‚tait trŠs charmante, mais c'‚tait surtout la dame de compagnie de Julia$",
+ "C'est la seule femme vraiment interessante que j'ai rencontr‚$",
+ "Elle avait de grandes connaissances historiques, et la consulter ‚tait trŠs enrichissant$",
+ "Je me suis toujours demand‚ ce que certains pouvaient lui trouver !$",
+ "Si la chambre est ferm‚e, demandez … L‚o$",
+ "J'ai ferm‚ sa chambre aprŠs sa mort et j'aimerais qu'il en soit ainsi encore un certain temps$",
+ "Vous savez ce que c'est : Des relations familiales$",
+ "Durant toutes ces ann‚es, je ne l'ai jamais servie … contre-coeur$",
+ "Je l'aimais autant qu'elle m'aimais, je crois$",
+ "De quel droit avez-vous p‚n‚tr‚ dans la chambre de ma femme ?!!$",
+ "C'est sans doute la photo de Murielle avec le filleul de Julia$",
+ "Je ne me rappelle pas$",
+ "C'est Murielle . C'est moi qui l'ai prise. et d'ailleurs elle est tir‚e … l'envers$",
+ "Vous ˆtes bien curieux !... C'est sans valeur$",
+ "Grimoires, parchemins et manuscrits : C'est le domaine de L‚o$",
+ "Dommage que la devise soit manquante ...$",
+ "C'est trŠs beau ... Et trŠs vieux ...$",
+ "Tiens ! C'est un endroit que je n'ai jamais visit‚$",
+ "D'apr‚s L‚o, il semblerait que les Lunes soient plus r‚centes$",
+ "Mˆme par ce temps, vous avez d‚nich‚ un soleil ...$",
+ "Profond et inqui‚tant : Le progrŠs a du bon$",
+ "Ca reste pour moi le plus grand des mystŠres$",
+ "Les derniers temps elle parlait d'un voyage . Et puis ...$",
+ "Il y a un peu plus d'un an, un soir, elle a d‚cid‚ de partir ...$",
+ "De toutes fa‡ons elle n'‚tait pas faite pour vivre ici$",
+ "Quoi ?! Quel corps ? Quel crypte ?$",
+ "Si il y en a, je ne les ai jamais trouv‚ ...$",
+ "Bien s–r ! ... Et des fant“mes aussi ...$",
+ "C'est la plus vielle de la r‚gion : Elle date du XI eme siŠcle$",
+ "Elle fut l‚gŠrement restaur‚e aprŠs la r‚volution$",
+ "Julia aimait beaucoup la peinture$",
+ "Ils ont diff‚rents styles, mais n'ont pas tous une trŠs grande valeur$",
+ "Que faites-vous l… ?$",
+ "Je suis s–r que vous cherchez quelque chose ici$",
+ "Je vous ‚coute$",
+ "Que d‚sirez-vous ?$",
+ "Oui ?$",
+ "Je suis … vous ...$",
+ "C'est pourquoi ?$",
+ "Allez-y$",
+ "C'est … quel sujet ?$",
+ "Max : … votre service, monsieur$",
+ "De toutes fa‡ons vous n'avez rien … faire ici ! Sortez !!$",
+ "Vous ˆtes trop curieux !$",
+ "J‚r“me ! Il y a longtemps ... Quelle tristesse, Julia est morte . Sa famille est ici : Guy, son fils . Eva, sa brue . L‚o, son mari bien s–r . Son beau fils, Pat . Des cousins : Bob, Ida, Luc . La tempŠte redouble, il vous faut rester . Les repas sont … 12h et 19h et il y a un recueillement … la chapelle tous les jours … 10h$",
+ "En vous voyant j'ai compris que vous decouvririez la v‚rit‚ ... Car je savais pourquoi vous veniez : J'avais retrouv‚ le brouillon de la lettre de Julia . Mais je suis trŠs joueur, alors ... Elle n'avait pas voulu que votre tƒche soit trop facile, pour me prot‚ger, sans doute, mais elle n'a pu mourir avec cette incertitude sur la conscience . Avez vous d‚couvert que le mur du silence est le nom que les ma‡ons ont donn‚ au mur qui porte ce blason, lors de la construction du manoir ? .. Et ces cadeaux que Julia a laiss‚ avant de mourir ‚taient autant de faux indices qui ne servaient qu'… faire ressortir l'importance des parchemins ... Effectivement, il y a plus d'un an, je travailais avec Murielle au d‚cryptage de ces manuscrits que je venais de trouver . Ma femme a fait la relation entre notre travail et la disparition de Murielle mais elle n'a jamais eu de preuves . Si ce n'est cette bague qu'elle a retrouv‚ un jour dans mes affaires . Une nuit, nous nous sommes aventur‚s dans le passage secret que nous avions d‚couvert . Murielle est morte par accident dans la piŠce de la vierge . J'ai r‚cup‚r‚ la bague rapidement, trouv‚ le tr‚sor et me suis enfuis . Je ne pensais pas qu'elle vivait encore, et je n'ai rien dit car j'avais besoin d'argent . J'ai fait passer cette somme sur le compte des courses de chevaux ...Partez maintenant, puisque vous n'ˆtes pas de la police . Laissez moi seul !$",
+ "F‚vrier 1951 ... Profession : detective priv‚ . Le froid figeait Paris et mes affaires lorsque ...$",
+ "Une lettre, un appel, des souvenirs d'une enfance encore proche . Que de jeux dans les piŠces d‚labr‚es du manoir de Mortevielle . Julia, une vieille femme a pr‚sent .$",
+ " au bureau$",
+ " … la cuisine$",
+ " … la cave$",
+ " dans le couloir$",
+ " dehors$",
+ " la salle … manger$",
+ " dans le manoir$",
+ " devant le manoir$",
+ " … la chapelle$",
+ " devant le puits$",
+ " au nord$",
+ " derriŠre le manoir$",
+ " au sud$",
+ " … l'est$",
+ " … l'ouest$",
+ " vers le manoir$",
+ " plus loin$",
+ " dans l'eau$",
+ " hors du puits$",
+ " dans le puits$",
+ " choix sur ‚cran$",
+ " Dans la serie MYSTERE...$",
+ " LE MANOIR DE MORTEVIELLE$",
+ "$",
+ " Sur une idee de...$",
+ " Bernard GRELAUD et Bruno GOURIER$",
+ "$",
+ " Realisation: LANKHOR$",
+ "$",
+ " Avec la participation de...$",
+ " Beatrice et Jean-Luc LANGLOIS$",
+ " pour la musique et les voix,$",
+ " Bernard GRELAUD pour la conception graphique,$",
+ " MARIA-DOLORES pour la realisation graphique,$",
+ " Bruno GOURIER pour la realisation technique,$",
+ " Clement ROQUES pour l'adaptation sur IBM PC et compatibles .$",
+ "$",
+ " Edition: LANKHOR$",
+ " COPYRIGHT 1988: LANKHOR$",
+ "$",
+ " A VOUS DE JOUER$",
+ " attacher$",
+ " attendre$",
+ " d‚foncer$",
+ " dormir$",
+ " ‚couter$",
+ " entrer$",
+ " fermer$",
+ " fouiller$",
+ " frapper$",
+ " gratter$",
+ " lire$",
+ " manger$",
+ " mettre$",
+ " ouvrir$",
+ " prendre$",
+ " regarder$",
+ " sentir$",
+ " sonder$",
+ " sortir$",
+ " soulever$",
+ " tourner$",
+ " se cacher$",
+ " fouiller$",
+ " lire$",
+ " poser$",
+ " regarder$",
+ " L‚o$",
+ " Pat$",
+ " Guy$",
+ " Eva$",
+ " Bob$",
+ " Luc$",
+ " Ida$",
+ " Max$",
+ "Comment Julia est-elle morte ?$",
+ "Elle s'est suicid‚e$",
+ "Elle est morte assassin‚e$",
+ "Elle est morte accidentellement$",
+ "Elle est morte naturellement$",
+ "D'o— provenait l'argent qui a permis la restauration du manoir ?$",
+ "chantage$",
+ "travail$",
+ "h‚ritage$",
+ "courses$",
+ "rentes$",
+ "hold-up$",
+ "d‚couverte$",
+ "Quel est le hobby de L‚o ?$",
+ "recherches historiques$",
+ "politique$",
+ "peinture$",
+ "drogue$",
+ "sciences occultes$",
+ "direction d'une secte$",
+ "Julia a laiss‚ une s‚rie d'indices . Ceux-ci sont repr‚sent‚s en un seul lieu . Lequel ?$",
+ "Chapelle$",
+ "Ext‚rieur$",
+ "Cave$",
+ "Grenier$",
+ "Cuisine$",
+ "Salle … manger$",
+ "Chambre Julia$",
+ "Chambre L‚o$",
+ "Chambre Pat$",
+ "Chambre Bob$",
+ "Chambre Max$",
+ "Chambre Luc/Ida$",
+ "Chambre Guy/Eva$",
+ "L'indice principal qui vous a permis d'arriver … la porte du souterrain est :$",
+ "Un poignard$",
+ "Une bague$",
+ "Un livre$",
+ "Un parchemin$",
+ "Une lettre$",
+ "Un pendule$",
+ "Combien y avait-il de parchemin dans le manoir ?$",
+ "Aucun$",
+ "Un seul$",
+ "Deux$",
+ "Trois$",
+ "Quatre$",
+ "Cinq$",
+ "Combien de personnes sont m‚l‚es … cette histoire - Julia y comprise, vous except‚ - ?$",
+ "Neuf$",
+ "Dix$",
+ "Onze$",
+ "Quel ‚tait le pr‚nom de la personne inconnue ?$",
+ "Mireille$",
+ "Fran‡oise$",
+ "Maguy$",
+ "Emilie$",
+ "Murielle$",
+ "Sophie$",
+ "De qui Murielle ‚tait-elle la maŒtresse ?$",
+ "Bob$",
+ "Luc$",
+ "Guy$",
+ "L‚o$",
+ "Max$",
+ "Murielle partageait une occupation avec une autre personne . Qui ?$",
+ "[1][ |Seul le hazard vous a permis d'arriver ici . Vous pr‚f‚rez|retourner enqu‚ter afin de mieux comprendre ...][ok]$",
+ "[1][ |Ins‚rez la disquette 1 dans le lecteur A][ok]$",
+ "[1][ |! ERREUR DISQUETTE !|On arrete tout][ok]$",
+ "[1][ |Vous devriez avoir remarqu‚|00% des indices][ok]$",
+ "[1][ |Ins‚rez la disquette 2 dans le lecteur A][ok]$",
+ "[1][ |Avant d'aller plus loin, vous faites|un point sur l'‚tat de vos connaissances][ok]$",
+ " MASTER .$",
+ " rorL$",
+};
+
+const char *gameDataDe[] = {
+ "TBT - Calm within the storm$",
+ "TBT - Discussed in colours$",
+ "TBT - Your mauve!$",
+ "TBT - Be kind enough to leave the room...$",
+ "TBT - If you're NOT overdrawn...$",
+ "TBT - If you're feeling blue...$",
+ "TBT - Read what's on the walls?$",
+ "TBT - Water sports$",
+ "TBT - Room for envy?$",
+ "TBT - A glance at the forbidden$",
+ "TBT - Smell of a woodfire and tobacco$",
+ "TBT - Tobacco and old books$",
+ "TBT - Onions, cinnamon and spirits$",
+ "TBT - A place seldom visited$",
+ "TBT - Humidity and decay$",
+ "TBT - Sorry, no ""door to door""$",
+ "TBT - Rotting corpse: deady cryptomania$",
+ "TBT - And what's more, there are disused traps$",
+ "TBT - It's already open$",
+ "TBT - Danger: avalanches$",
+ "TBT - Proper Charlie's place?$",
+ "TBT - An imposing building$",
+ "TBT - The other side of the mystery$",
+ "TBT - Strange horoscope$",
+ "TBT - Look out... but she wishes well?$",
+ "TBT - An oak door$",
+ "TBT - A photograph$",
+ "TBT - The coat of arms$",
+ "TBT - $",
+ "TBT - Max, the servant, welcomes you and shows you to your room$",
+ "TBT - Mortville 6/2/51@ My dear Jerome@Regarding my telegram, I must tell you the reason for my wor-@ries. A year ago, Murielle, my lady companion, disappeared. The de@part may have had something to do@with the financial success of themanor, or... A silence hard to un@derstand for my son Guy. Not ha@ving been able to see the light of day over this affair, I count @on you to sort things out. If my state of health doesn't improve, @take the decisions that you feel @are apropriate.@ Kind regards, Julia DEFRANCK$",
+ "TBT - Later, Guy will inform you of Leo's suicide after a@heavy bet at the races$",
+ "TBT - F3: AGAIN F8: STOP$",
+ "TBT - The master of the premises$",
+ "TBT - The future heir$",
+ "TBT - JULIA's son$",
+ "TBT - A pretty picture$",
+ "TBT - Superman!$",
+ "TBT - Ida's husband$",
+ "TBT - Interesting remarks?$",
+ "TBT - Service included!$",
+ "TBT - Nothing underneath$",
+ "TBT - You could hear a pin drop$",
+ "TBT - Half an hour passes: nothing! Wait any longer?$",
+ "TBT - Admire! Contemplate!$",
+ "TBT - No! Nothing!$",
+ "TBT - Impossible$",
+ "TBT - That stains!$",
+ "TBT - A treatise on the history of the area$",
+ "TBT - A few coins...$",
+ "TBT - First commandment...$",
+ "TBT - Pleasing to the nostrils!$",
+ "TBT - Spades, Hearts...$",
+ "TBT - Just a spoonful of sugar...$",
+ "TBT - A romantic novel$",
+ "TBT - Worth more than a penny, (whistle)$",
+ "TBT - Just needs a little patience$",
+ "TBT - Watch the sharp bends$",
+ "TBT - Deep and dark$",
+ "TBT - Normal sensations$",
+ "TBT - Sniff!$",
+ "TBT - Not discreet! Be content to watch!$",
+ "TBT - Bless you! Dusty!$",
+ "TBT - The canvas is signed, the wallpaper is not!$",
+ "TBT - Nothing, Unlucky!$",
+ "TBT - Be more discreet!$",
+ "TBT - The shutters are closed$",
+ "TBT - Snow! And more snow!$",
+ "TBT - Brilliant! The work of a master!$",
+ "TBT - No doubt at all! A genuine fake!$",
+ "TBT - Hmm! A cheap reproduction!$",
+ "TBT - A rare and valuable piece$",
+ "TBT - Nothing special$",
+ "TBT - Linen, personal belongings...$",
+ "TBT - Not just anywhere!$",
+ "TBT - It's not time!$",
+ "TBT - One doesn't speak with ones mouth full!$",
+ "TBT - Someone comes in, messes about then goes out again$",
+ "TBT - Someone's approaching your hiding-place$",
+ "TBT - Someone surprises you!$",
+ "TBT - Impossible! You're too loaded!$",
+ "TBT - Try again!$",
+ "TBT - Still puzzled!?$",
+ "TBT - You leave Mortville.In Paris a message awaits you...$",
+ "TBT - You hurt yourself$",
+ "TBT - Nothing more here$",
+ "TBT - The sound seems normal$",
+ "TBT - It doesn't move$",
+ "TBT - You are answered$",
+ "TBT - Not the right moment!$",
+ "TBT - The same matter, from another angle!$",
+ "TBT - The reflection is tarnished, but the frame is gold!$",
+ "TBT - Bric-a-brac$",
+ "TBT - Face to face with failure!$",
+ "TBT - Smells like something you'd rather not see!$",
+ "TBT - Cleaning products$",
+ "TBT - Got an itch?$",
+ "TBT - It's stuck, frozen. Brrr!$",
+ "TBT - All the locks are jammed!$",
+ "TBT - Papers$",
+ "TBT - No! Father christmas hasn't got himself stuck!$",
+ "TBT - It leads onto a corridor$",
+ "TBT - China, silverware...$",
+ "TBT - No! It's not Julia's remains!$",
+ "TBT - An old engraving$",
+ "TBT - You find a deep diamond-shaped opening$",
+ "TBT - The wall slides open! A passage! Do you follow it?$",
+ "TBT - The passageway closes$",
+ "TBT - A secret drawer: a notebook! Do you read it?$",
+ "TBT - The drawer shuts$",
+ "TBT - Nothing! Flesh and blood stuck to the stone$",
+ "TBT - Certain details lead you to believe death was not immediate!$",
+ "TBT - A rotten affair!$",
+ "TBT - Did she cling to dear life with just one finger?$",
+ "TBT - Has the treasure packed its trunk?$",
+ "TBT - A slot the size of a coin$",
+ "TBT - Part of the stone wall pivots.A crypt! Do you enter?$",
+ "TBT - The ring turns, the wall closes$",
+ "TBT - A stone column behind the altar$",
+ "TBT - There is a noise!$",
+ "TBT - Occupied!$",
+ "TBT - Take another chance?$",
+ "TBT - Too deep!$",
+ "TBT - The cellar wall pivots$",
+ "TBT - Nothing$",
+ "TBT - The one and only!$",
+ "TBT - The object slides to the bottom$",
+ "TBT - You have nothing in hand$",
+ "TBT - It is not open$",
+ "TBT - There is already something$",
+ "TBT - The door is locked$",
+ "TBT - No reply$",
+ "TBT - A solid wooden ball$",
+ "TBT - There's no more space$",
+ "TBT - A wooden ball pierced through the side$",
+ "TBT - ? ?$",
+ "TBT - Your move$",
+ "TBT - OK !$",
+ "TBT - Suddenly Max arrives with your suitcase: \"Thank you for your @visit!\".Mister discreet \"private eye\" (in need of a private optici@an!). Thoroughly demoralised, you@leave the manor. You are useless!$",
+ "TBT - Leo interrupts: \"The storm has died down,I am going into town in@1 hour. Get ready\". You have lost@time...but not your life$",
+ "TBT - Congestion, the deadly flu... You@are stuck here! Your whole case@sinks slowly beneath the water$",
+ "TBT - The water is rising fast,freezing your last illusions. Before you@have time to react...you are dead$",
+ "TBT - As soon as you reach the bottom of the well, a hand cuts the rope@Farewell sweet life!$",
+ "TBT - The storm covers your footprints.A wall of silence falls heavily@on your shoulders. Slowly you succumb to frosbite...$",
+ "TBT - You're not completely alone! A cold blade plunges into your backup@In future, be more care!$",
+ "TBT - You don't know what implication Leo may have had in Murielle's@death. Was she dead outright? In@any case,the family problems thatyou have uncovered in the course@of your enquiries would explain Leo's behaviour. You're not sure@that's the reason Julia had asked@for your help, but that's reason enough for you!Out of respect for@her, after taking certain precau-@tions you have a revealing talk with Leo.$",
+ "TBT - $",
+ "TBT - You don't have the keys to the manor. Your cries rest unheard@You're going to catch... your death!$",
+ "TBT - With a circular movement, the sword slices across you. Guts and@intestines spill out all over. A sorry state of affairs!$",
+ "TBT - Home, Sweet home !$",
+ "TBT - The mystery behind a closed door$",
+ "TBT - Bewitching charm of these old rooms$",
+ "TBT - An empty stomach$",
+ "TBT - Closer to heaven? Not so sure$",
+ "TBT - Afraid of the dark?$",
+ "TBT - Old rugs and a glint of gold$",
+ "TBT - Anguish!$",
+ "TBT - Safe? Perhaps not!$",
+ "TBT - A little ill at ease, eh!?$",
+ "TBT - Always further$",
+ "TBT - Your way of the cross!$",
+ "TBT - On the trail of...$",
+ "TBT - Watch what's hiding$",
+ "TBT - The road down to hell$",
+ "TBT - Feeling well? You look a little pale$",
+ "TBT - What lurks behind...?$",
+ "TBT - Close-up on:$",
+ "TBT - You notice, amongst other things$",
+ "TBT - And...$",
+ "TBT - That's all!$",
+ "TBT - A bit of reading$",
+ "TBT - The adventure awaits, you set off!$",
+ "TBT - Don't mess up YOUR next ADVENTURE!$",
+ "TBT - I don't understand$",
+ "TBT - There is an easier way$",
+ "TBT - No, not just now$",
+ "TBT - Too late$",
+ "TBT - $",
+ "TBT - Like a deep stony stare, a solitary eye that points towards the@stars; the artery that links hea-ven and hell. You must fathom@these depths keeping hold of that@which is, and will become. Monday, Tuesday, Wednesday, Sunday, from@Monday 1st to Sunday 1st,plunging from one day to the next your@\"IS\" or \"WILL BECOME\". Carrying your burden with love and light,@the smallest oversight will seal your fate.$",
+ "TBT - 10/1/51: We think we've solved the mystery of the manuscript and@located the crypt. Is it the idea@of success in what seems like a dream that disturbs me so? I feel@I have committed myself rather too much, as far as Leo is concer@ned... No! I should go on. @I should have put Guy in the pic-ture but for a week now, I've had@no news of him$",
+ "TBT - Take your prayers as you would to the holy place. From the pillar@of wisdom, bring the sun to his@knees. Thus will it show you the place to offer alms of another@kind and like young Arthur, open the way of darkness.White is your@colour, golden your hearth. So@advance with caution Orpheus and light your way unto the sad@virgin. Offer her the circle of the man with three faces. That he@may regain the world and turn with it to its original@inglory!$",
+ "TBT - The mountains are the fangs in a monstrous mouth opening on the@finity of a celestial orgy, grin-ding the stars as we grind our@teeth into dust. You will drop your chord of stone at your feet.@The laugh of silence at the@highest pitch, and in your right hand, the measure of genius. Thus@will you pass between the two crescents beyond the abyss of the@wall of silence. The key to the melody is within your grasp. It@suffices to find the note that clashes.$",
+ "TBT - 9/12 INTER. 518 3/13 EXPENS. 23@ 9/12 SALES 1203 7/12 CHEQUE 1598@ TOTAL 1721 TOTAL 1721$",
+ "TBT - 5/1/51@@ Luc, my love@ Guy knows about us. After an argument I told him everything! I@think only of you. Max keeps pes-tering me, but it's finished with @him. He should stick to his pots and pans! When can you and I be alone together? For you I would@get a divorce.@I love you.@ Eva$",
+ "TBT - Mortville, 10/2/51@@ Pat@ I recall you owe me 50000 frs that I lent you for your business@I need that money, can you repay me quickly?@ Guy$",
+ "TBT - Mortville, 15/2/51@ Dear Sir@ I am writing to you on the sub-ject of our business deal. I have@decided to go all the way in the certainty that my partner, Pat@DEFRANCK, has been forging the accounts. @In spite of$",
+ "TBT - A pipe$",
+ "TBT - A pen$",
+ "TBT - A lighter$",
+ "TBT - A retort$",
+ "TBT - A shaving brush$",
+ "TBT - A tin of paint$",
+ "TBT - A flute$",
+ "TBT - An expensive ring$",
+ "TBT - A reel of thread$",
+ "TBT - An old book$",
+ "TBT - A wallet$",
+ "TBT - A dagger$",
+ "TBT - A pistol$",
+ "TBT - A bible$",
+ "TBT - A candle$",
+ "TBT - A jewellery box$",
+ "TBT - An iron$",
+ "TBT - A photo$",
+ "TBT - A pocket watch$",
+ "TBT - A rope$",
+ "TBT - Keys$",
+ "TBT - A pearl necklace$",
+ "TBT - A bottle of perfume$",
+ "TBT - Binoculars$",
+ "TBT - Glasses$",
+ "TBT - A leather purse$",
+ "TBT - A tennis ball$",
+ "TBT - Ammunition$",
+ "TBT - A cut-throat razor$",
+ "TBT - A hairbrush$",
+ "TBT - A clothes brush$",
+ "TBT - A pack of cards$",
+ "TBT - A shoe horn$",
+ "TBT - A screwdriver$",
+ "TBT - A hammer$",
+ "TBT - Keys$",
+ "TBT - Keys$",
+ "TBT - An ashtray$",
+ "TBT - A paintbrush$",
+ "TBT - A rope$",
+ "TBT - A wooden object$",
+ "TBT - Sleeping pills$",
+ "TBT - A gold ring$",
+ "TBT - A jewellery box$",
+ "TBT - An alarm clock$",
+ "TBT - A coat of armour$",
+ "TBT - A candlestick$",
+ "TBT - A pair of gloves$",
+ "TBT - A engraved goblet$",
+ "TBT - A parchment$",
+ "TBT - A dagger$",
+ "TBT - A dossier$",
+ "TBT - A parchment$",
+ "TBT - A parchment$",
+ "TBT - A dossier$",
+ "TBT - A dossier$",
+ "TBT - A letter$",
+ "TBT - A novel$",
+ "TBT - A wooden rod$",
+ "TBT - An envelope$",
+ "TBT - A letter$",
+ "TBT - An envelope$",
+ "TBT - Julia$",
+ "TBT - Julia's death$",
+ "TBT - Julia's relationships$",
+ "TBT - A message from Julia$",
+ "TBT - Julia's inheritance$",
+ "TBT - Julia's final actions$",
+ "TBT - Julia's gifts$",
+ "TBT - Julia's bedroom$",
+ "TBT - The photo at Julia's home$",
+ "TBT - Julia and yourself...$",
+ "TBT - L‚o's occupations$",
+ "TBT - Pat's occupations$",
+ "TBT - Guy's occupations$",
+ "TBT - Bob's occupations$",
+ "TBT - Eva's occupations$",
+ "TBT - Luc's occupations$",
+ "TBT - Ida's occupations$",
+ "TBT - Max's occupations$",
+ "TBT - Your occupations$",
+ "TBT - L‚o's relationships$",
+ "TBT - Pat's relationships$",
+ "TBT - Guy's relationships$",
+ "TBT - Bob's relationships$",
+ "TBT - Eva's relationships$",
+ "TBT - Luc's relationships$",
+ "TBT - Ida's relationships$",
+ "TBT - Max's relationships$",
+ "TBT - Your relationships$",
+ "TBT - Murielle$",
+ "TBT - Murielle's relationships$",
+ "TBT - Murielle and yourself...$",
+ "TBT - Murielle's disappearance$",
+ "TBT - The wall of silence$",
+ "TBT - The manuscripts$",
+ "TBT - The coat of arms$",
+ "TBT - Engravings in the cellar$",
+ "TBT - The well$",
+ "TBT - The secret passages$",
+ "TBT - The chapel$",
+ "TBT - The paintings$",
+ "TBT - The photo of the attic$",
+ "TBT - The body in the crypt$",
+ "TBT - $",
+ "TBT - $",
+ "TBT - END OF THE CONVERSATION$",
+ "TBT - Les vieux appelaient ainsi la chaine de montagne qui se dresse au pied du manoir !$",
+ "TBT - C'est le massif montagneux que l'on aper‡oit devant le manoir$",
+ "TBT - Je n'en sais rien !$",
+ "TBT - Elle est morte d'une embolie pulmonaire$",
+ "TBT - Ma m‚re est morte soudainement . Son ‚tat semblait pourtant s'ˆtre am‚lior‚$",
+ "TBT - Madame DEFRANCK est morte d'un coup de froid$",
+ "TBT - Elle est morte d'une embolie pulmonaire$",
+ "TBT - Pardonnez moi mais je pr‚fŠre, actuellement garder le silence$",
+ "TBT - Ce sont toujours les meilleurs qui partent les premiers$",
+ "TBT - J'aimais beaucoup ma mŠre . Je regrette seulement qu'elle soit morte dans le manoir des DEFRANCK$",
+ "TBT - C'est une r‚gion qui a un pass‚ charg‚ et j'ai largement de quoi m'occuper . Et puis j'aime beaucoup les chevaux..$",
+ "TBT - C'est un passionn‚ d'histoire et un joueur inv‚t‚r‚ . D'ailleurs, voici un an il a gagn‚ une grosse somme$",
+ "TBT - Il a d‚j… beaucoup a faire avec la gestion et l'entretien du manoir ...$",
+ "TBT - Je suis PDG d'une petite soci‚t‚ de parfums . Mais quand je suis ici, je me repose$",
+ "TBT - C'est un homme dynamique qui a r‚ussi dans le parfum$",
+ "TBT - Lui ! C'est un arriviste v‚reux ! Les parfums ont du endormir son bon sens . D'ailleurs ici il passe ses soir‚es dans sa chambre$",
+ "TBT - J'ai ‚t‚ trŠs pr‚occup‚ par la sant‚ de ma mŠre, et maintenant je n'ai plus go–t … rien$",
+ "TBT - Il aurait mieux fait de s'occuper un peu plus de moi et un peu moins de sa mŠre$",
+ "TBT - Ce sont ses affaires ...$",
+ "TBT - Il n'a pas trop de chance en ce moment bien que ses affaires soient satisfaisantes$",
+ "TBT - Je travaille avec Pat mais ‡a ne va pas trŠs fort en ce moment$",
+ "TBT - Ah oui ?! Il a des occupations ? Il ferait bien de s'en occuper s‚rieusement alors$",
+ "TBT - Lui et Pat sont associ‚s . Je crois que ‡a ne va pas trop mal$",
+ "TBT - Je m'occupe de moi et c'est d‚j… beaucoup . Et vous ?$",
+ "TBT - Oh ‡a ! Je lui fais confiance . Elle sait s'occuper$",
+ "TBT - Mais ! Vous n'avez pas encore d‚couvert son occupation principale ..?$",
+ "TBT - Elle fait dans la d‚coration avec beaucoup dego–t d'ailleurs . Elle est toujours trŠs bien habill‚e$",
+ "TBT - Si les bijoux vous interessent, j'ai quelques affaires interessantes … saisir rapidement$",
+ "TBT - Les bijoux ...$",
+ "TBT - Je ne sais pas, mais j'aimerais bien qu'il s'occupe un peu moins de mes affaires !$",
+ "TBT - Quand on est une femme d'int‚rieur on trouve toujours de quoi s'occuper...$",
+ "TBT - Elle pourrait rester sans rien faire, mais non ! Elle coud, elle lit ...$",
+ "TBT - Elle n'a s–rement pas des occupations trŠs ‚panouissantes ...$",
+ "TBT - Une femme comme il n'y en a plus : Elle s'interesse a tout !$",
+ "TBT - Entre la cuisine et le m‚nage, je n'ai pas beaucoup de temps … vous accorder$",
+ "TBT - Je ne sais pas comment il s'y prend pour tout faire . C'est merveilleux !$",
+ "TBT - Il en ferait plus si il s'occupait moins des rag“ts et de la bouteille$",
+ "TBT - Je suis trŠs ind‚pendant . Tant qu'on ne s'occupe pas de mes affaires : Pas de problŠme$",
+ "TBT - C'est un ‚go‹ste . Je me demande si il aime autre chose que ses chevaux et ses grimoires$",
+ "TBT - Je crois qu'il s'entend bien avec tout le monde, mis … part, peut ˆtre, avec Guy$",
+ "TBT - C'est un homme de caractŠre . Il faut savoir le prendre ..$",
+ "TBT - Les affaires sont les affaires . Quant … la famille, je la laisse pour ce qu'elle est ...$",
+ "TBT - Relations ? Relations amicales ? Relations financiŠres sans doute$",
+ "TBT - Moi je n'ai rien … lui reprocher$",
+ "TBT - C'est un homme d'affaire d‚brouillard . Il nage parfois … contre-courant mais ... il s'en sortira toujours$",
+ "TBT - Ils m'ennuient tous .. Non ! Ce n'est mˆme pas ‡a .. Quoique .. certains ..$",
+ "TBT - A l'inverse de sa mŠre, c'est une personne trŠs renferm‚e ! Alors question relations ..$",
+ "TBT - Il doit sans doute faire beaucoup d'effort pour rester agr‚able malgr‚ tous ses ennuis$",
+ "TBT - Ses relations amoureuses : C'est termin‚ . Ses relations avec moi : Pas vraiment commenc‚es . Quant aux autres : Je ne suis pas les \"autres\"$",
+ "TBT - J'aime bien tout le monde, tant qu'on ne m'escroque pas$",
+ "TBT - Il ne suffit pas d'avoir un peu d'argent et d'ˆtre beau parleur pour plaire … tout le monde$",
+ "TBT - Sans histoire .. C'est quelqu'un d'agr‚able et g‚n‚reux . De plus, il ne manque pas d'humour$",
+ "TBT - Actuellement je m'entends plut“t bien avec tout le monde . Mais, ici, je ne vais pas m'‚tendre sur le sujet$",
+ "TBT - Beau plumage, mais ‡a ne vole pas haut ... Parlez en … son mari$",
+ "TBT - C'est pour un rendez-vous ?$",
+ "TBT - Elle est trŠs vivante ! Elle ne s'embarrasse pas de pr‚jug‚s stupides$",
+ "TBT - Dans mon m‚tier, on c“toit surtout des belles femmes et des truands$",
+ "TBT - La seule valeur s–re chez lui, c'est ses bijoux .. Et sa femme, mais ‡a il ne s'en rend pas compte$",
+ "TBT - C'est quelqu'un d'interessant . De pas toujours facile … comprendre, mais qui m‚rite le d‚tour$",
+ "TBT - Je ne d‚teste personne, mais j'aime les choses et les gens quand ils sont … leur place$",
+ "TBT - C'est entre nous . Mais voyez : quand je parle avec elle, je me sens vite … l'‚troit !$",
+ "TBT - Pour ne pas s'entendre avec elle, faut y mettre de la mauvaise volont‚$",
+ "TBT - Vous savez dans mon m‚tier on entend tout mais on ne retient rien, et le service est bien fait$",
+ "TBT - C'est un hypocrite, un larbin ! Personnellement je ne lui fais pas confiance$",
+ "TBT - Je ne connait pas le fond de sa pens‚e mais c'est quelqu'un de toujours trŠs correct et impeccable$",
+ "TBT - C'‚tait une personne qui a v‚cu au manoir, il y a un an .. peut ˆtre plus$",
+ "TBT - C'‚tait plus qu'une amie pour ma mŠre . En ces moments, j'aurais aim‚ qu'elle soit … mes cot‚s$",
+ "TBT - Murielle a ‚t‚ la dame de compagnie de Julia$",
+ "TBT - Elle aussi, faisait des recherches ...$",
+ "TBT - C'‚tait une femme trŠs cultiv‚e . Son brusque d‚part, il y a un an, m'a surpris et beaucoup chagrin‚$",
+ "TBT - Elle partageait avec L‚o sa passion de l'histoire et de la r‚gion$",
+ "TBT - Je crois que tout le monde l'aimait bien$",
+ "TBT - Elle s'entendait bien avec tout le monde . Elle aimait beaucoup son fils . Quant aux relations belle-mŠre, belle-fille ..$",
+ "TBT - A part L‚o, elle avait de trŠs bon rapport avec Max ...$",
+ "TBT - Bien que vos relations furent peu soutenues, J‚r“me, elle vous portait toujours dans son coeur ...$",
+ "TBT - A part sa famille, pas grand monde$",
+ "TBT - Ah oui ! Je crois qu'elle a beaucoup regrett‚ le d‚part de cette amie .. euh ! Marielle .. ou Mireille ...$",
+ "TBT - Non rien !$",
+ "TBT - Non ... Pas que le sache$",
+ "TBT - J'ai connu Julia en achetant le manoir . C'‚tait son seul bien . Mais toute ma fortune ‚tait la sienne ...$",
+ "TBT - Si ce n'est quelques objets personnels, je crois qu'elle n'avait plus rien … elle$",
+ "TBT - Je crois que toute sa fortune venait de L‚o . Alors, Pfuuut !$",
+ "TBT - A part la lettre pour vous que j'ai post‚, rien de bien important !$",
+ "TBT - J'ai ‚t‚ trŠs heureuse qu'elle m'offre sa bible reli‚e$",
+ "TBT - Ca a ‚t‚ rapide et elle n'a pas eu le temps de prendre des dispositions particuliŠres$",
+ "TBT - Son dernier pr‚sent m'a surpris$",
+ "TBT - Quel cadeau ?$",
+ "TBT - Un chandellier ...$",
+ "TBT - Oui, j'ai eu un cadeau . Ma femme a mˆme eu une bible$",
+ "TBT - Et bien oui ! Comme tout le monde, je crois$",
+ "TBT - Un poignard$",
+ "TBT - Je n'ai jamais ‚t‚ fouiller dans le grenier !$",
+ "TBT - Vous avez un don de double-vue ou un passe-partout$",
+ "TBT - Le portrait d'une jeune fille : C'est Murielle ...$",
+ "TBT - Vous savez, je la connaissais assez peu$",
+ "TBT - Elle ‚tait trŠs charmante, mais c'‚tait surtout la dame de compagnie de Julia$",
+ "TBT - C'est la seule femme vraiment interessante que j'ai rencontr‚$",
+ "TBT - Elle avait de grandes connaissances historiques, et la consulter ‚tait trŠs enrichissant$",
+ "TBT - Je me suis toujours demand‚ ce que certains pouvaient lui trouver !$",
+ "TBT - Si la chambre est ferm‚e, demandez … L‚o$",
+ "TBT - J'ai ferm‚ sa chambre aprŠs sa mort et j'aimerais qu'il en soit ainsi encore un certain temps$",
+ "TBT - Vous savez ce que c'est : Des relations familiales$",
+ "TBT - Durant toutes ces ann‚es, je ne l'ai jamais servie … contre-coeur$",
+ "TBT - Je l'aimais autant qu'elle m'aimais, je crois$",
+ "TBT - De quel droit avez-vous p‚n‚tr‚ dans la chambre de ma femme ?!!$",
+ "TBT - C'est sans doute la photo de Murielle avec le filleul de Julia$",
+ "TBT - Je ne me rappelle pas$",
+ "TBT - C'est Murielle . C'est moi qui l'ai prise. et d'ailleurs elle est tir‚e … l'envers$",
+ "TBT - Vous ˆtes bien curieux !... C'est sans valeur$",
+ "TBT - Grimoires, parchemins et manuscrits : C'est le domaine de L‚o$",
+ "TBT - Dommage que la devise soit manquante ...$",
+ "TBT - C'est trŠs beau ... Et trŠs vieux ...$",
+ "TBT - Tiens ! C'est un endroit que je n'ai jamais visit‚$",
+ "TBT - D'apr‚s L‚o, il semblerait que les Lunes soient plus r‚centes$",
+ "TBT - Mˆme par ce temps, vous avez d‚nich‚ un soleil ...$",
+ "TBT - Profond et inqui‚tant : Le progrŠs a du bon$",
+ "TBT - Ca reste pour moi le plus grand des mystŠres$",
+ "TBT - Les derniers temps elle parlait d'un voyage . Et puis ...$",
+ "TBT - Il y a un peu plus d'un an, un soir, elle a d‚cid‚ de partir ...$",
+ "TBT - De toutes fa‡ons elle n'‚tait pas faite pour vivre ici$",
+ "TBT - Quoi ?! Quel corps ? Quel crypte ?$",
+ "TBT - Si il y en a, je ne les ai jamais trouv‚ ...$",
+ "TBT - Bien s–r ! ... Et des fant“mes aussi ...$",
+ "TBT - C'est la plus vielle de la r‚gion : Elle date du XI eme siŠcle$",
+ "TBT - Elle fut l‚gŠrement restaur‚e aprŠs la r‚volution$",
+ "TBT - Julia aimait beaucoup la peinture$",
+ "TBT - Ils ont diff‚rents styles, mais n'ont pas tous une trŠs grande valeur$",
+ "TBT - Que faites-vous l… ?$",
+ "TBT - Je suis s–r que vous cherchez quelque chose ici$",
+ "TBT - Je vous ‚coute$",
+ "TBT - Que d‚sirez-vous ?$",
+ "TBT - Oui ?$",
+ "TBT - Je suis … vous ...$",
+ "TBT - C'est pourquoi ?$",
+ "TBT - Allez-y$",
+ "TBT - C'est … quel sujet ?$",
+ "TBT - Max : … votre service, monsieur$",
+ "TBT - De toutes fa‡ons vous n'avez rien … faire ici ! Sortez !!$",
+ "TBT - Vous ˆtes trop curieux !$",
+ "TBT - J‚r“me ! Il y a longtemps ... Quelle tristesse, Julia est morte . Sa famille est ici : Guy, son fils . Eva, sa brue . L‚o, son mari bien s–r . Son beau fils, Pat . Des cousins : Bob, Ida, Luc . La tempŠte redouble, il vous faut rester . Les repas sont … 12h et 19h et il y a un recueillement … la chapelle tous les jours … 10h$",
+ "TBT - En vous voyant j'ai compris que vous decouvririez la v‚rit‚ ... Car je savais pourquoi vous veniez : J'avais retrouv‚ le brouillon de la lettre de Julia . Mais je suis trŠs joueur, alors ... Elle n'avait pas voulu que votre tƒche soit trop facile, pour me prot‚ger, sans doute, mais elle n'a pu mourir avec cette incertitude sur la conscience . Avez vous d‚couvert que le mur du silence est le nom que les ma‡ons ont donn‚ au mur qui porte ce blason, lors de la construction du manoir ? .. Et ces cadeaux que Julia a laiss‚ avant de mourir ‚taient autant de faux indices qui ne servaient qu'… faire ressortir l'importance des parchemins ... Effectivement, il y a plus d'un an, je travailais avec Murielle au d‚cryptage de ces manuscrits que je venais de trouver . Ma femme a fait la relation entre notre travail et la disparition de Murielle mais elle n'a jamais eu de preuves . Si ce n'est cette bague qu'elle a retrouv‚ un jour dans mes affaires . Une nuit, nous nous sommes aventur‚s dans le passage secret que nous avions d‚couvert . Murielle est morte par accident dans la piŠce de la vierge . J'ai r‚cup‚r‚ la bague rapidement, trouv‚ le tr‚sor et me suis enfuis . Je ne pensais pas qu'elle vivait encore, et je n'ai rien dit car j'avais besoin d'argent . J'ai fait passer cette somme sur le compte des courses de chevaux ...Partez maintenant, puisque vous n'ˆtes pas de la police . Laissez moi seul !$",
+ "TBT - F‚vrier 1951 ... Profession : detective priv‚ . Le froid figeait Paris et mes affaires lorsque ...$",
+ "TBT - Une lettre, un appel, des souvenirs d'une enfance encore proche . Que de jeux dans les piŠces d‚labr‚es du manoir de Mortevielle . Julia, une vieille femme a pr‚sent .$",
+ "TBT - to the bureau$",
+ "TBT - to the kitchen$",
+ "TBT - to the cellar$",
+ "TBT - to the landing$",
+ "TBT - outside$",
+ "TBT - to the dining room$",
+ "TBT - inside the manor$",
+ "TBT - front of the manor$",
+ "TBT - to the chapel$",
+ "TBT - to the weel$",
+ "TBT - north$",
+ "TBT - behind the manor$",
+ "TBT - south$",
+ "TBT - east$",
+ "TBT - west$",
+ "TBT - towards the manor$",
+ "TBT - further$",
+ "TBT - in the water$",
+ "TBT - out of the weel$",
+ "TBT - in the weel$",
+ "TBT - choice on screen$",
+ "TBT - In the MYSTERY series...$",
+ "TBT - MORTVILLE MANOR$",
+ "TBT - $",
+ "TBT - From an original idea of...$",
+ "TBT - Bernard GRELAUD and Bruno GOURIER$",
+ "TBT - $",
+ "TBT - Directed by: KYILKHOR CREATION and LANGLOIS$",
+ "TBT - $",
+ "TBT - With the cooperation of...$",
+ "TBT - B‚atrice et Jean_Luc LANGLOIS$",
+ "TBT - for the music and the voices,$",
+ "TBT - Bernard GRELAUD for the graphic conception,$",
+ "TBT - MARIA-DOLORES for the graphic direction,$",
+ "TBT - Bruno GOURIER for the technical direction,$",
+ "TBT - Mick ANDON for the translation. $",
+ "TBT - $",
+ "TBT - Publisher: KYILKHOR and B&JL LANGLOIS $",
+ "TBT - COPYRIGHT 1987: KYILKHOR and B&JL LANGLOIS$",
+ "TBT - $",
+ "TBT - YOUR MOVE$",
+ "TBT - attach$",
+ "TBT - wait$",
+ "TBT - force$",
+ "TBT - sleep$",
+ "TBT - listen$",
+ "TBT - enter$",
+ "TBT - close$",
+ "TBT - search$",
+ "TBT - knock$",
+ "TBT - scratch$",
+ "TBT - read$",
+ "TBT - eat$",
+ "TBT - place$",
+ "TBT - open$",
+ "TBT - take$",
+ "TBT - look$",
+ "TBT - smell$",
+ "TBT - sound$",
+ "TBT - leave$",
+ "TBT - lift$",
+ "TBT - turn$",
+ "TBT - hide yourself$",
+ "TBT - search$",
+ "TBT - read$",
+ "TBT - put$",
+ "TBT - look$",
+ "TBT - Leo$",
+ "TBT - Pat$",
+ "TBT - Guy$",
+ "TBT - Eva$",
+ "TBT - Bob$",
+ "TBT - Luc$",
+ "TBT - Ida$",
+ "TBT - Max$",
+ "TBT - JULIA...$",
+ "TBT - - Did she commit suicide?$",
+ "TBT - - Was she murdered?$",
+ "TBT - - Did she die by accident?$",
+ "TBT - - Did she die of natural causes?$",
+ "TBT - Where did the money come from@for the restoration of the manor?$",
+ "TBT - - Blackmail$",
+ "TBT - - Honest work$",
+ "TBT - - Inheritance$",
+ "TBT - - Races$",
+ "TBT - - Rents$",
+ "TBT - - Hold-up$",
+ "TBT - - Other$",
+ "TBT - What is Leo's hobby?$",
+ "TBT - - Historical research$",
+ "TBT - - Politics$",
+ "TBT - - Painting$",
+ "TBT - - Drugs$",
+ "TBT - - Occult sciences$",
+ "TBT - - Management of a sect$",
+ "TBT - Julia left several clues that are@represented in one place. Which?$",
+ "TBT - - Chapel$",
+ "TBT - - Outside$",
+ "TBT - - Cellar$",
+ "TBT - - Attic$",
+ "TBT - - Kitchen$",
+ "TBT - - Dining room$",
+ "TBT - - Julia's room$",
+ "TBT - - Leo's room$",
+ "TBT - - Pat's room$",
+ "TBT - - Bob's room$",
+ "TBT - - Max's room$",
+ "TBT - - Luc/Ida's room$",
+ "TBT - - Guy/Eva's room$",
+ "TBT - The main clue that leads you@to the underground door is:$",
+ "TBT - - A dagger$",
+ "TBT - - A ring$",
+ "TBT - - A book$",
+ "TBT - - A parchment$",
+ "TBT - - A letter$",
+ "TBT - - A pendulum$",
+ "TBT - How many parchments were there in the manor?$",
+ "TBT - - 0$",
+ "TBT - - 1$",
+ "TBT - - 2$",
+ "TBT - - 3$",
+ "TBT - - 4$",
+ "TBT - - 5$",
+ "TBT - How many persons are involved in@this story?@(Julia included, but not yourself)$",
+ "TBT - - 9$",
+ "TBT - - 10$",
+ "TBT - - 11$",
+ "TBT - What was the first name@of the unknown character?$",
+ "TBT - - Mireille$",
+ "TBT - - Fran‡oise$",
+ "TBT - - Maguy$",
+ "TBT - - Emilie$",
+ "TBT - - Murielle$",
+ "TBT - - Sophie$",
+ "TBT - Wo did Murielle have an affair with?$",
+ "TBT - - Bob$",
+ "TBT - - Luc$",
+ "TBT - - Guy$",
+ "TBT - - Leo$",
+ "TBT - - Max$",
+ "TBT - Murielle shared an occupation@with one other person. Who?$",
+ "TBT - [1][You realize that certain elements of|this investigation remain a mystery for you.|Therefore, you decide first to learn|more before undertaking new risks..][ok]$",
+ "TBT - [3][ | insert disk 1 | in drive A ][ok]$",
+ "TBT - [1][ | Disk error | All stop... ][ok]$",
+ "TBT - [1][ | You should have noticed |00% of the clues ][ok]$",
+ "TBT - [3][ | insert disk 2 | in drive A ][ok]$",
+ "TBT - [1][ |Avant d'aller plus loin, vous faites|un point sur l'‚tat de vos connaissances][ok]$",
+ "TBT - MASTER .$",
+ "TBT - rorL$",
+ NULL
+};
+#endif
diff --git a/devtools/create_mortdat/module.mk b/devtools/create_mortdat/module.mk
new file mode 100644
index 0000000000..86b14d8284
--- /dev/null
+++ b/devtools/create_mortdat/module.mk
@@ -0,0 +1,11 @@
+
+MODULE := devtools/create_mortdat
+
+MODULE_OBJS := \
+ create_mortdat.o \
+
+# Set the name of the executable
+TOOL_EXECUTABLE := create_mortdat
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/devtools/extract_mort/extract_mort.cpp b/devtools/extract_mort/extract_mort.cpp
new file mode 100644
index 0000000000..477ca44631
--- /dev/null
+++ b/devtools/extract_mort/extract_mort.cpp
@@ -0,0 +1,387 @@
+/* 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 is a utility for extracting needed resource data from different language
+ * version of the Lure of the Temptress lure.exe executable files into a new file
+ * lure.dat - this file is required for the ScummVM Lure of the Temptress module
+ * to work properly
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+// HACK to allow building with the SDL backend on MinGW
+// see bug #1800764 "TOOLS: MinGW tools building broken"
+#ifdef main
+#undef main
+#endif // main
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common/endian.h"
+
+enum AccessMode {
+ kFileReadMode = 1,
+ kFileWriteMode = 2
+};
+
+class File {
+private:
+ FILE *f;
+public:
+ bool open(const char *filename, AccessMode mode = kFileReadMode) {
+ f = fopen(filename, (mode == kFileReadMode) ? "rb" : "wb");
+ return (f != NULL);
+ }
+ void close() {
+ fclose(f);
+ f = NULL;
+ }
+ int seek(int32 offset, int whence = SEEK_SET) {
+ return fseek(f, offset, whence);
+ }
+ long read(void *buffer, int len) {
+ return fread(buffer, 1, len, f);
+ }
+ void write(const void *buffer, int len) {
+ fwrite(buffer, 1, len, f);
+ }
+ byte readByte() {
+ byte v;
+ read(&v, sizeof(byte));
+ return v;
+ }
+ uint16 readWord() {
+ uint16 v;
+ read(&v, sizeof(uint16));
+ return FROM_LE_16(v);
+ }
+ uint32 readLong() {
+ uint32 v;
+ read(&v, sizeof(uint32));
+ return FROM_LE_32(v);
+ }
+ void readString(char *sLine) {
+ while ((*sLine = readByte()) != '\n')
+ ++sLine;
+
+ *sLine = '\0';
+ }
+ void writeByte(byte v) {
+ write(&v, sizeof(byte));
+ }
+ void writeWord(uint16 v) {
+ uint16 vTemp = TO_LE_16(v);
+ write(&vTemp, sizeof(uint16));
+ }
+ void writeLong(uint32 v) {
+ uint32 vTemp = TO_LE_32(v);
+ write(&vTemp, sizeof(uint32));
+ }
+ void writeString(const char *s) {
+ fprintf(f, "%s", s);
+ }
+ uint32 pos() {
+ return ftell(f);
+ }
+ uint32 size() {
+ int pos = ftell(f);
+ fseek (f, 0, SEEK_END);
+ int end = ftell (f);
+ fseek (f, pos, SEEK_SET);
+
+ return end;
+ }
+};
+
+File textFile, txxInp, txxNtp;
+
+/*-------------------------------------------------------------------------*/
+
+#define BUFFER_SIZE 32768
+
+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
+};
+
+/**
+ * Extracts a single character from the game data
+ */
+static void extractCharacter(unsigned char &c, int &idx, int &pt, bool &the_end, const uint16 *strData) {
+ uint16 oct, ocd;
+
+ /* 5-8 */
+ oct = FROM_LE_16(strData[idx]);
+
+ oct = ((uint16)(oct << (16 - pt))) >> (16 - pt);
+ if (pt < 6) {
+ idx = idx + 1;
+ oct = oct << (5 - pt);
+ pt = pt + 11;
+ oct = oct | (FROM_LE_16(strData[idx]) >> pt);
+ } else {
+ pt = pt - 5;
+ oct = (uint)oct >> pt;
+ }
+
+ switch (oct) {
+ case 11 : {
+ c = '$';
+ the_end = true;
+ }
+ break;
+ case 30:
+ case 31 : {
+ ocd = FROM_LE_16(strData[idx]);
+ ocd = (uint16)(ocd << (16 - pt)) >> (16 - pt);
+ if (pt < 6) {
+ idx = idx + 1;
+ ocd = ocd << (5 - pt);
+ pt = pt + 11;
+ ocd = ocd | (FROM_LE_16(strData[idx]) >> pt);
+ } else {
+ pt = pt - 5;
+ ocd = (uint)ocd >> pt;
+ }
+ if (oct == 30)
+ c = (char)tab30[ocd];
+ else
+ c = (char)tab31[ocd];
+
+ if (c == '\0') {
+ the_end = true;
+ }
+ }
+ break;
+ default:
+ c = (char)tabdr[oct];
+ }
+}
+
+/**
+ * Puts a compressed 5-bit value into the string data buffer
+ */
+static void addCompressedValue(int oct, int &indis, int &point, uint16 *strData) {
+ // Write out the part of the value that fits into the current word
+ if (point < 5)
+ strData[indis] |= oct >> (5 - point);
+ else
+ strData[indis] |= oct << (point - 5);
+
+ // Handling of there's any overlap into the next word
+ if (point < 5) {
+ // Overlapping into next word
+ ++indis;
+
+ // Get the bits that fall into the next word and set it
+ int remainder = oct & ((1 << (5 - point)) - 1);
+ strData[indis] |= remainder << (16 - (5 - point));
+
+ point += -5 + 16;
+ } else {
+ point -= 5;
+ if (point == 0) {
+ point = 16;
+ ++indis;
+ }
+ }
+}
+
+/**
+ * Compresses a single passed character and stores it in the compressed strings buffer
+ */
+static void compressCharacter(unsigned char ch, int &indis, int &point, uint16 *strData) {
+ if (ch == '$') {
+ // End of string
+ addCompressedValue(11, indis, point, strData);
+ return;
+ }
+
+ // Scan through the tabdr array for a match
+ for (int idx = 0; idx < 30; ++idx) {
+ if ((idx != 11) && (tabdr[idx] == ch)) {
+ addCompressedValue(idx, indis, point, strData);
+ return;
+ }
+ }
+
+ // Scan through the tab30 array
+ for (int idx = 0; idx < 32; ++idx) {
+ if (tab30[idx] == ch) {
+ addCompressedValue(30, indis, point, strData);
+ addCompressedValue(idx, indis, point, strData);
+ return;
+ }
+ }
+
+ // Scan through the tab31 array
+ for (int idx = 0; idx < 32; ++idx) {
+ if (tab31[idx] == ch) {
+ addCompressedValue(31, indis, point, strData);
+ addCompressedValue(idx, indis, point, strData);
+ return;
+ }
+ }
+
+ printf("Encountered invalid character '%c' when compressing strings\n", ch);
+ exit(1);
+}
+
+/**
+ * string extractor
+ */
+static void export_strings(const char *textFilename) {
+ char buffer[BUFFER_SIZE];
+ uint16 *strData;
+
+ // Open input and output files
+ if (!txxInp.open("TXX.INP", kFileReadMode)) {
+ if (!txxInp.open("TXX.MOR", kFileReadMode)) {
+ printf("Missing TXX.INP/MOR");
+ exit(-1);
+ }
+ }
+ if (!txxNtp.open("TXX.NTP", kFileReadMode)) {
+ if (!txxNtp.open("TXX.IND", kFileReadMode)) {
+ printf("Missing TXX.NTP/IND");
+ exit(-1);
+ }
+ }
+ textFile.open(textFilename, kFileWriteMode);
+
+ // Read all the compressed string data into a buffer
+ printf("%d %d", txxInp.size(), txxNtp.size());
+ strData = (uint16 *)malloc(txxInp.size());
+ txxInp.read(strData, txxInp.size());
+
+ // Loop through getting each string
+ for (unsigned int strIndex = 0; strIndex < (txxNtp.size() / 3); ++strIndex) {
+ int indis = txxNtp.readWord();
+ int point = txxNtp.readByte();
+
+ // Extract the string
+ int charIndex = 0;
+ unsigned char ch;
+ bool endFlag = false;
+ do {
+ extractCharacter(ch, indis, point, endFlag, strData);
+ buffer[charIndex++] = ch;
+ if (ch == BUFFER_SIZE) {
+ printf("Extracted string exceeded allowed buffer size.\n");
+ exit(1);
+ }
+
+ if (indis >= (txxInp.size() / 2))
+ endFlag = true;
+ } while (!endFlag);
+
+ // Write out the string
+ buffer[charIndex++] = '\n';
+ buffer[charIndex] = '\0';
+ textFile.writeString(buffer);
+ }
+
+ // Close the files and free the buffer
+ free(strData);
+ txxInp.close();
+ txxNtp.close();
+ textFile.close();
+}
+
+/**
+ * string importer
+ */
+static void import_strings(const char *textFilename) {
+ // Open input and output files
+ txxInp.open("TXX.INP", kFileWriteMode);
+ txxNtp.open("TXX.NTP", kFileWriteMode);
+ textFile.open(textFilename, kFileReadMode);
+
+ // Set up a buffer for the output compressed strings
+ uint16 strData[BUFFER_SIZE];
+ memset(strData, 0, BUFFER_SIZE);
+ char sLine[BUFFER_SIZE];
+
+ int indis = 0;
+ int point = 16;
+
+ while (textFile.pos() < textFile.size()) {
+ // Read in the next source line
+ textFile.readString(sLine);
+
+ // Write out the index entry for the string
+ txxNtp.writeWord(indis);
+ txxNtp.writeByte(point);
+
+ // Loop through writing out the characters to the compressed data buffer
+ char *s = sLine;
+ while (*s) {
+ compressCharacter(*s, indis, point, strData);
+ ++s;
+ }
+ }
+
+ // Write out the compressed data
+ if (point != 16)
+ ++indis;
+ txxInp.write(strData, indis * 2);
+
+ // Close the files
+ txxInp.close();
+ txxNtp.close();
+ textFile.close();
+}
+
+
+int main(int argc, char *argv[]) {
+ if (argc != 3) {
+ printf("Format: %s export|import output_file\n", argv[0]);
+ printf("The program must be run from the directory with the Mortville Manor game files.\n");
+ exit(0);
+ }
+
+ // Do the processing
+ if (!strcmp(argv[1], "export"))
+ export_strings(argv[2]);
+ else if (!strcmp(argv[1], "import"))
+ import_strings(argv[2]);
+ else
+ printf("Unknown operation specified\n");
+}
diff --git a/devtools/extract_mort/module.mk b/devtools/extract_mort/module.mk
new file mode 100644
index 0000000000..cbdcd251d9
--- /dev/null
+++ b/devtools/extract_mort/module.mk
@@ -0,0 +1,11 @@
+
+MODULE := devtools/extract_mort
+
+MODULE_OBJS := \
+ extract_mort.o \
+
+# Set the name of the executable
+TOOL_EXECUTABLE := extract_mort
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/dists/engine-data/mort.dat b/dists/engine-data/mort.dat
new file mode 100644
index 0000000000..4415f57c29
--- /dev/null
+++ b/dists/engine-data/mort.dat
Binary files differ
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