Initial commit
This commit is contained in:
2
engines/mortevielle/POTFILES
Normal file
2
engines/mortevielle/POTFILES
Normal file
@@ -0,0 +1,2 @@
|
||||
engines/mortevielle/mortevielle.cpp
|
||||
engines/mortevielle/metaengine.cpp
|
||||
1713
engines/mortevielle/actions.cpp
Normal file
1713
engines/mortevielle/actions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3
engines/mortevielle/configure.engine
Normal file
3
engines/mortevielle/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine mortevielle "Mortevielle" yes "" "" "highres"
|
||||
4
engines/mortevielle/credits.pl
Normal file
4
engines/mortevielle/credits.pl
Normal file
@@ -0,0 +1,4 @@
|
||||
begin_section("Mortevielle");
|
||||
add_person("Arnaud Boutonné", "Strangerke", "");
|
||||
add_person("Paul Gilbert", "dreammaster", "");
|
||||
end_section();
|
||||
55
engines/mortevielle/debugger.cpp
Normal file
55
engines/mortevielle/debugger.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mortevielle/mortevielle.h"
|
||||
#include "mortevielle/debugger.h"
|
||||
|
||||
namespace Mortevielle {
|
||||
|
||||
Debugger::Debugger(MortevielleEngine *vm) : GUI::Debugger() {
|
||||
_vm = vm;
|
||||
registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
|
||||
registerCmd("show_questions", WRAP_METHOD(Debugger, Cmd_showAllQuestions));
|
||||
registerCmd("reset_parano", WRAP_METHOD(Debugger, Cmd_resetParano));
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_showAllQuestions(int argc, const char **argv) {
|
||||
for (int i = 1; i <= 10; ++i)
|
||||
_vm->_coreVar._pctHintFound[i] = '*';
|
||||
|
||||
for (int i = 1; i <= 42; ++i)
|
||||
_vm->_coreVar._availableQuestion[i] = '*';
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
_vm->_charAnswerCount[i] = 0;
|
||||
_vm->_charAnswerMax[i] = 999;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_resetParano(int argc, const char **argv) {
|
||||
_vm->_coreVar._faithScore = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
47
engines/mortevielle/debugger.h
Normal file
47
engines/mortevielle/debugger.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MORTEVIELLE_DEBUGGER_H
|
||||
#define MORTEVIELLE_DEBUGGER_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace Mortevielle {
|
||||
|
||||
class MortevielleEngine;
|
||||
|
||||
class Debugger : public GUI::Debugger {
|
||||
private:
|
||||
MortevielleEngine *_vm;
|
||||
|
||||
protected:
|
||||
bool Cmd_showAllQuestions(int argc, const char **argv);
|
||||
bool Cmd_resetParano(int argc, const char **argv);
|
||||
|
||||
public:
|
||||
Debugger(MortevielleEngine *vm);
|
||||
~Debugger() override {}
|
||||
};
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
|
||||
#endif
|
||||
68
engines/mortevielle/detection.cpp
Normal file
68
engines/mortevielle/detection.cpp
Normal file
@@ -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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "mortevielle/detection.h"
|
||||
#include "mortevielle/mortevielle.h"
|
||||
|
||||
static const PlainGameDescriptor MortevielleGame[] = {
|
||||
{"mortevielle", "Mortville Manor"},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
static const DebugChannelDef debugFlagList[] = {
|
||||
{Mortevielle::kMortevielleCore, "core", "Core debugging"},
|
||||
{Mortevielle::kMortevielleGraphics, "graphics", "Graphics debugging"},
|
||||
DEBUG_CHANNEL_END
|
||||
};
|
||||
|
||||
#include "mortevielle/detection_tables.h"
|
||||
|
||||
class MortevielleMetaEngineDetection : public AdvancedMetaEngineDetection<Mortevielle::MortevielleGameDescription> {
|
||||
public:
|
||||
MortevielleMetaEngineDetection() : AdvancedMetaEngineDetection(Mortevielle::MortevielleGameDescriptions, MortevielleGame) {
|
||||
_md5Bytes = 512;
|
||||
// Use kADFlagUseExtraAsHint to distinguish between original and improved versions
|
||||
// (i.e. use or not of the game data file).
|
||||
_flags = kADFlagUseExtraAsHint;
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "mortevielle";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "Mortville Manor";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "Mortville Manor (C) 1987-89 Lankhor";
|
||||
}
|
||||
|
||||
const DebugChannelDef *getDebugChannels() const override {
|
||||
return debugFlagList;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
REGISTER_PLUGIN_STATIC(MORTEVIELLE_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, MortevielleMetaEngineDetection);
|
||||
46
engines/mortevielle/detection.h
Normal file
46
engines/mortevielle/detection.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MORTEVIELLE_DETECTION_H
|
||||
#define MORTEVIELLE_DETECTION_H
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
namespace Mortevielle {
|
||||
|
||||
#define GAMEOPTION_TTS GUIO_GAMEOPTIONS1
|
||||
|
||||
enum {
|
||||
kUseOriginalData = 0,
|
||||
kUseEngineDataFile = 1
|
||||
};
|
||||
|
||||
struct MortevielleGameDescription {
|
||||
AD_GAME_DESCRIPTION_HELPERS(desc);
|
||||
|
||||
ADGameDescription desc;
|
||||
Common::Language originalLanguage;
|
||||
uint8 dataFeature;
|
||||
};
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
|
||||
#endif // MORTEVIELLE_DETECTION_H
|
||||
114
engines/mortevielle/detection_tables.h
Normal file
114
engines/mortevielle/detection_tables.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Mortevielle {
|
||||
|
||||
static const MortevielleGameDescription MortevielleGameDescriptions[] = {
|
||||
// French
|
||||
{
|
||||
{
|
||||
"mortevielle",
|
||||
"",
|
||||
AD_ENTRY2s("menufr.mor", "e413f36b9e14eef16130adc347a9391f", 144,
|
||||
"dxx.mor", "949e68e829ecd5ad29e36a00347a9e7e", 207744),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOMIDI, GAMEOPTION_TTS)
|
||||
}, Common::FR_FRA, kUseOriginalData
|
||||
},
|
||||
// German
|
||||
{
|
||||
{
|
||||
"mortevielle",
|
||||
"",
|
||||
AD_ENTRY2s("menual.mor", "792aea282b07a1d74c4a4abeabc90c19", 144,
|
||||
"dxx.mor", "949e68e829ecd5ad29e36a00347a9e7e", 207744),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOMIDI, GAMEOPTION_TTS)
|
||||
}, Common::DE_DEU, kUseOriginalData
|
||||
},
|
||||
|
||||
// German, improved translation
|
||||
// {
|
||||
// {
|
||||
// "mortevielle",
|
||||
// "Improved Translation",
|
||||
// AD_ENTRY2s("menual.mor", "792aea282b07a1d74c4a4abeabc90c19", 144,
|
||||
// "dxx.mor", "949e68e829ecd5ad29e36a00347a9e7e", 207744),
|
||||
// Common::DE_DEU,
|
||||
// Common::kPlatformDOS,
|
||||
// ADGF_NO_FLAGS,
|
||||
// GUIO2(GUIO_NOMIDI, GAMEOPTION_TTS)
|
||||
// }, Common::DE_DEU, kUseEngineDataFile
|
||||
// },
|
||||
|
||||
// DOS English version doesn't exist. Technically, they are French or German versions,
|
||||
// using English strings stored mort.dat
|
||||
|
||||
// English on top of French version
|
||||
{
|
||||
{
|
||||
"mortevielle",
|
||||
"",
|
||||
AD_ENTRY2s("menufr.mor", "e413f36b9e14eef16130adc347a9391f", 144,
|
||||
"dxx.mor", "949e68e829ecd5ad29e36a00347a9e7e", 207744),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOMIDI, GAMEOPTION_TTS)
|
||||
}, Common::FR_FRA, kUseEngineDataFile
|
||||
},
|
||||
|
||||
// English on top of German version
|
||||
{
|
||||
{
|
||||
"mortevielle",
|
||||
"",
|
||||
AD_ENTRY2s("menual.mor", "792aea282b07a1d74c4a4abeabc90c19", 144,
|
||||
"dxx.mor", "949e68e829ecd5ad29e36a00347a9e7e", 207744),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOMIDI, GAMEOPTION_TTS)
|
||||
}, Common::DE_DEU, kUseEngineDataFile
|
||||
},
|
||||
|
||||
// French, provided by ultrapingu in bug ref #6575
|
||||
{
|
||||
{
|
||||
"mortevielle",
|
||||
"",
|
||||
AD_ENTRY2s("menu.mor", "3fef0a3f8fca99fdcb6dbca8cbcef46f", 160,
|
||||
"dxx.mor", "949e68e829ecd5ad29e36a00347a9e7e", 207744),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOMIDI, GAMEOPTION_TTS)
|
||||
}, Common::FR_FRA, kUseEngineDataFile
|
||||
},
|
||||
|
||||
{ AD_TABLE_END_MARKER , Common::EN_ANY, kUseEngineDataFile}
|
||||
};
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
475
engines/mortevielle/dialogs.cpp
Normal file
475
engines/mortevielle/dialogs.cpp
Normal file
@@ -0,0 +1,475 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Mortville Manor DOS source code
|
||||
* Copyright (c) 1987-1989 Lankhor
|
||||
*/
|
||||
|
||||
#include "mortevielle/mortevielle.h"
|
||||
|
||||
#include "mortevielle/dialogs.h"
|
||||
#include "mortevielle/mouse.h"
|
||||
#include "mortevielle/outtext.h"
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Mortevielle {
|
||||
|
||||
DialogManager::DialogManager(MortevielleEngine *vm) {
|
||||
_vm = vm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert function - Show
|
||||
* @remarks Originally called 'do_alert'
|
||||
*/
|
||||
int DialogManager::show(const Common::String &msg) {
|
||||
// Make a copy of the current screen surface for later restore
|
||||
_vm->_backgroundSurface.copyFrom(*_vm->_screenSurface);
|
||||
|
||||
_vm->_mouse->hideMouse();
|
||||
while (_vm->keyPressed())
|
||||
_vm->getChar();
|
||||
|
||||
_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);
|
||||
|
||||
Common::Point curPos;
|
||||
if (alertStr == "") {
|
||||
drawAlertBox(10, 5, colNumb);
|
||||
} else {
|
||||
drawAlertBox(8, 7, colNumb);
|
||||
int i = -1;
|
||||
_vm->_screenSurface->_textPos.y = 70;
|
||||
do {
|
||||
curPos.x = 320;
|
||||
Common::String displayStr = "";
|
||||
while ((alertStr[i + 1] != '|') && (alertStr[i + 1] != ']')) {
|
||||
++i;
|
||||
displayStr += alertStr[i];
|
||||
curPos.x -= 3;
|
||||
}
|
||||
_vm->_screenSurface->putxy(curPos.x, _vm->_screenSurface->_textPos.y);
|
||||
_vm->_screenSurface->_textPos.y += 6;
|
||||
_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) * kResolutionScaler;
|
||||
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) * kResolutionScaler;
|
||||
limit[2][2] = (limit[2][1]) + 40;
|
||||
}
|
||||
_vm->_mouse->showMouse();
|
||||
int id = 0;
|
||||
bool dummyFl = false;
|
||||
bool test3;
|
||||
do {
|
||||
char dummyKey = '\377';
|
||||
_vm->_mouse->moveMouse(dummyFl, dummyKey);
|
||||
if (_vm->shouldQuit())
|
||||
return 0;
|
||||
|
||||
curPos = _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) {
|
||||
_vm->_mouse->hideMouse();
|
||||
if (id != 0) {
|
||||
setPosition(id, coldep, esp);
|
||||
|
||||
Common::String tmpStr(" ");
|
||||
tmpStr += buttonStr[id];
|
||||
tmpStr += " ";
|
||||
_vm->_screenSurface->drawString(tmpStr, 0);
|
||||
}
|
||||
setPosition(ix, coldep, esp);
|
||||
|
||||
Common::String tmp2 = " ";
|
||||
tmp2 += buttonStr[ix];
|
||||
tmp2 += " ";
|
||||
_vm->_screenSurface->drawString(tmp2, 1);
|
||||
|
||||
id = ix;
|
||||
_vm->_mouse->showMouse();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((id != 0) && !newaff) {
|
||||
_vm->_mouse->hideMouse();
|
||||
setPosition(id, coldep, esp);
|
||||
|
||||
Common::String tmp3(" ");
|
||||
tmp3 += buttonStr[id];
|
||||
tmp3 += " ";
|
||||
_vm->_screenSurface->drawString(tmp3, 0);
|
||||
|
||||
id = 0;
|
||||
_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 (!_vm->getMouseClick());
|
||||
_vm->setMouseClick(false);
|
||||
_vm->_mouse->hideMouse();
|
||||
if (!test3) {
|
||||
id = 1;
|
||||
setPosition(1, coldep, esp);
|
||||
Common::String tmp4(" ");
|
||||
tmp4 += buttonStr[1];
|
||||
tmp4 += " ";
|
||||
_vm->_screenSurface->drawString(tmp4, 1);
|
||||
}
|
||||
_vm->_mouse->showMouse();
|
||||
|
||||
/* Restore the background area */
|
||||
_vm->_screenSurface->copyFrom(_vm->_backgroundSurface, 0, 0);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert function - Decode Alert Details
|
||||
* @remarks Originally called 'decod'
|
||||
*/
|
||||
void DialogManager::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;
|
||||
}
|
||||
|
||||
choiceListStr = Common::String(inputStr.c_str() + i);
|
||||
col *= 6;
|
||||
}
|
||||
|
||||
void DialogManager::setPosition(int ji, int coldep, int esp) {
|
||||
_vm->_screenSurface->putxy(coldep + (40 + esp) * (ji - 1), 98);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert function - Draw Alert Box
|
||||
* @remarks Originally called 'fait_boite'
|
||||
*/
|
||||
void DialogManager::drawAlertBox(int firstLine, int lineNum, int width) {
|
||||
if (width > 640)
|
||||
width = 640;
|
||||
int x = 320 - ((uint)width / 2);
|
||||
int y = (firstLine - 1) * 8;
|
||||
int xx = x + width;
|
||||
int yy = y + (lineNum * 8);
|
||||
_vm->_screenSurface->fillRect(15, Common::Rect(x, y, xx, yy));
|
||||
_vm->_screenSurface->fillRect(0, Common::Rect(x, y + 2, xx, y + 4));
|
||||
_vm->_screenSurface->fillRect(0, Common::Rect(x, yy - 4, xx, yy - 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert function - Set Button Text
|
||||
* @remarks Originally called 'fait_choix'
|
||||
*/
|
||||
void DialogManager::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] += ' ';
|
||||
|
||||
_vm->_screenSurface->putxy(x, 98);
|
||||
|
||||
Common::String tmp(" ");
|
||||
tmp += str[l];
|
||||
tmp += " ";
|
||||
|
||||
_vm->_screenSurface->drawString(tmp, 0);
|
||||
x += esp + 40;
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Questions asked before entering the hidden passage
|
||||
*/
|
||||
bool DialogManager::showKnowledgeCheck() {
|
||||
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;
|
||||
int correctCount = 0;
|
||||
|
||||
for (int indx = 0; indx < 10; ++indx) {
|
||||
_vm->_mouse->hideMouse();
|
||||
_vm->clearScreen();
|
||||
_vm->_mouse->showMouse();
|
||||
int dialogHeight = 23;
|
||||
_vm->_screenSurface->fillRect(15, Common::Rect(0, 14, 630, dialogHeight));
|
||||
Common::String tmpStr = _vm->getString(textIndexArr[indx]);
|
||||
_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;
|
||||
|
||||
int prevChoice = 1;
|
||||
for (int j = firstOption; j <= lastOption; ++j, ++prevChoice) {
|
||||
tmpStr = _vm->getString(j);
|
||||
if ((int)tmpStr.size() > maxLength)
|
||||
maxLength = tmpStr.size();
|
||||
_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 * kResolutionScaler, 27 + j * 8, (maxLength * 3 + 55) * kResolutionScaler, 34 + j * 8);
|
||||
coor[j]._enabled = true;
|
||||
|
||||
while ((int)choiceArray[j].size() < maxLength) {
|
||||
choiceArray[j] += ' ';
|
||||
}
|
||||
}
|
||||
coor[lastOption - firstOption + 2]._enabled = false;
|
||||
int rep = 6;
|
||||
_vm->_screenSurface->drawBox(80, 33, 40 + (maxLength * rep), (lastOption - firstOption) * 8 + 16, 15);
|
||||
rep = 0;
|
||||
|
||||
prevChoice = 0;
|
||||
warning("Expected answer: %d", correctAnswerArr[indx]);
|
||||
do {
|
||||
_vm->setMouseClick(false);
|
||||
bool flag;
|
||||
char key;
|
||||
_vm->_mouse->moveMouse(flag, key);
|
||||
if (_vm->shouldQuit())
|
||||
return false;
|
||||
|
||||
currChoice = 1;
|
||||
while (coor[currChoice]._enabled && !_vm->_mouse->isMouseIn(coor[currChoice]._rect))
|
||||
++currChoice;
|
||||
if (coor[currChoice]._enabled) {
|
||||
if ((prevChoice != 0) && (prevChoice != currChoice)) {
|
||||
tmpStr = choiceArray[prevChoice] + '$';
|
||||
_vm->_text->displayStr(tmpStr, 100, 27 + (prevChoice * 8), 100, 1, 0);
|
||||
}
|
||||
if (prevChoice != currChoice) {
|
||||
tmpStr = choiceArray[currChoice] + '$';
|
||||
_vm->_text->displayStr(tmpStr, 100, 27 + (currChoice * 8), 100, 1, 1);
|
||||
prevChoice = currChoice;
|
||||
}
|
||||
} else if (prevChoice != 0) {
|
||||
tmpStr = choiceArray[prevChoice] + '$';
|
||||
_vm->_text->displayStr(tmpStr, 100, 27 + (prevChoice * 8), 100, 1, 0);
|
||||
prevChoice = 0;
|
||||
}
|
||||
} while (!((prevChoice != 0) && _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 DialogManager::drawF3F8() {
|
||||
Common::String f3 = _vm->getEngineString(S_F3);
|
||||
Common::String f8 = _vm->getEngineString(S_F8);
|
||||
|
||||
// Write the F3 and F8 text strings
|
||||
_vm->_screenSurface->putxy(3, 44);
|
||||
_vm->_screenSurface->drawString(f3, 5);
|
||||
_vm->_screenSurface->_textPos.y = 51;
|
||||
_vm->_screenSurface->drawString(f8, 5);
|
||||
|
||||
// Get the width of the written text strings
|
||||
int f3Width = _vm->_screenSurface->getStringWidth(f3);
|
||||
int f8Width = _vm->_screenSurface->getStringWidth(f8);
|
||||
|
||||
// Write out the bounding box
|
||||
_vm->_screenSurface->drawBox(0, 42, MAX(f3Width, f8Width) + 4, 16, 7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert function - Loop until F8 is pressed, update
|
||||
* Graphical Device if modified
|
||||
* @remarks Originally called 'diver'
|
||||
*/
|
||||
void DialogManager::checkForF8(int SpeechNum, bool drawFrame2Fl) {
|
||||
_vm->testKeyboard();
|
||||
do {
|
||||
_vm->_soundManager->startSpeech(SpeechNum, 0, 0);
|
||||
_vm->_key = waitForF3F8();
|
||||
if (_vm->shouldQuit())
|
||||
return;
|
||||
} while (_vm->_key != 66); // keycode for F8
|
||||
// just stop the speech when pressing F8
|
||||
if (_vm->_soundManager->_ttsMan != nullptr)
|
||||
_vm->_soundManager->_ttsMan->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert function - Loop until F3 or F8 is pressed
|
||||
* @remarks Originally called 'atf3f8'
|
||||
*/
|
||||
int DialogManager::waitForF3F8() {
|
||||
int key;
|
||||
|
||||
do {
|
||||
key = _vm->gettKeyPressed();
|
||||
if (_vm->shouldQuit())
|
||||
return key;
|
||||
} while ((key != 61) && (key != 66));
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intro function - display intro screen
|
||||
* @remarks Originally called 'aff50'
|
||||
*/
|
||||
void DialogManager::displayIntroScreen(bool drawFrame2Fl) {
|
||||
_vm->_caff = 50;
|
||||
_vm->_maff = 0;
|
||||
_vm->_text->taffich();
|
||||
_vm->draw(63, 12);
|
||||
if (drawFrame2Fl)
|
||||
displayIntroFrame2();
|
||||
else
|
||||
_vm->handleDescriptionText(2, kDialogStringIndex + 142);
|
||||
|
||||
// Draw the f3/f8 dialog
|
||||
drawF3F8();
|
||||
}
|
||||
|
||||
/**
|
||||
* Intro function - display 2nd frame of intro
|
||||
* @remarks Originally called 'ani50'
|
||||
*/
|
||||
void DialogManager::displayIntroFrame2() {
|
||||
_vm->_crep = _vm->getAnimOffset(1, 1);
|
||||
_vm->displayPicture(&_vm->_curAnim[_vm->_crep], 63, 12);
|
||||
_vm->_crep = _vm->getAnimOffset(2, 1);
|
||||
_vm->displayPicture(&_vm->_curAnim[_vm->_crep], 63, 12);
|
||||
_vm->_largestClearScreen = false;
|
||||
_vm->handleDescriptionText(2, kDialogStringIndex + 143);
|
||||
}
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
65
engines/mortevielle/dialogs.h
Normal file
65
engines/mortevielle/dialogs.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
class MortevielleEngine;
|
||||
|
||||
static const int NUM_LINES = 7;
|
||||
const int kMaxHotspots = 14;
|
||||
|
||||
struct Hotspot {
|
||||
Common::Rect _rect;
|
||||
bool _enabled;
|
||||
};
|
||||
|
||||
class DialogManager {
|
||||
private:
|
||||
MortevielleEngine *_vm;
|
||||
|
||||
void decodeAlertDetails(Common::String inputStr, int &choiceNumb, int &lineNumb, int &col, Common::String &choiceStr, Common::String &choiceListStr);
|
||||
void setPosition(int ji, int coldep, int esp);
|
||||
void drawAlertBox(int firstLine, int lineNum, int width);
|
||||
void setButtonText(Common::String c, int coldep, int nbcase, Common::String *str, int esp);
|
||||
public:
|
||||
DialogManager(MortevielleEngine *vm);
|
||||
|
||||
int show(const Common::String &msg);
|
||||
void drawF3F8();
|
||||
void checkForF8(int SpeechNum, bool drawFrame2Fl);
|
||||
int waitForF3F8();
|
||||
void displayIntroScreen(bool drawFrame2Fl);
|
||||
void displayIntroFrame2();
|
||||
bool showKnowledgeCheck();
|
||||
};
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
#endif
|
||||
1170
engines/mortevielle/graphics.cpp
Normal file
1170
engines/mortevielle/graphics.cpp
Normal file
File diff suppressed because it is too large
Load Diff
117
engines/mortevielle/graphics.h
Normal file
117
engines/mortevielle/graphics.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 MortevielleEngine;
|
||||
|
||||
class PaletteManager {
|
||||
private:
|
||||
void setPalette(const int *palette, uint idx, uint size);
|
||||
|
||||
public:
|
||||
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;
|
||||
int _lookupIndex, _lookupValue;
|
||||
bool _nibbleFlag;
|
||||
int _thickness;
|
||||
int _yInc, _yEnd, _xInc, _xEnd;
|
||||
|
||||
byte nextNibble(const byte *&pSrc);
|
||||
byte nextByte(const byte *&pSrc, const byte *&pLookup);
|
||||
|
||||
void majTtxTty();
|
||||
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 nextDecompPtr(byte *&pDest);
|
||||
void negXInc();
|
||||
void negYInc();
|
||||
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;
|
||||
|
||||
~GfxSurface();
|
||||
|
||||
void decode(const byte *pSrc);
|
||||
};
|
||||
|
||||
class ScreenSurface: public Graphics::Surface {
|
||||
private:
|
||||
MortevielleEngine *_vm;
|
||||
|
||||
Common::List<Common::Rect> _dirtyRects;
|
||||
byte _fontData[FONT_NUM_CHARS * FONT_HEIGHT];
|
||||
|
||||
public:
|
||||
ScreenSurface(MortevielleEngine *vm);
|
||||
|
||||
Common::Point _textPos; // Original called xwhere/ywhere
|
||||
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 color, 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
|
||||
809
engines/mortevielle/menu.cpp
Normal file
809
engines/mortevielle/menu.cpp
Normal file
@@ -0,0 +1,809 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Mortville Manor DOS source code
|
||||
* Copyright (c) 1987-1989 Lankhor
|
||||
*/
|
||||
|
||||
#include "mortevielle/mortevielle.h"
|
||||
|
||||
#include "mortevielle/menu.h"
|
||||
#include "mortevielle/mouse.h"
|
||||
#include "mortevielle/outtext.h"
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/textconsole.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}
|
||||
};
|
||||
|
||||
Menu::Menu(MortevielleEngine *vm) {
|
||||
_vm = vm;
|
||||
_opcodeAttach = _opcodeWait = _opcodeForce = _opcodeSleep = OPCODE_NONE;
|
||||
_opcodeListen = _opcodeEnter = _opcodeClose = _opcodeSearch = OPCODE_NONE;
|
||||
_opcodeKnock = _opcodeScratch = _opcodeRead = _opcodeEat = OPCODE_NONE;
|
||||
_opcodePlace = _opcodeOpen = _opcodeTake = _opcodeLook = OPCODE_NONE;
|
||||
_opcodeSmell = _opcodeSound = _opcodeLeave = _opcodeLift = OPCODE_NONE;
|
||||
_opcodeTurn = _opcodeSHide = _opcodeSSearch = _opcodeSRead = OPCODE_NONE;
|
||||
_opcodeSPut = _opcodeSLook = _msg3 = _msg4 = OPCODE_NONE;
|
||||
|
||||
_menuActive = false;
|
||||
_menuSelected = false;
|
||||
_multiTitle = false;
|
||||
_menuDisplayed = false;
|
||||
for (int i = 0; i < 9; i++) {
|
||||
_discussMenu[i]._menuId = MENU_NONE;
|
||||
_discussMenu[i]._actionId = 0;
|
||||
_inventoryMenu[i]._menuId = MENU_NONE;
|
||||
_inventoryMenu[i]._actionId = 0;
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
_moveMenu[i]._menuId = MENU_NONE;
|
||||
_moveMenu[i]._actionId = 0;
|
||||
}
|
||||
for (int i = 0; i < 12; i++) {
|
||||
_actionMenu[i]._menuId = MENU_NONE;
|
||||
_actionMenu[i]._actionId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::readVerbNums(Common::File &f, int dataSize) {
|
||||
// Figure out what language Id is needed
|
||||
byte desiredLanguageId;
|
||||
switch(_vm->getLanguage()) {
|
||||
case Common::EN_ANY:
|
||||
desiredLanguageId = MORTDAT_LANG_ENGLISH;
|
||||
break;
|
||||
case Common::FR_FRA:
|
||||
desiredLanguageId = MORTDAT_LANG_FRENCH;
|
||||
break;
|
||||
case Common::DE_DEU:
|
||||
desiredLanguageId = MORTDAT_LANG_GERMAN;
|
||||
break;
|
||||
default:
|
||||
warning("Language not supported, switching to English");
|
||||
desiredLanguageId = MORTDAT_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;
|
||||
}
|
||||
|
||||
assert(dataSize == 52);
|
||||
_opcodeAttach = f.readUint16LE();
|
||||
_opcodeWait = f.readUint16LE();
|
||||
_opcodeForce = f.readUint16LE();
|
||||
_opcodeSleep = f.readUint16LE();
|
||||
_opcodeListen = f.readUint16LE();
|
||||
_opcodeEnter = f.readUint16LE();
|
||||
_opcodeClose = f.readUint16LE();
|
||||
_opcodeSearch = f.readUint16LE();
|
||||
_opcodeKnock = f.readUint16LE();
|
||||
_opcodeScratch = f.readUint16LE();
|
||||
_opcodeRead = f.readUint16LE();
|
||||
_opcodeEat = f.readUint16LE();
|
||||
_opcodePlace = f.readUint16LE();
|
||||
_opcodeOpen = f.readUint16LE();
|
||||
_opcodeTake = f.readUint16LE();
|
||||
_opcodeLook = f.readUint16LE();
|
||||
_opcodeSmell = f.readUint16LE();
|
||||
_opcodeSound = f.readUint16LE();
|
||||
_opcodeLeave = f.readUint16LE();
|
||||
_opcodeLift = f.readUint16LE();
|
||||
_opcodeTurn = f.readUint16LE();
|
||||
_opcodeSHide = f.readUint16LE();
|
||||
_opcodeSSearch = f.readUint16LE();
|
||||
_opcodeSRead = f.readUint16LE();
|
||||
_opcodeSPut = f.readUint16LE();
|
||||
_opcodeSLook = f.readUint16LE();
|
||||
|
||||
_actionMenu[0]._menuId = OPCODE_NONE >> 8;
|
||||
_actionMenu[0]._actionId = OPCODE_NONE & 0xFF;
|
||||
|
||||
_actionMenu[1]._menuId = _opcodeSHide >> 8;
|
||||
_actionMenu[1]._actionId = _opcodeSHide & 0xFF;
|
||||
|
||||
_actionMenu[2]._menuId = _opcodeAttach >> 8;
|
||||
_actionMenu[2]._actionId = _opcodeAttach & 0xFF;
|
||||
|
||||
_actionMenu[3]._menuId = _opcodeForce >> 8;
|
||||
_actionMenu[3]._actionId = _opcodeForce & 0xFF;
|
||||
|
||||
_actionMenu[4]._menuId = _opcodeSleep >> 8;
|
||||
_actionMenu[4]._actionId = _opcodeSleep & 0xFF;
|
||||
|
||||
_actionMenu[5]._menuId = _opcodeEnter >> 8;
|
||||
_actionMenu[5]._actionId = _opcodeEnter & 0xFF;
|
||||
|
||||
_actionMenu[6]._menuId = _opcodeClose >> 8;
|
||||
_actionMenu[6]._actionId = _opcodeClose & 0xFF;
|
||||
|
||||
_actionMenu[7]._menuId = _opcodeKnock >> 8;
|
||||
_actionMenu[7]._actionId = _opcodeKnock & 0xFF;
|
||||
|
||||
_actionMenu[8]._menuId = _opcodeEat >> 8;
|
||||
_actionMenu[8]._actionId = _opcodeEat & 0xFF;
|
||||
|
||||
_actionMenu[9]._menuId = _opcodePlace >> 8;
|
||||
_actionMenu[9]._actionId = _opcodePlace & 0xFF;
|
||||
|
||||
_actionMenu[10]._menuId = _opcodeOpen >> 8;
|
||||
_actionMenu[10]._actionId = _opcodeOpen & 0xFF;
|
||||
|
||||
_actionMenu[11]._menuId = _opcodeLeave >> 8;
|
||||
_actionMenu[11]._actionId = _opcodeLeave & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a menu's contents
|
||||
* @remarks Originally called 'menut'
|
||||
*/
|
||||
void Menu::setText(MenuItem item, Common::String name) {
|
||||
Common::String s = name;
|
||||
|
||||
switch (item._menuId) {
|
||||
case MENU_INVENTORY:
|
||||
if (item._actionId != 7) {
|
||||
while (s.size() < 22)
|
||||
s += ' ';
|
||||
|
||||
_inventoryStringArray[item._actionId] = s;
|
||||
_inventoryStringArray[item._actionId].insertChar(' ', 0);
|
||||
}
|
||||
break;
|
||||
case MENU_MOVE: {
|
||||
// If the first character isn't '*' or ' ' then it's missing a heading space
|
||||
char c = s[0];
|
||||
if (c != '*' && c != ' ')
|
||||
s.insertChar(' ', 0);
|
||||
|
||||
while (s.size() < 22)
|
||||
s += ' ';
|
||||
|
||||
_moveStringArray[item._actionId] = s;
|
||||
}
|
||||
break;
|
||||
case MENU_ACTION: {
|
||||
// If the first character isn't '*' or ' ' then it's missing a heading space
|
||||
char c = s[0];
|
||||
if (c != '*' && c != ' ')
|
||||
s.insertChar(' ', 0);
|
||||
|
||||
while (s.size() < 10)
|
||||
s += ' ';
|
||||
|
||||
_actionStringArray[item._actionId] = s;
|
||||
}
|
||||
break;
|
||||
case MENU_SELF: {
|
||||
// If the first character isn't '*' or ' ' then it's missing a heading space
|
||||
char c = s[0];
|
||||
if (c != '*' && c != ' ')
|
||||
s.insertChar(' ', 0);
|
||||
|
||||
while (s.size() < 10)
|
||||
s += ' ';
|
||||
|
||||
_selfStringArray[item._actionId] = s;
|
||||
}
|
||||
break;
|
||||
case MENU_DISCUSS:
|
||||
_discussStringArray[item._actionId] = s;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init destination menu
|
||||
* @remarks Originally called 'tmlieu'
|
||||
*/
|
||||
void Menu::setDestinationText(int roomId) {
|
||||
Common::String nomp;
|
||||
|
||||
if (roomId == ROOM26)
|
||||
roomId = LANDING;
|
||||
|
||||
int destinationId = 0;
|
||||
for (; (destinationId < 7) && (_vm->_destinationArray[destinationId][roomId]); ++destinationId) {
|
||||
nomp = _vm->getString(_vm->_destinationArray[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 Menu number
|
||||
* @param actionId Item index
|
||||
*/
|
||||
void Menu::disableMenuItem(MenuItem item) {
|
||||
switch (item._menuId) {
|
||||
case MENU_INVENTORY:
|
||||
if (item._actionId > 6) {
|
||||
_inventoryStringArray[item._actionId].setChar('<', 0);
|
||||
_inventoryStringArray[item._actionId].setChar('>', 21);
|
||||
} else
|
||||
_inventoryStringArray[item._actionId].setChar('*', 0);
|
||||
break;
|
||||
case MENU_MOVE:
|
||||
_moveStringArray[item._actionId].setChar('*', 0);
|
||||
break;
|
||||
case MENU_ACTION:
|
||||
_actionStringArray[item._actionId].setChar('*', 0);
|
||||
break;
|
||||
case MENU_SELF:
|
||||
_selfStringArray[item._actionId].setChar('*', 0);
|
||||
break;
|
||||
case MENU_DISCUSS:
|
||||
_discussStringArray[item._actionId].setChar('*', 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a menu item
|
||||
* @param menuId Menu number
|
||||
* @param actionId Item index
|
||||
* @remarks Originally called menu_enable
|
||||
*/
|
||||
void Menu::enableMenuItem(MenuItem item) {
|
||||
switch (item._menuId) {
|
||||
case MENU_INVENTORY:
|
||||
_inventoryStringArray[item._actionId].setChar(' ', 0);
|
||||
_inventoryStringArray[item._actionId].setChar(' ', 21);
|
||||
break;
|
||||
case MENU_MOVE:
|
||||
_moveStringArray[item._actionId].setChar(' ', 0);
|
||||
break;
|
||||
case MENU_ACTION:
|
||||
_actionStringArray[item._actionId].setChar(' ', 0);
|
||||
break;
|
||||
case MENU_SELF:
|
||||
_selfStringArray[item._actionId].setChar(' ', 0);
|
||||
break;
|
||||
case MENU_DISCUSS:
|
||||
_discussStringArray[item._actionId].setChar(' ', 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::displayMenu() {
|
||||
_vm->_mouse->hideMouse();
|
||||
_vm->_screenSurface->fillRect(7, Common::Rect(0, 0, 639, 10));
|
||||
|
||||
int col = 28 * kResolutionScaler;
|
||||
for (int charNum = 0; charNum < 6; charNum++) {
|
||||
// One character after the other
|
||||
int idx = 0;
|
||||
for (int y = 1; y < 9; ++y) {
|
||||
// One column after the other
|
||||
int x = col;
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
// One line after the other
|
||||
uint msk = 0x80;
|
||||
for (int pt = 0; pt <= 7; ++pt) {
|
||||
if ((_charArr[charNum][idx] & msk) != 0) {
|
||||
_vm->_screenSurface->setPixel(Common::Point(x + 1, y + 1), 0);
|
||||
_vm->_screenSurface->setPixel(Common::Point(x, y + 1), 0);
|
||||
_vm->_screenSurface->setPixel(Common::Point(x, y), 9);
|
||||
}
|
||||
msk >>= 1;
|
||||
++x;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
col += 48 * kResolutionScaler;
|
||||
}
|
||||
_vm->_mouse->showMouse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the menu
|
||||
*/
|
||||
void Menu::drawMenu() {
|
||||
displayMenu();
|
||||
_menuActive = true;
|
||||
_msg4 = OPCODE_NONE;
|
||||
_msg3 = OPCODE_NONE;
|
||||
_menuSelected = false;
|
||||
_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 = _msg4 & 0xFF;
|
||||
|
||||
_vm->_screenSurface->putxy(menuConstants[_msg3 - 1][0] << 3, (menuIndex + 1) << 3);
|
||||
|
||||
Common::String str;
|
||||
switch (_msg3) {
|
||||
case MENU_INVENTORY:
|
||||
str = _inventoryStringArray[menuIndex];
|
||||
break;
|
||||
case MENU_MOVE:
|
||||
str = _moveStringArray[menuIndex];
|
||||
break;
|
||||
case MENU_ACTION:
|
||||
str = _actionStringArray[menuIndex];
|
||||
break;
|
||||
case MENU_SELF:
|
||||
str = _selfStringArray[menuIndex];
|
||||
break;
|
||||
case MENU_DISCUSS:
|
||||
str = _discussStringArray[menuIndex];
|
||||
break;
|
||||
case MENU_FILE:
|
||||
str = _vm->getEngineString(S_SAVE_LOAD + menuIndex);
|
||||
break;
|
||||
case MENU_SAVE:
|
||||
str = _vm->getEngineString(S_SAVE_LOAD + 1);
|
||||
str += ' ';
|
||||
str += (char)(48 + menuIndex);
|
||||
break;
|
||||
case MENU_LOAD:
|
||||
if (menuIndex == 1) {
|
||||
str = _vm->getEngineString(S_RESTART);
|
||||
} else {
|
||||
str = _vm->getEngineString(S_SAVE_LOAD + 2);
|
||||
str += ' ';
|
||||
str += (char)(47 + menuIndex);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if ((str[0] != '*') && (str[0] != '<'))
|
||||
_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) * kResolutionScaler;
|
||||
|
||||
int charWidth = 6;
|
||||
int xmx = dxcar * charWidth + xmn + 2;
|
||||
if ((pos.x > xmn) && (pos.x < xmx) && (pos.y < ymx) && (pos.y > 15)) {
|
||||
int 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) {
|
||||
// Make a copy of the current screen surface for later restore
|
||||
_vm->_backgroundSurface.copyFrom(*_vm->_screenSurface);
|
||||
|
||||
// Draw the menu
|
||||
int minX = menuConstants[ii - 1][0] << 3;
|
||||
int lineNum = menuConstants[ii - 1][3];
|
||||
_vm->_mouse->hideMouse();
|
||||
int deltaX = 6;
|
||||
int maxX = minX + (menuConstants[ii - 1][2] * deltaX) + 6;
|
||||
if ((ii == 4) && (_vm->getLanguage() == Common::EN_ANY))
|
||||
// Extra width needed for Self menu in English version
|
||||
maxX = 435;
|
||||
|
||||
_vm->_screenSurface->fillRect(15, Common::Rect(minX, 12, maxX, 10 + (menuConstants[ii - 1][1] << 1)));
|
||||
_vm->_screenSurface->fillRect(0, Common::Rect(maxX, 12, maxX + 4, 10 + (menuConstants[ii - 1][1] << 1)));
|
||||
_vm->_screenSurface->fillRect(0, Common::Rect(minX, 8 + (menuConstants[ii - 1][1] << 1), maxX + 4, 12 + (menuConstants[ii - 1][1] << 1)));
|
||||
_vm->_screenSurface->putxy(minX, 16);
|
||||
for (int i = 1; i <= lineNum; i++) {
|
||||
switch (ii) {
|
||||
case 1:
|
||||
if (_inventoryStringArray[i][0] != '*')
|
||||
_vm->_screenSurface->drawString(_inventoryStringArray[i], 4);
|
||||
break;
|
||||
case 2:
|
||||
if (_moveStringArray[i][0] != '*')
|
||||
_vm->_screenSurface->drawString(_moveStringArray[i], 4);
|
||||
break;
|
||||
case 3:
|
||||
if (_actionStringArray[i][0] != '*')
|
||||
_vm->_screenSurface->drawString(_actionStringArray[i], 4);
|
||||
break;
|
||||
case 4:
|
||||
if (_selfStringArray[i][0] != '*')
|
||||
_vm->_screenSurface->drawString(_selfStringArray[i], 4);
|
||||
break;
|
||||
case 5:
|
||||
if (_discussStringArray[i][0] != '*')
|
||||
_vm->_screenSurface->drawString(_discussStringArray[i], 4);
|
||||
break;
|
||||
case 6:
|
||||
_vm->_screenSurface->drawString(_vm->getEngineString(S_SAVE_LOAD + i), 4);
|
||||
break;
|
||||
case 7: {
|
||||
Common::String s = _vm->getEngineString(S_SAVE_LOAD + 1);
|
||||
s += ' ';
|
||||
s += (char)(48 + i);
|
||||
_vm->_screenSurface->drawString(s, 4);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
if (i == 1)
|
||||
_vm->_screenSurface->drawString(_vm->getEngineString(S_RESTART), 4);
|
||||
else {
|
||||
Common::String s = _vm->getEngineString(S_SAVE_LOAD + 2);
|
||||
s += ' ';
|
||||
s += (char)(47 + i);
|
||||
_vm->_screenSurface->drawString(s, 4);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_vm->_screenSurface->putxy(minX, _vm->_screenSurface->_textPos.y + 8);
|
||||
}
|
||||
_multiTitle = true;
|
||||
_vm->_mouse->showMouse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu is being removed, so restore the previous background area.
|
||||
*/
|
||||
void Menu::menuUp(int msgId) {
|
||||
if (_multiTitle) {
|
||||
/* Restore the background area */
|
||||
assert(_vm->_screenSurface->pitch == _vm->_backgroundSurface.pitch);
|
||||
|
||||
// Get a pointer to the source and destination of the area to restore
|
||||
const byte *pSrc = (const byte *)_vm->_backgroundSurface.getBasePtr(0, 10);
|
||||
Graphics::Surface destArea = _vm->_screenSurface->lockArea(Common::Rect(0, 10, SCREEN_WIDTH, SCREEN_HEIGHT));
|
||||
byte *pDest = (byte *)destArea.getPixels();
|
||||
|
||||
// Copy the data
|
||||
Common::copy(pSrc, pSrc + (400 - 10) * SCREEN_WIDTH, pDest);
|
||||
|
||||
_multiTitle = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase the menu
|
||||
*/
|
||||
void Menu::eraseMenu() {
|
||||
_menuActive = false;
|
||||
_vm->setMouseClick(false);
|
||||
menuUp(_msg3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle updates to the menu
|
||||
* @remarks Originally called 'mdn'
|
||||
*/
|
||||
void Menu::updateMenu() {
|
||||
if (!_menuActive)
|
||||
return;
|
||||
|
||||
Common::Point curPos = _vm->_mouse->_pos;
|
||||
if (!_vm->getMouseClick()) {
|
||||
if (curPos == _vm->_prevPos)
|
||||
return;
|
||||
else
|
||||
_vm->_prevPos = curPos;
|
||||
|
||||
bool tes = (curPos.y < 11)
|
||||
&& ((curPos.x >= (28 * kResolutionScaler) && curPos.x <= (28 * kResolutionScaler + 24))
|
||||
|| (curPos.x >= (76 * kResolutionScaler) && curPos.x <= (76 * kResolutionScaler + 24))
|
||||
|| ((curPos.x > 124 * kResolutionScaler) && (curPos.x < 124 * kResolutionScaler + 24))
|
||||
|| ((curPos.x > 172 * kResolutionScaler) && (curPos.x < 172 * kResolutionScaler + 24))
|
||||
|| ((curPos.x > 220 * kResolutionScaler) && (curPos.x < 220 * kResolutionScaler + 24))
|
||||
|| ((curPos.x > 268 * kResolutionScaler) && (curPos.x < 268 * kResolutionScaler + 24)));
|
||||
if (tes) {
|
||||
int ix;
|
||||
|
||||
if (curPos.x < 76 * kResolutionScaler)
|
||||
ix = MENU_INVENTORY;
|
||||
else if (curPos.x < 124 * kResolutionScaler)
|
||||
ix = MENU_MOVE;
|
||||
else if (curPos.x < 172 * kResolutionScaler)
|
||||
ix = MENU_ACTION;
|
||||
else if (curPos.x < 220 * kResolutionScaler)
|
||||
ix = MENU_SELF;
|
||||
else if (curPos.x < 268 * kResolutionScaler)
|
||||
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
|
||||
_vm->setMouseClick(false);
|
||||
menuUp(_msg3);
|
||||
if ((_msg4 & 0xFF) == 1)
|
||||
_msg3 = MENU_SAVE;
|
||||
else
|
||||
_msg3 = MENU_LOAD;
|
||||
menuDown(_msg3);
|
||||
|
||||
_vm->setMouseClick(false);
|
||||
} else {
|
||||
// A menu was clicked on
|
||||
_menuSelected = (_multiTitle) && (_msg4 != OPCODE_NONE);
|
||||
menuUp(_msg3);
|
||||
_vm->_currAction = _msg4;
|
||||
_vm->_currMenu = _msg3;
|
||||
_msg3 = OPCODE_NONE;
|
||||
_msg4 = OPCODE_NONE;
|
||||
|
||||
_vm->setMouseClick(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::initMenu() {
|
||||
Common::File f;
|
||||
|
||||
bool menuLoaded = false;
|
||||
// First try to read it from mort.dat if useOriginalData() is false
|
||||
if (!_vm->useOriginalData()) {
|
||||
if (!f.open(MORT_DAT))
|
||||
warning("File %s not found. Using default menu from game data", MORT_DAT);
|
||||
else {
|
||||
// Figure out what language Id is needed
|
||||
byte desiredLanguageId;
|
||||
switch(_vm->getLanguage()) {
|
||||
case Common::EN_ANY:
|
||||
desiredLanguageId = MORTDAT_LANG_ENGLISH;
|
||||
break;
|
||||
case Common::FR_FRA:
|
||||
desiredLanguageId = MORTDAT_LANG_FRENCH;
|
||||
break;
|
||||
case Common::DE_DEU:
|
||||
desiredLanguageId = MORTDAT_LANG_GERMAN;
|
||||
break;
|
||||
default:
|
||||
warning("Language not supported, switching to English");
|
||||
desiredLanguageId = MORTDAT_LANG_ENGLISH;
|
||||
break;
|
||||
}
|
||||
|
||||
// Validate the data file header
|
||||
char fileId[4];
|
||||
f.read(fileId, 4);
|
||||
// Do not display warnings here. They would already have been displayed in MortevielleEngine::loadMortDat().
|
||||
if (strncmp(fileId, "MORT", 4) == 0 && f.readByte() >= MORT_DAT_REQUIRED_VERSION) {
|
||||
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, "MENU", 4)) {
|
||||
// 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);
|
||||
continue;
|
||||
}
|
||||
if (dataSize == 6 * 24) {
|
||||
f.read(_charArr, dataSize);
|
||||
menuLoaded = true;
|
||||
} else
|
||||
warning("Wrong size %d for menu data. Expected %d or less", dataSize, 6 * 24);
|
||||
break;
|
||||
} else {
|
||||
// Other sections
|
||||
f.skip(dataSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Close the file
|
||||
f.close();
|
||||
if (!menuLoaded)
|
||||
warning("Failed to load menu from mort.dat. Will use default menu from game data instead.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!menuLoaded) {
|
||||
// Load menu from game data using the original language
|
||||
if (_vm->getOriginalLanguage() == Common::FR_FRA) {
|
||||
// We do not support reading menu.mor (it has a different format).
|
||||
// If the game version does not have menufr.mor it has to get the menu from the DAT file.
|
||||
if (!f.open("menufr.mor"))
|
||||
error("Missing file - menufr.mor");
|
||||
} else { // Common::DE_DEU
|
||||
if (!f.open("menual.mor"))
|
||||
error("Missing file - menual.mor");
|
||||
}
|
||||
f.read(_charArr, 6 * 24);
|
||||
f.close();
|
||||
}
|
||||
|
||||
// Skipped: dialog asking to swap floppy
|
||||
|
||||
for (int i = 1; i <= 8; ++i)
|
||||
_inventoryStringArray[i] = "* ";
|
||||
_inventoryStringArray[7] = "< -*-*-*-*-*-*-*-*-*- ";
|
||||
for (int i = 1; i <= 7; ++i)
|
||||
_moveStringArray[i] = "* ";
|
||||
for (int i = 1; i < 22; i++) {
|
||||
_actionStringArray[i] = _vm->getString(i + kMenuActionStringIndex);
|
||||
if ((_actionStringArray[i][0] != '*') && (_actionStringArray[i][0] != ' '))
|
||||
_actionStringArray[i].insertChar(' ', 0);
|
||||
while (_actionStringArray[i].size() < 10)
|
||||
_actionStringArray[i] += ' ';
|
||||
|
||||
if (i < 9) {
|
||||
if (i < 6) {
|
||||
_selfStringArray[i] = _vm->getString(i + kMenuSelfStringIndex);
|
||||
if ((_selfStringArray[i][0] != '*') && (_selfStringArray[i][0] != ' '))
|
||||
_selfStringArray[i].insertChar(' ', 0);
|
||||
while (_selfStringArray[i].size() < 10)
|
||||
_selfStringArray[i] += ' ';
|
||||
}
|
||||
_discussStringArray[i] = _vm->getString(i + kMenuSayStringIndex) + ' ';
|
||||
}
|
||||
}
|
||||
for (int i = 1; i <= 8; ++i) {
|
||||
_discussMenu[i]._menuId = MENU_DISCUSS;
|
||||
_discussMenu[i]._actionId = i;
|
||||
if (i < 8) {
|
||||
_moveMenu[i]._menuId = MENU_MOVE;
|
||||
_moveMenu[i]._actionId = i;
|
||||
}
|
||||
_inventoryMenu[i]._menuId = MENU_INVENTORY;
|
||||
_inventoryMenu[i]._actionId = i;
|
||||
if (i > 6)
|
||||
disableMenuItem(_inventoryMenu[i]);
|
||||
}
|
||||
_msg3 = OPCODE_NONE;
|
||||
_msg4 = OPCODE_NONE;
|
||||
_vm->_currMenu = OPCODE_NONE;
|
||||
_vm->_currAction = OPCODE_NONE;
|
||||
_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]);
|
||||
|
||||
MenuItem miSound;
|
||||
miSound._menuId = _opcodeSound >> 8;
|
||||
miSound._actionId = _opcodeSound & 0xFF;
|
||||
|
||||
MenuItem miLift;
|
||||
miLift._menuId = _opcodeLift >> 8;
|
||||
miLift._actionId = _opcodeLift & 0xFF;
|
||||
|
||||
setText(miSound, _vm->getEngineString(S_SUITE));
|
||||
setText(miLift, _vm->getEngineString(S_STOP));
|
||||
}
|
||||
|
||||
/**
|
||||
* Engine function - Switch action menu from "Search" mode back to normal mode
|
||||
* @remarks Originally called 'mfouen'
|
||||
*/
|
||||
void Menu::unsetSearchMenu() {
|
||||
setDestinationText(_vm->_coreVar._currPlace);
|
||||
for (int i = 1; i <= 11; ++i)
|
||||
enableMenuItem(_actionMenu[i]);
|
||||
|
||||
MenuItem miSound;
|
||||
miSound._menuId = _opcodeSound >> 8;
|
||||
miSound._actionId = _opcodeSound & 0xFF;
|
||||
|
||||
MenuItem miLift;
|
||||
miLift._menuId = _opcodeLift >> 8;
|
||||
miLift._actionId = _opcodeLift & 0xFF;
|
||||
|
||||
setText(miSound, _vm->getEngineString(S_PROBE));
|
||||
setText(miLift, _vm->getEngineString(S_RAISE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Inventory menu texts
|
||||
* @remarks Originally called 'modinv'
|
||||
*/
|
||||
void Menu::setInventoryText() {
|
||||
Common::String nomp;
|
||||
|
||||
int cy = 0;
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
if (_vm->_coreVar._inventory[i] != 0) {
|
||||
++cy;
|
||||
int r = _vm->_coreVar._inventory[i] + 400;
|
||||
nomp = _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
|
||||
123
engines/mortevielle/menu.h
Normal file
123
engines/mortevielle/menu.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
class MortevielleEngine;
|
||||
|
||||
enum {
|
||||
MENU_NONE = 0, MENU_INVENTORY = 1, MENU_MOVE = 2, MENU_ACTION = 3,
|
||||
MENU_SELF = 4, MENU_DISCUSS = 5, MENU_FILE = 6, MENU_SAVE = 7,
|
||||
MENU_LOAD = 8
|
||||
};
|
||||
|
||||
const int OPCODE_NONE = 0;
|
||||
|
||||
struct MenuItem {
|
||||
int _menuId;
|
||||
int _actionId;
|
||||
};
|
||||
|
||||
class Menu {
|
||||
private:
|
||||
MortevielleEngine *_vm;
|
||||
|
||||
byte _charArr[6][24];
|
||||
int _msg3;
|
||||
int _msg4;
|
||||
|
||||
void util(Common::Point pos);
|
||||
void invert(int indx);
|
||||
void menuDown(int ii);
|
||||
|
||||
public:
|
||||
Menu(MortevielleEngine *vm);
|
||||
|
||||
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];
|
||||
MenuItem _discussMenu[9];
|
||||
MenuItem _inventoryMenu[9];
|
||||
MenuItem _moveMenu[8];
|
||||
|
||||
int _opcodeAttach;
|
||||
int _opcodeWait;
|
||||
int _opcodeForce;
|
||||
int _opcodeSleep;
|
||||
int _opcodeListen;
|
||||
int _opcodeEnter;
|
||||
int _opcodeClose;
|
||||
int _opcodeSearch;
|
||||
int _opcodeKnock;
|
||||
int _opcodeScratch;
|
||||
int _opcodeRead;
|
||||
int _opcodeEat;
|
||||
int _opcodePlace;
|
||||
int _opcodeOpen;
|
||||
int _opcodeTake;
|
||||
int _opcodeLook;
|
||||
int _opcodeSmell;
|
||||
int _opcodeSound;
|
||||
int _opcodeLeave;
|
||||
int _opcodeLift;
|
||||
int _opcodeTurn;
|
||||
int _opcodeSHide;
|
||||
int _opcodeSSearch;
|
||||
int _opcodeSRead;
|
||||
int _opcodeSPut;
|
||||
int _opcodeSLook;
|
||||
MenuItem _actionMenu[12];
|
||||
|
||||
void readVerbNums(Common::File &f, int dataSize);
|
||||
void setText(MenuItem item, Common::String name);
|
||||
void setDestinationText(int roomId);
|
||||
void setInventoryText();
|
||||
void disableMenuItem(MenuItem item);
|
||||
void enableMenuItem(MenuItem item);
|
||||
void displayMenu();
|
||||
void drawMenu();
|
||||
void menuUp(int msgId);
|
||||
void eraseMenu();
|
||||
void updateMenu();
|
||||
void initMenu();
|
||||
|
||||
void setSearchMenu();
|
||||
void unsetSearchMenu();
|
||||
};
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
#endif
|
||||
123
engines/mortevielle/metaengine.cpp
Normal file
123
engines/mortevielle/metaengine.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "mortevielle/mortevielle.h"
|
||||
#include "mortevielle/saveload.h"
|
||||
#include "mortevielle/detection.h"
|
||||
|
||||
namespace Mortevielle {
|
||||
|
||||
#ifdef USE_TTS
|
||||
static const ADExtraGuiOptionsMap optionsList[] = {
|
||||
{
|
||||
GAMEOPTION_TTS,
|
||||
{
|
||||
_s("Enable Text to Speech"),
|
||||
_s("Use TTS to read text in the game (if TTS is available)"),
|
||||
"tts_enabled",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
uint32 MortevielleEngine::getGameFlags() const { return _gameDescription->desc.flags; }
|
||||
|
||||
Common::Language MortevielleEngine::getLanguage() const { return _gameDescription->desc.language; }
|
||||
|
||||
Common::Language MortevielleEngine::getOriginalLanguage() const { return _gameDescription->originalLanguage; }
|
||||
|
||||
bool MortevielleEngine::useOriginalData() const { return _gameDescription->dataFeature == kUseOriginalData; }
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
|
||||
class MortevielleMetaEngine : public AdvancedMetaEngine<Mortevielle::MortevielleGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "mortevielle";
|
||||
}
|
||||
|
||||
#ifdef USE_TTS
|
||||
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
|
||||
return Mortevielle::optionsList;
|
||||
}
|
||||
#endif
|
||||
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const Mortevielle::MortevielleGameDescription *desc) const override;
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
|
||||
int getMaximumSaveSlot() const override;
|
||||
SaveStateList listSaves(const char *target) const override;
|
||||
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
|
||||
Common::String getSavegameFile(int saveGameIdx, const char *target) const override {
|
||||
if (!target)
|
||||
target = getName();
|
||||
if (saveGameIdx == kSavegameFilePattern)
|
||||
return Common::String::format("%s.###", target); // There is also sav0.mor for slot 0
|
||||
else
|
||||
return Mortevielle::MortevielleEngine::generateSaveFilename(target, saveGameIdx);
|
||||
}
|
||||
};
|
||||
|
||||
Common::Error MortevielleMetaEngine::createInstance(OSystem *syst, Engine **engine, const Mortevielle::MortevielleGameDescription *desc) const {
|
||||
*engine = new Mortevielle::MortevielleEngine(syst,desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool MortevielleMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
switch (f) {
|
||||
case kSupportsListSaves:
|
||||
case kSupportsDeleteSave:
|
||||
case kSupportsLoadingDuringStartup:
|
||||
case kSavesSupportMetaInfo:
|
||||
case kSavesSupportThumbnail:
|
||||
case kSavesSupportCreationDate:
|
||||
case kSimpleSavesNames:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int MortevielleMetaEngine::getMaximumSaveSlot() const { return 99; }
|
||||
|
||||
SaveStateList MortevielleMetaEngine::listSaves(const char *target) const {
|
||||
return Mortevielle::SavegameManager::listSaves(this, target);
|
||||
}
|
||||
|
||||
SaveStateDescriptor MortevielleMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
|
||||
Common::String filename = Mortevielle::MortevielleEngine::generateSaveFilename(target, slot);
|
||||
return Mortevielle::SavegameManager::querySaveMetaInfos(this, filename);
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(MORTEVIELLE)
|
||||
REGISTER_PLUGIN_DYNAMIC(MORTEVIELLE, PLUGIN_TYPE_ENGINE, MortevielleMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(MORTEVIELLE, PLUGIN_TYPE_ENGINE, MortevielleMetaEngine);
|
||||
#endif
|
||||
26
engines/mortevielle/module.mk
Normal file
26
engines/mortevielle/module.mk
Normal file
@@ -0,0 +1,26 @@
|
||||
MODULE := engines/mortevielle
|
||||
|
||||
MODULE_OBJS := \
|
||||
actions.o \
|
||||
debugger.o \
|
||||
dialogs.o \
|
||||
graphics.o \
|
||||
menu.o \
|
||||
metaengine.o \
|
||||
mortevielle.o \
|
||||
mouse.o \
|
||||
outtext.o \
|
||||
saveload.o \
|
||||
sound.o \
|
||||
utils.o
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_MORTEVIELLE), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
510
engines/mortevielle/mortevielle.cpp
Normal file
510
engines/mortevielle/mortevielle.cpp
Normal file
@@ -0,0 +1,510 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Mortville Manor DOS source code
|
||||
* Copyright (c) 1987-1989 Lankhor
|
||||
*/
|
||||
|
||||
#include "mortevielle/mortevielle.h"
|
||||
|
||||
#include "mortevielle/dialogs.h"
|
||||
#include "mortevielle/menu.h"
|
||||
#include "mortevielle/mouse.h"
|
||||
#include "mortevielle/outtext.h"
|
||||
#include "mortevielle/saveload.h"
|
||||
#include "mortevielle/outtext.h"
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "common/translation.h"
|
||||
#include "engines/util.h"
|
||||
#include "engines/engine.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
|
||||
namespace Mortevielle {
|
||||
|
||||
MortevielleEngine *g_vm;
|
||||
|
||||
MortevielleEngine::MortevielleEngine(OSystem *system, const MortevielleGameDescription *gameDesc):
|
||||
Engine(system), _gameDescription(gameDesc), _randomSource("mortevielle") {
|
||||
|
||||
g_vm = this;
|
||||
setDebugger(new Debugger(this));
|
||||
_dialogManager = new DialogManager(this);
|
||||
_screenSurface = new ScreenSurface(this);
|
||||
_mouse = new MouseHandler(this);
|
||||
_text = new TextHandler(this);
|
||||
_soundManager = new SoundManager(this, _mixer);
|
||||
_savegameManager = new SavegameManager(this);
|
||||
_menu = new Menu(this);
|
||||
|
||||
_lastGameFrame = 0;
|
||||
_mouseClick = false;
|
||||
_inMainGameLoop = false;
|
||||
_quitGame = false;
|
||||
_pauseStartTime = -1;
|
||||
|
||||
_roomPresenceLuc = false;
|
||||
_roomPresenceIda = false;
|
||||
_purpleRoomPresenceLeo = false;
|
||||
_roomPresenceGuy = false;
|
||||
_roomPresenceEva = false;
|
||||
_roomPresenceMax = false;
|
||||
_roomPresenceBob = false;
|
||||
_roomPresencePat = false;
|
||||
_toiletsPresenceBobMax = false;
|
||||
_bathRoomPresenceBobMax = false;
|
||||
_juliaRoomPresenceLeo = false;
|
||||
|
||||
_soundOff = false;
|
||||
_largestClearScreen = false;
|
||||
_hiddenHero = false;
|
||||
_heroSearching = false;
|
||||
_keyPressedEsc = false;
|
||||
_reloadCFIEC = false;
|
||||
|
||||
_outsideOnlyFl = true;
|
||||
_col = false;
|
||||
_syn = false;
|
||||
_obpart = false;
|
||||
_destinationOk = false;
|
||||
_anyone = false;
|
||||
_uptodatePresence = false;
|
||||
|
||||
_textColor = 0;
|
||||
_place = -1;
|
||||
|
||||
_x26KeyCount = -1;
|
||||
_caff = -1;
|
||||
_day = 0;
|
||||
|
||||
_curPict = nullptr;
|
||||
_curAnim = nullptr;
|
||||
_rightFramePict = nullptr;
|
||||
|
||||
resetCoreVar();
|
||||
|
||||
_maff = 0;
|
||||
_crep = 0;
|
||||
|
||||
_minute = 0;
|
||||
_curSearchObjId = 0;
|
||||
_controlMenu = 0;
|
||||
_startTime = 0;
|
||||
_endTime = 0;
|
||||
_roomDoorId = OWN_ROOM;
|
||||
_openObjCount = 0;
|
||||
_takeObjCount = 0;
|
||||
_num = 0;
|
||||
_searchCount = 0;
|
||||
_introSpeechPlayed = false;
|
||||
_inGameHourDuration = 0;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
_currentHourCount = 0;
|
||||
_currentTime = 0;
|
||||
_cfiecBuffer = nullptr;
|
||||
_cfiecBufferSize = 0;
|
||||
for (int i = 0; i < 601; i++) {
|
||||
_dialogHintArray[i]._hintId = 0;
|
||||
_dialogHintArray[i]._point = 0;
|
||||
}
|
||||
_currMenu = OPCODE_NONE;
|
||||
_currAction = OPCODE_NONE;
|
||||
_menuOpcode = OPCODE_NONE;
|
||||
_addFix = 0;
|
||||
_currBitIndex = 0;
|
||||
_currDay = 0;
|
||||
_currHour = 10;
|
||||
_currHalfHour = 0;
|
||||
_hour = 10;
|
||||
_key = 0;
|
||||
_manorDistance = 0;
|
||||
_numpal = 0;
|
||||
_savedBitIndex = 0;
|
||||
_endGame = false;
|
||||
_loseGame = false;
|
||||
_txxFileFl = false;
|
||||
_is = 0;
|
||||
}
|
||||
|
||||
MortevielleEngine::~MortevielleEngine() {
|
||||
delete _menu;
|
||||
delete _savegameManager;
|
||||
delete _soundManager;
|
||||
delete _text;
|
||||
delete _mouse;
|
||||
delete _screenSurface;
|
||||
delete _dialogManager;
|
||||
|
||||
free(_curPict);
|
||||
free(_curAnim);
|
||||
free(_rightFramePict);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether the engine supports given features
|
||||
*/
|
||||
bool MortevielleEngine::hasFeature(EngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsReturnToLauncher) ||
|
||||
(f == kSupportsLoadingDuringRuntime) ||
|
||||
(f == kSupportsSavingDuringRuntime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a game can currently be loaded
|
||||
*/
|
||||
bool MortevielleEngine::canLoadGameStateCurrently(Common::U32String *msg) {
|
||||
// Saving is only allowed in the main game event loop
|
||||
return _inMainGameLoop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a game can currently be saved
|
||||
*/
|
||||
bool MortevielleEngine::canSaveGameStateCurrently(Common::U32String *msg) {
|
||||
// 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, bool isAutosave) {
|
||||
if (slot == 0)
|
||||
return Common::kWritingFailed;
|
||||
|
||||
return _savegameManager->saveGame(slot, desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Support method that generates a savegame name
|
||||
* @param slot Slot number
|
||||
*/
|
||||
Common::String MortevielleEngine::generateSaveFilename(const Common::String &target, int slot) {
|
||||
if (slot == 0)
|
||||
// Initial game state loaded when the game starts
|
||||
return "sav0.mor";
|
||||
|
||||
return Common::String::format("%s.%03d", target.c_str(), slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the game.
|
||||
*/
|
||||
void MortevielleEngine::pauseEngineIntern(bool pause) {
|
||||
Engine::pauseEngineIntern(pause);
|
||||
if (pause) {
|
||||
if (_pauseStartTime == -1)
|
||||
_pauseStartTime = readclock();
|
||||
} else {
|
||||
if (_pauseStartTime != -1) {
|
||||
int pauseEndTime = readclock();
|
||||
_currentTime += (pauseEndTime - _pauseStartTime);
|
||||
if (_uptodatePresence)
|
||||
_startTime += (pauseEndTime - _pauseStartTime);
|
||||
}
|
||||
_pauseStartTime = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the game state
|
||||
*/
|
||||
Common::ErrorCode MortevielleEngine::initialize() {
|
||||
// Initialize graphics mode
|
||||
initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
|
||||
// Set up an intermediate screen surface
|
||||
_screenSurface->create(SCREEN_WIDTH, SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
_txxFileFl = false;
|
||||
// Load texts from TXX files
|
||||
loadTexts();
|
||||
|
||||
// Load the mort.dat resource
|
||||
Common::ErrorCode result = loadMortDat();
|
||||
if (result != Common::kNoError) {
|
||||
_screenSurface->free();
|
||||
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();
|
||||
|
||||
loadPalette();
|
||||
loadCFIPH();
|
||||
loadCFIEC();
|
||||
decodeNumber(&_cfiecBuffer[161 * 16], (_cfiecBufferSize - (161 * 16)) / 64);
|
||||
_x26KeyCount = 1;
|
||||
initMaxAnswer();
|
||||
initMouse();
|
||||
|
||||
loadPlaces();
|
||||
_soundOff = false;
|
||||
_largestClearScreen = false;
|
||||
|
||||
testKeyboard();
|
||||
showConfigScreen();
|
||||
testKeyboard();
|
||||
clearScreen();
|
||||
|
||||
_soundManager->loadNoise();
|
||||
_soundManager->loadAmbiantSounds();
|
||||
|
||||
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)) {
|
||||
GUIErrorMessageFormat(_("Unable to locate the '%s' engine data file."), MORT_DAT);
|
||||
return Common::kReadingFailed;
|
||||
}
|
||||
|
||||
// Validate the data file header
|
||||
char fileId[4];
|
||||
f.read(fileId, 4);
|
||||
if (strncmp(fileId, "MORT", 4) != 0) {
|
||||
GUIErrorMessageFormat(_("The '%s' engine data file is corrupt."), MORT_DAT);
|
||||
return Common::kReadingFailed;
|
||||
}
|
||||
|
||||
// Check the version
|
||||
int majVer = f.readByte();
|
||||
int minVer = f.readByte();
|
||||
|
||||
if (majVer < MORT_DAT_REQUIRED_VERSION) {
|
||||
GUIErrorMessageFormat(
|
||||
_("Incorrect version of the '%s' engine data file found. Expected %d.%d but got %d.%d."),
|
||||
MORT_DAT, MORT_DAT_REQUIRED_VERSION, 0, majVer, minVer);
|
||||
return Common::kReadingFailed;
|
||||
}
|
||||
|
||||
// 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 if (!strncmp(dataType, "VERB", 4)) {
|
||||
_menu->readVerbNums(f, dataSize);
|
||||
} 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 = MORTDAT_LANG_ENGLISH;
|
||||
break;
|
||||
case Common::FR_FRA:
|
||||
desiredLanguageId = MORTDAT_LANG_FRENCH;
|
||||
break;
|
||||
case Common::DE_DEU:
|
||||
desiredLanguageId = MORTDAT_LANG_GERMAN;
|
||||
break;
|
||||
default:
|
||||
warning("Language not supported, switching to English");
|
||||
desiredLanguageId = MORTDAT_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);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
Common::Error MortevielleEngine::run() {
|
||||
// Initialize the game
|
||||
Common::ErrorCode err = initialize();
|
||||
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();
|
||||
else {
|
||||
_caff = 51;
|
||||
_text->taffich();
|
||||
}
|
||||
|
||||
// Either load the initial game state savegame, or the specified savegame number
|
||||
adzon();
|
||||
resetVariables();
|
||||
if (loadSlot != 0)
|
||||
_savegameManager->loadSavegame(getSaveStateName(loadSlot));
|
||||
|
||||
// Run the main game loop
|
||||
mainGame();
|
||||
|
||||
// Cleanup (allocated in initialize())
|
||||
_screenSurface->free();
|
||||
free(_soundManager->_cfiphBuffer);
|
||||
free(_cfiecBuffer);
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the game introduction
|
||||
*/
|
||||
void MortevielleEngine::showIntroduction() {
|
||||
_dialogManager->displayIntroScreen(false);
|
||||
_dialogManager->checkForF8(142, false);
|
||||
if (shouldQuit())
|
||||
return;
|
||||
|
||||
_dialogManager->displayIntroFrame2();
|
||||
_dialogManager->checkForF8(143, true);
|
||||
if (shouldQuit())
|
||||
return;
|
||||
|
||||
showTitleScreen();
|
||||
music();
|
||||
_mixer->stopAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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], (_cfiecBufferSize - (161 * 16)) / 64);
|
||||
|
||||
_menu->initMenu();
|
||||
|
||||
charToHour();
|
||||
initGame();
|
||||
clearScreen();
|
||||
drawRightFrame();
|
||||
_mouse->showMouse();
|
||||
|
||||
// Loop to play the game
|
||||
do {
|
||||
playGame();
|
||||
if (shouldQuit())
|
||||
return;
|
||||
} 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();
|
||||
if (shouldQuit())
|
||||
return;
|
||||
} while (!((_quitGame) || (_endGame) || (_loseGame)));
|
||||
|
||||
if (_endGame)
|
||||
endGame();
|
||||
else if (_loseGame)
|
||||
askRestart();
|
||||
}
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
486
engines/mortevielle/mortevielle.h
Normal file
486
engines/mortevielle/mortevielle.h
Normal file
@@ -0,0 +1,486 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Mortville Manor DOS source code
|
||||
* Copyright (c) 1987-1989 Lankhor
|
||||
*/
|
||||
|
||||
#ifndef MORTEVIELLE_MORTEVIELLE_H
|
||||
#define MORTEVIELLE_MORTEVIELLE_H
|
||||
|
||||
#include "common/events.h"
|
||||
#include "common/file.h"
|
||||
#include "common/random.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/stack.h"
|
||||
#include "engines/engine.h"
|
||||
#include "common/error.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "mortevielle/debugger.h"
|
||||
#include "mortevielle/dialogs.h"
|
||||
#include "mortevielle/graphics.h"
|
||||
#include "mortevielle/menu.h"
|
||||
#include "mortevielle/mouse.h"
|
||||
#include "mortevielle/saveload.h"
|
||||
#include "mortevielle/sound.h"
|
||||
#include "mortevielle/outtext.h"
|
||||
#include "mortevielle/detection.h"
|
||||
|
||||
namespace Mortevielle {
|
||||
|
||||
// Debug channels
|
||||
enum {
|
||||
kMortevielleCore = 1,
|
||||
kMortevielleGraphics,
|
||||
kMortevielleSounds,
|
||||
};
|
||||
|
||||
// Game languages
|
||||
enum {
|
||||
MORTDAT_LANG_FRENCH = 0,
|
||||
MORTDAT_LANG_ENGLISH = 1,
|
||||
MORTDAT_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_OKAY = 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)
|
||||
#define DISK_ACCESS_DELAY 1000
|
||||
|
||||
const int kTime1 = 410;
|
||||
const int kTime2 = 250;
|
||||
|
||||
const int kAcha = 492;
|
||||
const int kFleche = 1758;
|
||||
|
||||
const int kAsoul = 154;
|
||||
const int kAouvr = 282;
|
||||
const int kAsearch = 387;
|
||||
const int kArcf = 1272;
|
||||
const int kArep = 1314;
|
||||
const int kAmzon = 1650;
|
||||
const int kArega = 0;
|
||||
|
||||
const int kMaxDialogIndex = 9000;
|
||||
const int kMaxDialogHint = 600;
|
||||
|
||||
const int kDescriptionStringIndex = 0; // Unused
|
||||
const int kInventoryStringIndex = 186;
|
||||
const int kQuestionStringIndex = 247;
|
||||
const int kDialogStringIndex = 292;
|
||||
const int kMenuPlaceStringIndex = 435;
|
||||
const int kStartingScreenStringIndex = 456;
|
||||
const int kMenuActionStringIndex = 476;
|
||||
const int kMenuSelfStringIndex = 497;
|
||||
const int kMenuSayStringIndex = 502;
|
||||
const int kMaxPatt = 20;
|
||||
|
||||
const int kResolutionScaler = 2;
|
||||
/*
|
||||
18 "It's already open$",
|
||||
26 "A photograph$"
|
||||
*/
|
||||
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, JULIA_ROOM = 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, COAT_ARMS = 27
|
||||
};
|
||||
|
||||
struct SaveStruct {
|
||||
int _faithScore;
|
||||
byte _pctHintFound[11];
|
||||
byte _availableQuestion[43];
|
||||
byte _inventory[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 MortevielleGameDescription *_gameDescription;
|
||||
Common::Stack<int> _keypresses;
|
||||
uint32 _lastGameFrame;
|
||||
Common::Point _mousePos;
|
||||
Common::StringArray _engineStrings;
|
||||
Common::StringArray _gameStrings;
|
||||
|
||||
int _menuOpcode;
|
||||
|
||||
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 _juliaRoomPresenceLeo;
|
||||
bool _hiddenHero;
|
||||
bool _heroSearching;
|
||||
bool _keyPressedEsc;
|
||||
bool _reloadCFIEC;
|
||||
bool _col;
|
||||
bool _syn;
|
||||
bool _obpart;
|
||||
bool _anyone;
|
||||
bool _uptodatePresence;
|
||||
|
||||
int _textColor;
|
||||
int _place;
|
||||
int _manorDistance;
|
||||
int _currBitIndex;
|
||||
int _currDay;
|
||||
int _currHour;
|
||||
int _currHalfHour;
|
||||
int _day;
|
||||
int _hour;
|
||||
int _minute;
|
||||
int _curSearchObjId;
|
||||
int _controlMenu;
|
||||
int _startTime;
|
||||
int _endTime;
|
||||
Common::Point _stdPal[91][17];
|
||||
|
||||
int _x26KeyCount;
|
||||
int _roomDoorId;
|
||||
int _openObjCount;
|
||||
int _takeObjCount;
|
||||
int _num;
|
||||
int _searchCount;
|
||||
bool _introSpeechPlayed;
|
||||
int _inGameHourDuration;
|
||||
int _x;
|
||||
int _y;
|
||||
int _currentHourCount;
|
||||
int _currentTime;
|
||||
int _pauseStartTime;
|
||||
|
||||
Common::String _hintPctMessage;
|
||||
byte *_cfiecBuffer;
|
||||
int _cfiecBufferSize;
|
||||
int _openObjects[7];
|
||||
uint16 _dialogIndexArray[kMaxDialogIndex + 1];
|
||||
Hint _dialogHintArray[kMaxDialogHint + 1];
|
||||
|
||||
Common::ErrorCode initialize();
|
||||
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 loadPalette();
|
||||
void loadTexts();
|
||||
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 faithScore, int roomId);
|
||||
void setPresenceFlags(int roomId);
|
||||
void testKey(bool d);
|
||||
void exitRoom();
|
||||
void getReadDescription(int objId);
|
||||
void getSearchDescription(int objId);
|
||||
int checkLeaveSecretPassage();
|
||||
void startDialog(int16 rep);
|
||||
void endSearch();
|
||||
int convertCharacterIndexToBitIndex(int characterIndex);
|
||||
int convertBitIndexToCharacterIndex(int bitIndex);
|
||||
void clearUpperLeftPart();
|
||||
void clearDescriptionBar();
|
||||
void clearVerbBar();
|
||||
void clearUpperRightPart();
|
||||
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 faithScore);
|
||||
void setRandomPresencePurpleRoom(int faithScore);
|
||||
void setRandomPresenceBlueRoom(int faithScore);
|
||||
void setRandomPresenceRedRoom(int faithScore);
|
||||
void setRandomPresenceJuliaRoom(int faithScore);
|
||||
void setRandomPresenceDiningRoom(int faithScore);
|
||||
void setRandomPresenceBureau(int faithScore);
|
||||
void setRandomPresenceKitchen(int faithScore);
|
||||
void setRandomPresenceAttic(int faithScore);
|
||||
void setRandomPresenceLanding(int faithScore);
|
||||
void setRandomPresenceChapel(int faithScore);
|
||||
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 handleOpcode();
|
||||
void prepareDisplayText();
|
||||
bool decryptNextChar(char &c, int &idx, byte &pt);
|
||||
void displayStatusArrow();
|
||||
void displayStatusInDescriptionBar(char stat);
|
||||
void displayQuestionText(Common::String s, int cmd);
|
||||
void displayTextInDescriptionBar(int x, int y, int nb, int mesgId);
|
||||
void displayTextInVerbBar(Common::String text);
|
||||
void displayTextBlock(Common::String text);
|
||||
void mapMessageId(int &mesgId);
|
||||
void resetOpenObjects();
|
||||
void setCoordinates(int sx);
|
||||
void drawPicture();
|
||||
void drawPictureWithText();
|
||||
void addObjectToInventory(int objectId);
|
||||
void putInHand(int &objId);
|
||||
void initMaxAnswer();
|
||||
void displayAnimFrame(int frameNum, int animId);
|
||||
int getFirstObject();
|
||||
void prepareNextObject();
|
||||
void putObject();
|
||||
void resetObjectPlace();
|
||||
void resetCoreVar();
|
||||
void drawDiscussionBox();
|
||||
void displayNarrativePicture(int af, int ob);
|
||||
void menuUp();
|
||||
void displayLookScreen(int objId);
|
||||
|
||||
void adzon();
|
||||
|
||||
public:
|
||||
Common::Point _prevPos;
|
||||
int _currMenu;
|
||||
int _currAction;
|
||||
int _drawingSizeArr[108];
|
||||
int _charAnswerCount[9];
|
||||
int _charAnswerMax[9];
|
||||
byte _tabdon[4001];
|
||||
bool _soundOff;
|
||||
bool _outsideOnlyFl;
|
||||
bool _destinationOk;
|
||||
bool _largestClearScreen;
|
||||
float _addFix;
|
||||
int _savedBitIndex;
|
||||
int _numpal;
|
||||
int _key;
|
||||
bool _mouseClick;
|
||||
SaveStruct _coreVar, _saveStruct;
|
||||
|
||||
int _maff;
|
||||
int _caff;
|
||||
int _crep;
|
||||
int _is; // ???
|
||||
|
||||
byte _destinationArray[7][25];
|
||||
|
||||
byte *_curPict;
|
||||
byte *_curAnim;
|
||||
byte *_rightFramePict;
|
||||
|
||||
PaletteManager _paletteManager;
|
||||
GfxSurface _backgroundSurface;
|
||||
Common::RandomSource _randomSource;
|
||||
|
||||
ScreenSurface *_screenSurface;
|
||||
SoundManager *_soundManager;
|
||||
SavegameManager *_savegameManager;
|
||||
Menu *_menu;
|
||||
MouseHandler *_mouse;
|
||||
TextHandler *_text;
|
||||
DialogManager *_dialogManager;
|
||||
|
||||
MortevielleEngine(OSystem *system, const MortevielleGameDescription *gameDesc);
|
||||
~MortevielleEngine() override;
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
Common::Error loadGameState(int slot) override;
|
||||
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
|
||||
Common::Error run() override;
|
||||
void pauseEngineIntern(bool pause) override;
|
||||
uint32 getGameFlags() const;
|
||||
Common::Language getLanguage() const;
|
||||
Common::Language getOriginalLanguage() const;
|
||||
bool useOriginalData() const;
|
||||
static Common::String generateSaveFilename(const Common::String &target, int slot);
|
||||
Common::String getSaveStateName(int slot) const override {
|
||||
return generateSaveFilename(_targetName, slot);
|
||||
}
|
||||
|
||||
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 delay(int amount);
|
||||
void gameLoaded();
|
||||
void initGame();
|
||||
void displayAloneText();
|
||||
void displayInterScreenMessage(int mesgId);
|
||||
void draw(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 displayPicture(const byte *pic, int x, int y);
|
||||
|
||||
int gettKeyPressed();
|
||||
void handleDescriptionText(int f, int mesgId);
|
||||
int getAnimOffset(int frameNum, int animNum);
|
||||
|
||||
void clearScreen();
|
||||
};
|
||||
|
||||
extern MortevielleEngine *g_vm;
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
|
||||
#endif
|
||||
268
engines/mortevielle/mouse.cpp
Normal file
268
engines/mortevielle/mouse.cpp
Normal file
@@ -0,0 +1,268 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Mortville Manor DOS source code
|
||||
* Copyright (c) 1987-1989 Lankhor
|
||||
*/
|
||||
|
||||
#include "mortevielle/mortevielle.h"
|
||||
#include "mortevielle/mouse.h"
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Mortevielle {
|
||||
|
||||
MouseHandler::MouseHandler(MortevielleEngine *vm) {
|
||||
_vm = vm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the mouse
|
||||
* @remarks Originally called 'init_mouse'
|
||||
*/
|
||||
void MouseHandler::initMouse() {
|
||||
_pos = Common::Point(0, 0);
|
||||
_vm->setMouseClick(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs up the area behind where the mouse cursor is to be drawn
|
||||
* @remarks Originally called 'hide_mouse'
|
||||
*/
|
||||
void MouseHandler::hideMouse() {
|
||||
// No implementation needed in ScummVM
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the mouse cursor
|
||||
* @remarks Originally called 'show_mouse'
|
||||
*/
|
||||
void MouseHandler::showMouse() {
|
||||
// ScummVM implementation uses CursorMan for drawing the cursor
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mouse position
|
||||
* @remarks Originally called 'pos_mouse'
|
||||
*/
|
||||
void MouseHandler::setMousePosition(Common::Point newPos) {
|
||||
if (newPos.x > 314 * kResolutionScaler)
|
||||
newPos.x = 314 * kResolutionScaler;
|
||||
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
|
||||
_vm->setMousePos(newPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mouse poisition
|
||||
* @remarks Originally called 'read_pos_mouse'
|
||||
*/
|
||||
void MouseHandler::getMousePosition(int &x, int &y, bool &click) {
|
||||
x = _vm->getMousePos().x;
|
||||
y = _vm->getMousePos().y;
|
||||
click = _vm->getMouseClick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move mouse
|
||||
* @remarks Originally called 'mov_mouse'
|
||||
*/
|
||||
void MouseHandler::moveMouse(bool &funct, char &key) {
|
||||
int cx, cy;
|
||||
bool click;
|
||||
|
||||
// Set defaults and check pending events
|
||||
funct = false;
|
||||
key = '\377';
|
||||
bool p_key = _vm->keyPressed();
|
||||
|
||||
// If mouse button clicked, return it
|
||||
if (_vm->getMouseClick())
|
||||
return;
|
||||
|
||||
// Handle any pending keypresses
|
||||
while (p_key) {
|
||||
if (_vm->shouldQuit())
|
||||
return;
|
||||
|
||||
char in1 = _vm->getChar();
|
||||
getMousePosition(cx, cy, click);
|
||||
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 * kResolutionScaler;
|
||||
cy = 1;
|
||||
break;
|
||||
case '3':
|
||||
cy = 190;
|
||||
cx = 315 * kResolutionScaler;
|
||||
break;
|
||||
case '5':
|
||||
cy = 100;
|
||||
cx = 155 * kResolutionScaler;
|
||||
break;
|
||||
case ' ':
|
||||
case '\15':
|
||||
_vm->setMouseClick(true);
|
||||
return;
|
||||
break;
|
||||
case '\33':
|
||||
p_key = _vm->keyPressed();
|
||||
|
||||
if (p_key) {
|
||||
char in2 = _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 = kResolutionScaler * 32;
|
||||
cy = 8;
|
||||
break;
|
||||
case 'D':
|
||||
cx = 80 * kResolutionScaler;
|
||||
cy = 8;
|
||||
break;
|
||||
case 'A':
|
||||
cx = 126 * kResolutionScaler;
|
||||
cy = 8;
|
||||
break;
|
||||
case 'S':
|
||||
cx = 174 * kResolutionScaler;
|
||||
cy = 8;
|
||||
break;
|
||||
case 'P':
|
||||
cx = 222 * kResolutionScaler;
|
||||
cy = 8;
|
||||
break;
|
||||
case 'F':
|
||||
cx = kResolutionScaler * 270;
|
||||
cy = 8;
|
||||
break;
|
||||
case '\23':
|
||||
_vm->_soundOff = !_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 = _vm->keyPressed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse function : Is mouse in a given rect?
|
||||
* @remarks Originally called 'dans_rect'
|
||||
*/
|
||||
bool MouseHandler::isMouseIn(Common::Rect r) {
|
||||
int x, y;
|
||||
bool click;
|
||||
|
||||
getMousePosition(x, y, click);
|
||||
if ((x > r.left) && (x < r.right) && (y > r.top) && (y < r.bottom))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
52
engines/mortevielle/mouse.h
Normal file
52
engines/mortevielle/mouse.h
Normal file
@@ -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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 MortevielleEngine;
|
||||
|
||||
class MouseHandler {
|
||||
private:
|
||||
MortevielleEngine *_vm;
|
||||
public:
|
||||
Common::Point _pos;
|
||||
MouseHandler(MortevielleEngine *vm);
|
||||
|
||||
void initMouse();
|
||||
void hideMouse();
|
||||
void showMouse();
|
||||
void setMousePosition(Common::Point newPos);
|
||||
void getMousePosition(int &x, int &y, bool &click);
|
||||
void moveMouse(bool &funct, char &key);
|
||||
bool isMouseIn(Common::Rect r);
|
||||
};
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
#endif
|
||||
331
engines/mortevielle/outtext.cpp
Normal file
331
engines/mortevielle/outtext.cpp
Normal file
@@ -0,0 +1,331 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Mortville Manor DOS source code
|
||||
* Copyright (c) 1987-1989 Lankhor
|
||||
*/
|
||||
|
||||
#include "mortevielle/mortevielle.h"
|
||||
#include "mortevielle/mouse.h"
|
||||
#include "mortevielle/outtext.h"
|
||||
#include "mortevielle/graphics.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Mortevielle {
|
||||
|
||||
TextHandler::TextHandler(MortevielleEngine *vm) {
|
||||
_vm = vm;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
Common::String s;
|
||||
int i, j;
|
||||
|
||||
// Safeguard: add $ just in case
|
||||
inputStr += '$';
|
||||
|
||||
_vm->_screenSurface->putxy(x, y);
|
||||
int 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 '@':
|
||||
_vm->_screenSurface->drawString(s, typ);
|
||||
s = "";
|
||||
++p;
|
||||
xc = x;
|
||||
yc += 6;
|
||||
_vm->_screenSurface->putxy(xc, yc);
|
||||
break;
|
||||
case ' ':
|
||||
s += ' ';
|
||||
xc += tab;
|
||||
++p;
|
||||
if (nextWord(p, inputStr.c_str(), tab) + xc > xf) {
|
||||
_vm->_screenSurface->drawString(s, typ);
|
||||
s = "";
|
||||
xc = x;
|
||||
yc += 6;
|
||||
if (yc > yf) {
|
||||
while (!_vm->keyPressed())
|
||||
;
|
||||
i = y;
|
||||
do {
|
||||
j = x;
|
||||
do {
|
||||
_vm->_screenSurface->putxy(j, i);
|
||||
_vm->_screenSurface->drawString(" ", 0);
|
||||
j += 6;
|
||||
} while (j <= xf);
|
||||
i += 6;
|
||||
} while (i <= yf);
|
||||
yc = y;
|
||||
}
|
||||
_vm->_screenSurface->putxy(xc, yc);
|
||||
}
|
||||
break;
|
||||
case '$':
|
||||
stringParsed = true;
|
||||
_vm->_screenSurface->drawString(s, typ);
|
||||
break;
|
||||
default:
|
||||
s += inputStr[p];
|
||||
++p;
|
||||
xc += tab;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load DES (picture container) file
|
||||
* @remarks Originally called 'chardes'
|
||||
*/
|
||||
void TextHandler::loadPictureFile(const Common::Path &filename, const Common::Path &altFilename, int32 skipSize, int length) {
|
||||
Common::File f;
|
||||
if (!f.open(filename)) {
|
||||
if (!f.open(altFilename))
|
||||
error("Missing file: Either %s or %s", filename.toString().c_str(), altFilename.toString().c_str());
|
||||
}
|
||||
// HACK: The original game contains a bug in the 2nd intro screen, in German DOS version.
|
||||
// The size specified in the fxx array is wrong (too short). In order to fix it, we are using
|
||||
// the value -1 to force a variable read length.
|
||||
if (length == -1)
|
||||
length = f.size() - skipSize;
|
||||
|
||||
assert(skipSize + length <= f.size());
|
||||
|
||||
free(_vm->_curPict);
|
||||
_vm->_curPict = (byte *)malloc(sizeof(byte) * length);
|
||||
f.seek(skipSize);
|
||||
f.read(_vm->_curPict, length);
|
||||
f.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load ANI file
|
||||
* @remarks Originally called 'charani'
|
||||
*/
|
||||
void TextHandler::loadAniFile(const Common::Path &filename, int32 skipSize, int length) {
|
||||
Common::File f;
|
||||
if (!f.open(filename))
|
||||
error("Missing file - %s", filename.toString().c_str());
|
||||
|
||||
assert(skipSize + length <= f.size());
|
||||
|
||||
free(_vm->_curAnim);
|
||||
_vm->_curAnim = (byte *)malloc(sizeof(byte) * length);
|
||||
f.seek(skipSize);
|
||||
f.read(_vm->_curAnim, length);
|
||||
f.close();
|
||||
}
|
||||
|
||||
void TextHandler::taffich() {
|
||||
static const byte tran1[] = { 121, 121, 138, 139, 120 };
|
||||
static const byte tran2[] = { 150, 150, 152, 152, 100, 110, 159, 100, 100 };
|
||||
|
||||
int cx, drawingSize, npal;
|
||||
int32 drawingStartPos;
|
||||
|
||||
int a = _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 (_vm->_maff == a)
|
||||
return;
|
||||
|
||||
switch (a) {
|
||||
case 16:
|
||||
_vm->_coreVar._pctHintFound[9] = '*';
|
||||
_vm->_coreVar._availableQuestion[42] = '*';
|
||||
break;
|
||||
case 20:
|
||||
_vm->_coreVar._availableQuestion[39] = '*';
|
||||
if (_vm->_coreVar._availableQuestion[36] == '*') {
|
||||
_vm->_coreVar._pctHintFound[3] = '*';
|
||||
_vm->_coreVar._availableQuestion[38] = '*';
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
_vm->_coreVar._availableQuestion[37] = '*';
|
||||
break;
|
||||
case 30:
|
||||
_vm->_coreVar._availableQuestion[9] = '*';
|
||||
break;
|
||||
case 31: // Coat of arms
|
||||
_vm->_coreVar._pctHintFound[4] = '*';
|
||||
_vm->_coreVar._availableQuestion[35] = '*';
|
||||
break;
|
||||
case 118:
|
||||
_vm->_coreVar._availableQuestion[41] = '*';
|
||||
break;
|
||||
case 143:
|
||||
_vm->_coreVar._pctHintFound[1] = '*';
|
||||
break;
|
||||
case 150:
|
||||
_vm->_coreVar._availableQuestion[34] = '*';
|
||||
break;
|
||||
case 151:
|
||||
_vm->_coreVar._pctHintFound[2] = '*';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_vm->_destinationOk = true;
|
||||
_vm->_mouse->hideMouse();
|
||||
drawingStartPos = 0;
|
||||
Common::Path filename, altFilename;
|
||||
|
||||
if ((a != 50) && (a != 51)) {
|
||||
int m = a + 2000;
|
||||
|
||||
if ((m > 2001) && (m < 2010))
|
||||
m = 2001;
|
||||
else if (m == 2011)
|
||||
m = 2010;
|
||||
if (a == 32)
|
||||
m = 2034;
|
||||
else if ((a == 17) && (_vm->_maff == 14))
|
||||
m = 2018;
|
||||
else if (a > 99) {
|
||||
if ((_vm->_is == 1) || (_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;
|
||||
else if ( ((a < 27) && ( ((_vm->_maff > 69) && (!_vm->_coreVar._alreadyEnteredManor)) || (_vm->_maff > 99) )) || ((_vm->_maff > 29) && (_vm->_maff < 33)) )
|
||||
m = 2033;
|
||||
|
||||
_vm->displayInterScreenMessage(m);
|
||||
_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)
|
||||
drawingStartPos += _vm->_drawingSizeArr[cx];
|
||||
drawingSize = _vm->_drawingSizeArr[a];
|
||||
|
||||
altFilename = filename = "DXX.mor";
|
||||
} else {
|
||||
filename = "DZZ.mor";
|
||||
altFilename = "DZZALL";
|
||||
|
||||
if (a == 50) {
|
||||
// First intro screen
|
||||
drawingStartPos = 0;
|
||||
drawingSize = _vm->_drawingSizeArr[87];
|
||||
} else { // a == 51
|
||||
// Second intro screen
|
||||
drawingStartPos = _vm->_drawingSizeArr[87];
|
||||
// HACK: Force a variable size in order to fix the wrong size used by the German version
|
||||
drawingSize = -1;
|
||||
}
|
||||
_vm->_maff = a;
|
||||
npal = a + 37;
|
||||
}
|
||||
loadPictureFile(filename, altFilename, drawingStartPos, drawingSize);
|
||||
_vm->_numpal = npal;
|
||||
_vm->setPal(npal);
|
||||
|
||||
if ((b < 15) || (b == 16) || (b == 17) || (b == 24) || (b == 26) || (b == 50)) {
|
||||
drawingStartPos = 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)
|
||||
drawingStartPos += _vm->_drawingSizeArr[cx + 89];
|
||||
drawingSize = _vm->_drawingSizeArr[b + 89];
|
||||
filename = "AXX.mor";
|
||||
} else { // b == 50
|
||||
// CHECKME: the size of AZZ.mor is 1280 for the DOS version
|
||||
// and 1260 for the Amiga version. Maybe the 20 bytes
|
||||
// are a filler (to get 10 blocks of 128 bytes),
|
||||
// or the size should be variable.
|
||||
drawingSize = 1260;
|
||||
filename = "AZZ.mor";
|
||||
}
|
||||
loadAniFile(filename, drawingStartPos, drawingSize);
|
||||
}
|
||||
_vm->_mouse->showMouse();
|
||||
if ((a < COAT_ARMS) && ((_vm->_maff < COAT_ARMS) || (_vm->_coreVar._currPlace == LANDING)) && (_vm->_currAction != _vm->_menu->_opcodeEnter)) {
|
||||
if ((a == ATTIC) || (a == CELLAR))
|
||||
_vm->displayAloneText();
|
||||
else if (!_vm->_outsideOnlyFl)
|
||||
_vm->getPresence(_vm->_coreVar._currPlace);
|
||||
_vm->_savedBitIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
49
engines/mortevielle/outtext.h
Normal file
49
engines/mortevielle/outtext.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
class MortevielleEngine;
|
||||
|
||||
class TextHandler {
|
||||
private:
|
||||
MortevielleEngine *_vm;
|
||||
int nextWord(int p, const char *ch, int &tab);
|
||||
public:
|
||||
TextHandler (MortevielleEngine *vm);
|
||||
|
||||
void displayStr(Common::String inputStr, int x, int y, int dx, int dy, int typ);
|
||||
void loadPictureFile(const Common::Path &filename, const Common::Path &altFilename, int32 skipSize, int length);
|
||||
void loadAniFile(const Common::Path &filename, int32 skipSize, int length);
|
||||
void taffich();
|
||||
};
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
#endif
|
||||
332
engines/mortevielle/saveload.cpp
Normal file
332
engines/mortevielle/saveload.cpp
Normal file
@@ -0,0 +1,332 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Mortville Manor DOS source code
|
||||
* Copyright (c) 1987-1989 Lankhor
|
||||
*/
|
||||
|
||||
#include "mortevielle/mortevielle.h"
|
||||
#include "mortevielle/dialogs.h"
|
||||
#include "mortevielle/mouse.h"
|
||||
#include "mortevielle/saveload.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "graphics/paletteman.h"
|
||||
|
||||
namespace Mortevielle {
|
||||
|
||||
static const char SAVEGAME_ID[4] = { 'M', 'O', 'R', 'T' };
|
||||
|
||||
SavegameManager::SavegameManager(MortevielleEngine *vm) {
|
||||
_vm = vm;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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._pctHintFound[i]);
|
||||
for (int i = 0; i < 43; ++i)
|
||||
sz.syncAsByte(g_vm->_saveStruct._availableQuestion[i]);
|
||||
for (int i = 0; i < 31; ++i)
|
||||
sz.syncAsByte(g_vm->_saveStruct._inventory[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'
|
||||
*/
|
||||
bool SavegameManager::loadSavegame(const Common::String &filename) {
|
||||
// Try loading first from the save area
|
||||
Common::SeekableReadStream *stream = g_system->getSavefileManager()->openForLoading(filename);
|
||||
|
||||
Common::File f;
|
||||
if (stream == nullptr) {
|
||||
if (!f.open(Common::Path(filename))) {
|
||||
warning("Unable to open save file '%s'", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
if (!readSavegameHeader(stream, header)) {
|
||||
delete stream;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
stream->seek(0);
|
||||
}
|
||||
|
||||
// Read the game contents
|
||||
Common::Serializer sz(stream, nullptr);
|
||||
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;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a saved game
|
||||
*/
|
||||
Common::Error SavegameManager::loadGame(const Common::String &filename) {
|
||||
g_vm->_mouse->hideMouse();
|
||||
g_vm->displayEmptyHand();
|
||||
if (loadSavegame(filename)) {
|
||||
/* Initialization */
|
||||
g_vm->charToHour();
|
||||
g_vm->initGame();
|
||||
g_vm->gameLoaded();
|
||||
g_vm->_mouse->showMouse();
|
||||
return Common::kNoError;
|
||||
} else
|
||||
return Common::kUnknownError;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = _vm->getSaveStateName(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(nullptr, 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;
|
||||
}
|
||||
|
||||
Common::Error SavegameManager::loadGame(int slot) {
|
||||
return loadGame(_vm->getSaveStateName(slot));
|
||||
}
|
||||
|
||||
Common::Error SavegameManager::saveGame(int slot) {
|
||||
return saveGame(slot, _vm->getSaveStateName(slot));
|
||||
}
|
||||
|
||||
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.getPixels(), 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);
|
||||
}
|
||||
|
||||
WARN_UNUSED_RESULT bool SavegameManager::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail) {
|
||||
// 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
|
||||
if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) {
|
||||
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 MetaEngine *metaEngine, const Common::String &target) {
|
||||
Common::String pattern = target;
|
||||
pattern += ".###";
|
||||
|
||||
Common::StringArray files = g_system->getSavefileManager()->listSavefiles(pattern);
|
||||
|
||||
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() + fname.size() - 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) {
|
||||
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(metaEngine, slotNumber, saveDescription));
|
||||
|
||||
delete in;
|
||||
}
|
||||
}
|
||||
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
|
||||
SaveStateDescriptor SavegameManager::querySaveMetaInfos(const MetaEngine *metaEngine, const Common::String &fileName) {
|
||||
Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
|
||||
|
||||
if (f) {
|
||||
// Get the slot number
|
||||
int slot = 1;
|
||||
if (fileName.size() > 4 && fileName[fileName.size() - 4] == '.')
|
||||
slot = atoi(fileName.c_str() + fileName.size() - 3);
|
||||
|
||||
// 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;
|
||||
|
||||
return SaveStateDescriptor(metaEngine, slot, Common::String::format("Savegame - %03d", slot));
|
||||
} else {
|
||||
// Get the savegame header information
|
||||
SavegameHeader header;
|
||||
if (!readSavegameHeader(f, header, false)) {
|
||||
delete f;
|
||||
return SaveStateDescriptor();
|
||||
}
|
||||
delete f;
|
||||
|
||||
// Create the return descriptor
|
||||
SaveStateDescriptor desc(metaEngine, 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
|
||||
73
engines/mortevielle/saveload.h
Normal file
73
engines/mortevielle/saveload.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/scaler.h"
|
||||
#include "graphics/thumbnail.h"
|
||||
#include "engines/savestate.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 MortevielleEngine;
|
||||
|
||||
class SavegameManager {
|
||||
private:
|
||||
MortevielleEngine *_vm;
|
||||
byte _tabdonSaveBuffer[391];
|
||||
|
||||
void sync_save(Common::Serializer &sz);
|
||||
public:
|
||||
SavegameManager(MortevielleEngine *vm);
|
||||
|
||||
bool loadSavegame(const Common::String &filename);
|
||||
Common::Error loadGame(const Common::String &filename);
|
||||
Common::Error saveGame(int n, const Common::String &saveName);
|
||||
Common::Error loadGame(int slot);
|
||||
Common::Error saveGame(int slot);
|
||||
|
||||
void writeSavegameHeader(Common::OutSaveFile *out, const Common::String &saveName);
|
||||
WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail = true);
|
||||
static SaveStateList listSaves(const MetaEngine *metaEngine, const Common::String &target);
|
||||
static SaveStateDescriptor querySaveMetaInfos(const MetaEngine *metaEngine, const Common::String &fileName);
|
||||
};
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
#endif
|
||||
853
engines/mortevielle/sound.cpp
Normal file
853
engines/mortevielle/sound.cpp
Normal file
@@ -0,0 +1,853 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on original Mortville Manor DOS source code
|
||||
* Copyright (c) 1987-1989 Lankhor
|
||||
*/
|
||||
|
||||
#include "mortevielle/mortevielle.h"
|
||||
#include "mortevielle/sound.h"
|
||||
#include "mortevielle/dialogs.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/text-to-speech.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};
|
||||
|
||||
SoundManager::SoundManager(MortevielleEngine *vm, Audio::Mixer *mixer) {
|
||||
_vm = vm;
|
||||
_mixer = mixer;
|
||||
_audioStream = nullptr;
|
||||
_ambiantNoiseBuf = nullptr;
|
||||
_noiseBuf = nullptr;
|
||||
_ttsMan = g_system->getTextToSpeechManager();
|
||||
if (_ttsMan) {
|
||||
_ttsMan->setLanguage(ConfMan.get("language"));
|
||||
_ttsMan->enable(ConfMan.getBool("tts_enabled"));
|
||||
_ttsMan->stop();
|
||||
_ttsMan->setRate(0);
|
||||
_ttsMan->setPitch(0);
|
||||
_ttsMan->setVolume(100);
|
||||
}
|
||||
|
||||
_soundType = 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;
|
||||
}
|
||||
_buildingSentence = false;
|
||||
_ptr_oct = 0;
|
||||
_cfiphBuffer = nullptr;
|
||||
}
|
||||
|
||||
SoundManager::~SoundManager() {
|
||||
if (_audioStream)
|
||||
_audioStream->finish();
|
||||
free(_ambiantNoiseBuf);
|
||||
free(_noiseBuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode music data
|
||||
*/
|
||||
int SoundManager::decodeMusic(const byte *PSrc, byte *PDest, int size) {
|
||||
static const int tab[16] = { -96, -72, -48, -32, -20, -12, -8, -4, 0, 4, 8, 12, 20, 32, 48, 72 };
|
||||
|
||||
uint seed = 128;
|
||||
int decompSize = 0;
|
||||
int skipSize = 0;
|
||||
|
||||
for (int idx1 = 0; idx1 < size; ++idx1) {
|
||||
byte srcByte = *PSrc++;
|
||||
int v = tab[srcByte >> 4];
|
||||
seed += v;
|
||||
*PDest++ = seed & 0xff;
|
||||
|
||||
v = tab[srcByte & 0xf];
|
||||
seed += v;
|
||||
*PDest++ = seed & 0xff;
|
||||
|
||||
if (srcByte == 0)
|
||||
skipSize += 2;
|
||||
else {
|
||||
decompSize += skipSize + 2;
|
||||
skipSize = 0;
|
||||
}
|
||||
}
|
||||
return decompSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load sonmus.mor file
|
||||
* @remarks Originally called 'charge_son'
|
||||
*/
|
||||
void SoundManager::loadAmbiantSounds() {
|
||||
Common::File f;
|
||||
if (!f.open("sonmus.mor"))
|
||||
error("Missing file - sonmus.mor");
|
||||
|
||||
free(_ambiantNoiseBuf);
|
||||
int size = f.size();
|
||||
byte *compMusicBuf1 = (byte *)malloc(sizeof(byte) * size);
|
||||
_ambiantNoiseBuf = (byte *)malloc(sizeof(byte) * size * 2);
|
||||
f.read(compMusicBuf1, size);
|
||||
f.close();
|
||||
|
||||
decodeMusic(compMusicBuf1, _ambiantNoiseBuf, size);
|
||||
free(compMusicBuf1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Speech function - Load Noise files
|
||||
* @remarks Originally called 'charge_bruit' and 'charge_bruit5'
|
||||
*/
|
||||
void SoundManager::loadNoise() {
|
||||
Common::File f1, f5;
|
||||
|
||||
if (!f5.open("bruit5"))
|
||||
error("Missing file - bruit5");
|
||||
|
||||
if (f1.open("bruits")) { //Translation: "noise"
|
||||
assert(f1.size() > 32000);
|
||||
_noiseBuf = (byte *)malloc(sizeof(byte) * (f1.size() + f5.size()));
|
||||
|
||||
f1.read(_noiseBuf, 32000); // 250 * 128
|
||||
f5.read(&_noiseBuf[32000], f5.size());
|
||||
f1.read(&_noiseBuf[32000 + f5.size()], f1.size() - 32000); // 19072
|
||||
|
||||
f1.close();
|
||||
} else {
|
||||
Common::File f2, f3, f4;
|
||||
if (!f1.open("bruit1") || !f2.open("bruit2") || !f3.open("bruit3") || !f4.open("bruit4"))
|
||||
error("Missing file - bruits");
|
||||
|
||||
assert(f4.size() == 32000);
|
||||
_noiseBuf = (byte *)malloc(sizeof(byte) * (f1.size() + f2.size() + f3.size() + f4.size() + f5.size()));
|
||||
|
||||
f4.read(_noiseBuf, f4.size());
|
||||
int pos = f4.size();
|
||||
f5.read(&_noiseBuf[pos], f5.size());
|
||||
pos += f5.size();
|
||||
f1.read(&_noiseBuf[pos], f1.size());
|
||||
pos += f1.size();
|
||||
f2.read(&_noiseBuf[pos], f2.size());
|
||||
pos += f2.size();
|
||||
f3.read(&_noiseBuf[pos], f3.size());
|
||||
|
||||
f1.close();
|
||||
f2.close();
|
||||
f3.close();
|
||||
f4.close();
|
||||
}
|
||||
f5.close();
|
||||
}
|
||||
|
||||
void SoundManager::regenbruit() {
|
||||
int i = 69876;
|
||||
for (int j = 0; j < 100; j++) {
|
||||
_cfiphBuffer[j] = READ_BE_UINT16(&_noiseBuf[i]);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::litph(tablint &t, int typ, int tempo) {
|
||||
if (!_buildingSentence) {
|
||||
if (_mixer->isSoundHandleActive(_soundHandle))
|
||||
_mixer->stopHandle(_soundHandle);
|
||||
if (_ttsMan) {
|
||||
if (_ttsMan->isSpeaking())
|
||||
_ttsMan->stop();
|
||||
}
|
||||
_buildingSentence = true;
|
||||
}
|
||||
int freq = tempo * 252; // 25.2 * 10
|
||||
int i = 0;
|
||||
while (i < _ptr_oct) {
|
||||
int idx = _troctBuf[i];
|
||||
i++;
|
||||
switch(idx) {
|
||||
case 0: {
|
||||
int val = _troctBuf[i];
|
||||
i++;
|
||||
if (_soundType == 1) {
|
||||
debugC(5, kMortevielleSounds, "litph - duson");
|
||||
const static int noiseAdr[] = {0, 17224,
|
||||
17224, 33676,
|
||||
33676, 51014,
|
||||
51014, 59396,
|
||||
59396, 61286,
|
||||
61286, 69875};
|
||||
if (val > 5) {
|
||||
warning("unhandled index %d", val);
|
||||
} else {
|
||||
if (!_audioStream)
|
||||
_audioStream = Audio::makeQueuingAudioStream(freq, false);
|
||||
_audioStream->queueBuffer(&_noiseBuf[noiseAdr[val * 2]], noiseAdr[(val * 2) + 1] - noiseAdr[(val * 2)], DisposeAfterUse::NO, Audio::FLAG_UNSIGNED);
|
||||
}
|
||||
} else { // 2
|
||||
debugC(5, kMortevielleSounds, "litph - vadson");
|
||||
const static int ambiantNoiseAdr[] = {0, 14020,
|
||||
14020, 18994,
|
||||
18994, 19630,
|
||||
19630, 22258,
|
||||
22258, 37322,
|
||||
37322, 44472,
|
||||
44472, 52324,
|
||||
52324, 59598,
|
||||
59598, 69748};
|
||||
if (val > 8) {
|
||||
warning("unhandled index %d", val);
|
||||
} else {
|
||||
if (!_audioStream)
|
||||
_audioStream = Audio::makeQueuingAudioStream(freq, false);
|
||||
_audioStream->queueBuffer(&_ambiantNoiseBuf[ambiantNoiseAdr[val * 2]], ambiantNoiseAdr[(val * 2) + 1] - ambiantNoiseAdr[(val * 2)], DisposeAfterUse::NO, Audio::FLAG_UNSIGNED);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
int val = _troctBuf[i];
|
||||
i++;
|
||||
int tmpidx = (val * 12) + 268;
|
||||
val = _troctBuf[i];
|
||||
i++;
|
||||
warning("TODO: reech %d %d", tmpidx, val);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (_soundType) {
|
||||
i += 2;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
warning("TODO: pari2");
|
||||
i += 2;
|
||||
break;
|
||||
default:
|
||||
static byte emptyBuf[19] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
if (idx == 62)
|
||||
warning("TODO: blab");
|
||||
else if (idx == 32) {
|
||||
if (!_audioStream)
|
||||
_audioStream = Audio::makeQueuingAudioStream(freq, false);
|
||||
_audioStream->queueBuffer(emptyBuf, 19, DisposeAfterUse::NO, Audio::FLAG_UNSIGNED);
|
||||
} else if (idx == 35) {
|
||||
if (i < _ptr_oct)
|
||||
warning("unexpected 35 - stop the buffering");
|
||||
i = _ptr_oct;
|
||||
} else if (idx == 46) {
|
||||
if (!_audioStream)
|
||||
_audioStream = Audio::makeQueuingAudioStream(freq, false);
|
||||
for (int j = 0; j < 10; j++)
|
||||
_audioStream->queueBuffer(emptyBuf, 19, DisposeAfterUse::NO, Audio::FLAG_UNSIGNED);
|
||||
} else {
|
||||
warning("Other code: %d - %d %d", idx, _troctBuf[i], _troctBuf[i + 1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::playSong(const byte* buf, uint size, uint loops) {
|
||||
int freq = kTempoMusic * 252; // 25.2 * 10
|
||||
Audio::SeekableAudioStream *raw = Audio::makeRawStream(buf, size, freq, Audio::FLAG_UNSIGNED, DisposeAfterUse::NO);
|
||||
Audio::AudioStream *stream = Audio::makeLoopingAudioStream(raw, loops);
|
||||
Audio::SoundHandle songHandle;
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &songHandle, stream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::YES);
|
||||
|
||||
while (_mixer->isSoundHandleActive(songHandle) && !_vm->keyPressed() && !_vm->_mouseClick && !_vm->shouldQuit())
|
||||
;
|
||||
// In case the handle is still active, stop it.
|
||||
_mixer->stopHandle(songHandle);
|
||||
}
|
||||
|
||||
void SoundManager::spfrac(int wor) {
|
||||
_queue[2]._rep = (uint)wor >> 12;
|
||||
_queue[2]._freq = ((uint)wor >> 6) & 7;
|
||||
_queue[2]._acc = ((uint)wor >> 9) & 7;
|
||||
}
|
||||
|
||||
void SoundManager::charg_car(int &currWordNumb) {
|
||||
assert(currWordNumb < 1712);
|
||||
int wor = READ_BE_UINT16(&_wordBuf[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_ -= 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 SoundManager::entroct(byte o) {
|
||||
assert(_ptr_oct < 10576);
|
||||
_troctBuf[_ptr_oct] = o;
|
||||
++_ptr_oct;
|
||||
}
|
||||
|
||||
void SoundManager::cctable(tablint &t) {
|
||||
float tb[257];
|
||||
|
||||
tb[0] = 0;
|
||||
for (int k = 0; k <= 255; ++k) {
|
||||
tb[k + 1] = _vm->_addFix + tb[k];
|
||||
t[255 - k] = abs((int)tb[k] + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load phoneme sound file
|
||||
* @remarks Originally called 'charge_phbruit'
|
||||
*/
|
||||
void SoundManager::loadPhonemeSounds() {
|
||||
Common::File f;
|
||||
|
||||
if (!f.open("phbrui.mor"))
|
||||
error("Missing file - phbrui.mor");
|
||||
|
||||
for (int i = 1; i <= f.size() / 2; ++i)
|
||||
_cfiphBuffer[i] = f.readUint16BE();
|
||||
|
||||
f.close();
|
||||
}
|
||||
|
||||
void SoundManager::trait_car() {
|
||||
byte d3;
|
||||
int d2, i;
|
||||
|
||||
switch (_queue[1]._code) {
|
||||
case 9:
|
||||
if (_queue[1]._val != (int)'#') {
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
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:
|
||||
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)) {
|
||||
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:
|
||||
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)) {
|
||||
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 SoundManager::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 SoundManager::initQueue() {
|
||||
_queue[2]._rep = 0;
|
||||
_queue[2]._freq = 0;
|
||||
_queue[2]._acc = 0;
|
||||
moveQueue();
|
||||
moveQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a phoneme
|
||||
* @remarks Originally called 'trait_ph'
|
||||
*/
|
||||
void SoundManager::handlePhoneme() {
|
||||
const uint16 deca[3] = {300, 30, 40};
|
||||
|
||||
uint16 startPos = _cfiphBuffer[_phonemeNumb - 1] + deca[_soundType];
|
||||
uint16 endPos = _cfiphBuffer[_phonemeNumb] + deca[_soundType];
|
||||
int wordCount = endPos - startPos;
|
||||
|
||||
startPos /= 2;
|
||||
endPos /= 2;
|
||||
assert((endPos - startPos) < 1711);
|
||||
for (int i = startPos, currWord = 0; i < endPos; i++, currWord += 2)
|
||||
WRITE_BE_UINT16(&_wordBuf[currWord], _cfiphBuffer[i]);
|
||||
|
||||
_ptr_oct = 0;
|
||||
int currWord = 0;
|
||||
initQueue();
|
||||
|
||||
do {
|
||||
moveQueue();
|
||||
charg_car(currWord);
|
||||
trait_car();
|
||||
} while (currWord < wordCount);
|
||||
|
||||
moveQueue();
|
||||
trait_car();
|
||||
entroct((int)'#');
|
||||
|
||||
#ifdef DEBUG
|
||||
warning("---");
|
||||
for (int i = 0; i < _ptr_oct; ) {
|
||||
if ((_troctBuf[i] == 32) || (_troctBuf[i] == 35) || (_troctBuf[i] == 46)) {
|
||||
warning("%d", _troctBuf[i]);
|
||||
i++;
|
||||
} else {
|
||||
warning("%d %d %d", _troctBuf[i], _troctBuf[i + 1], _troctBuf[i + 1]);
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
warning("---");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Start speech
|
||||
* @remarks Originally called 'parole'
|
||||
*/
|
||||
void SoundManager::startSpeech(int rep, int character, int typ) {
|
||||
if (_vm->_soundOff)
|
||||
return;
|
||||
|
||||
_soundType = typ;
|
||||
|
||||
if (typ == 0) {
|
||||
// Speech
|
||||
const int haut[9] = { 0, 0, 1, -3, 6, -2, 2, 7, -1 };
|
||||
const int voiceIndices[9] = { 0, 1, 2, 3, 0, 4, 5, 1, 6 };
|
||||
if (!_ttsMan || !ConfMan.getBool("tts_enabled"))
|
||||
return;
|
||||
Common::Array<int> voices;
|
||||
int pitch = haut[character];
|
||||
bool male;
|
||||
if (haut[character] > 5) {
|
||||
voices = _ttsMan->getVoiceIndicesByGender(Common::TTSVoice::FEMALE);
|
||||
male = false;
|
||||
pitch -= 6;
|
||||
} else {
|
||||
voices = _ttsMan->getVoiceIndicesByGender(Common::TTSVoice::MALE);
|
||||
male = true;
|
||||
}
|
||||
pitch *= 5;
|
||||
// If there is no voice available for the given gender, just set it to the 0th
|
||||
// voice
|
||||
if (voices.empty())
|
||||
_ttsMan->setVoice(0);
|
||||
else {
|
||||
int voiceIndex = voiceIndices[character] % voices.size();
|
||||
_ttsMan->setVoice(voices[voiceIndex]);
|
||||
}
|
||||
// If the selected voice is a different gender, than we want, just try to
|
||||
// set the pitch so it may sound a little bit closer to the gender we want
|
||||
if (!((_ttsMan->getVoice().getGender() == Common::TTSVoice::MALE) == male)) {
|
||||
if (male)
|
||||
pitch -= 50;
|
||||
else
|
||||
pitch += 50;
|
||||
}
|
||||
|
||||
_ttsMan->setPitch(pitch);
|
||||
_ttsMan->say(_vm->getString(rep + kDialogStringIndex), Common::kDos850);
|
||||
return;
|
||||
}
|
||||
uint16 savph[501];
|
||||
int tempo;
|
||||
|
||||
_phonemeNumb = rep;
|
||||
for (int i = 0; i <= 500; ++i)
|
||||
savph[i] = _cfiphBuffer[i];
|
||||
tempo = kTempoNoise;
|
||||
_vm->_addFix = (float)((tempo - 8)) / 256;
|
||||
cctable(_tbi);
|
||||
switch (typ) {
|
||||
case 1:
|
||||
regenbruit();
|
||||
break;
|
||||
case 2:
|
||||
loadPhonemeSounds();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
handlePhoneme();
|
||||
litph(_tbi, typ, tempo);
|
||||
|
||||
_buildingSentence = false;
|
||||
_audioStream->finish();
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, _audioStream);
|
||||
_audioStream = nullptr;
|
||||
|
||||
for (int i = 0; i <= 500; ++i)
|
||||
_cfiphBuffer[i] = savph[i];
|
||||
_vm->setPal(_vm->_numpal);
|
||||
}
|
||||
|
||||
void SoundManager::waitSpeech() {
|
||||
if (_soundType == 0) {
|
||||
if (!_ttsMan)
|
||||
return;
|
||||
while (_ttsMan->isSpeaking() && !_vm->keyPressed() && !_vm->_mouseClick && !_vm->shouldQuit())
|
||||
;
|
||||
// In case the TTS is still speaking, stop it.
|
||||
_ttsMan->stop();
|
||||
} else {
|
||||
while (_mixer->isSoundHandleActive(_soundHandle) && !_vm->keyPressed() && !_vm->_mouseClick && !_vm->shouldQuit())
|
||||
;
|
||||
// In case the handle is still active, stop it.
|
||||
_mixer->stopHandle(_soundHandle);
|
||||
}
|
||||
if (!_vm->keyPressed() && !_vm->_mouseClick && !_vm->shouldQuit())
|
||||
g_system->delayMillis(600);
|
||||
}
|
||||
} // End of namespace Mortevielle
|
||||
108
engines/mortevielle/sound.h
Normal file
108
engines/mortevielle/sound.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/mixer.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/queue.h"
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
namespace Audio {
|
||||
class QueuingAudioStream;
|
||||
}
|
||||
|
||||
namespace Mortevielle {
|
||||
class MortevielleEngine;
|
||||
|
||||
const int kNullValue = 255;
|
||||
const int kTempoMusic = 71;
|
||||
const int kTempoNoise = 78;
|
||||
const int kTempoF = 80;
|
||||
const int kTempoM = 89;
|
||||
|
||||
struct SpeechQueue {
|
||||
int _val;
|
||||
int _code;
|
||||
int _acc;
|
||||
int _freq;
|
||||
int _rep;
|
||||
};
|
||||
|
||||
typedef int tablint[256];
|
||||
|
||||
class SoundManager {
|
||||
private:
|
||||
MortevielleEngine *_vm;
|
||||
|
||||
byte *_ambiantNoiseBuf;
|
||||
byte *_noiseBuf;
|
||||
int _phonemeNumb;
|
||||
int _soundType;
|
||||
SpeechQueue _queue[3];
|
||||
byte _wordBuf[1712];
|
||||
byte _troctBuf[10576];
|
||||
bool _buildingSentence;
|
||||
int _ptr_oct;
|
||||
int _tbi[256];
|
||||
|
||||
Audio::QueuingAudioStream *_audioStream;
|
||||
|
||||
void loadPhonemeSounds();
|
||||
void moveQueue();
|
||||
void initQueue();
|
||||
void handlePhoneme();
|
||||
|
||||
void spfrac(int wor);
|
||||
void charg_car(int &currWordNumb);
|
||||
void entroct(byte o);
|
||||
void cctable(tablint &t);
|
||||
void trait_car();
|
||||
|
||||
void regenbruit();
|
||||
void litph(tablint &t, int typ, int tempo);
|
||||
|
||||
public:
|
||||
SoundManager(MortevielleEngine *vm, Audio::Mixer *mixer);
|
||||
~SoundManager();
|
||||
|
||||
Audio::Mixer *_mixer;
|
||||
Common::TextToSpeechManager *_ttsMan;
|
||||
Audio::SoundHandle _soundHandle;
|
||||
uint16 *_cfiphBuffer;
|
||||
|
||||
int decodeMusic(const byte *PSrc, byte *PDest, int size);
|
||||
void playSong(const byte *buf, uint usize, uint loops);
|
||||
void loadAmbiantSounds();
|
||||
void loadNoise();
|
||||
void startSpeech(int rep, int character, int typ);
|
||||
void waitSpeech();
|
||||
};
|
||||
|
||||
} // End of namespace Mortevielle
|
||||
|
||||
#endif
|
||||
3464
engines/mortevielle/utils.cpp
Normal file
3464
engines/mortevielle/utils.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user