scummvm/engines/private/private.cpp

1058 lines
31 KiB
C++
Raw Normal View History

2021-02-13 14:55:30 -03:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
2020-12-24 16:10:32 -03:00
#include "common/scummsys.h"
2020-12-24 19:02:12 -03:00
#include "audio/decoders/wave.h"
#include "audio/audiostream.h"
2021-01-13 19:27:09 -03:00
#include "common/archive.h"
2020-12-24 16:10:32 -03:00
#include "common/config-manager.h"
#include "common/debug.h"
#include "common/debug-channels.h"
#include "common/error.h"
#include "common/events.h"
#include "common/file.h"
#include "common/fs.h"
#include "common/system.h"
#include "common/str.h"
2021-01-12 20:49:12 -03:00
#include "common/savefile.h"
2021-01-31 20:15:56 -03:00
#include "common/timer.h"
2020-12-24 16:10:32 -03:00
#include "engines/util.h"
#include "image/bmp.h"
#include "graphics/cursorman.h"
2021-01-10 11:18:55 -03:00
#include "private/cursors.h"
2020-12-24 16:10:32 -03:00
#include "private/private.h"
#include "private/tokens.h"
2020-12-30 14:34:32 -03:00
#include "private/grammar.h"
2020-12-24 16:10:32 -03:00
namespace Private {
2021-01-07 23:32:17 -03:00
PrivateEngine *g_private = NULL;
extern int parse(char *);
2020-12-30 17:25:02 -03:00
2021-02-11 22:26:04 -03:00
PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
: Engine(syst), _gameDescription(gd) {
_rnd = new Common::RandomSource("private");
2020-12-24 16:10:32 -03:00
2021-02-15 11:04:45 -03:00
// Debug channels
DebugMan.addDebugChannel(kPrivateDebugFunction, "functions", "Function execution debug channel");
DebugMan.addDebugChannel(kPrivateDebugCode, "code", "Code execution debug channel");
DebugMan.addDebugChannel(kPrivateDebugScript, "script", "Script execution debug channel");
2021-02-15 11:04:45 -03:00
// Global object for external reference
g_private = this;
2021-01-07 23:32:17 -03:00
2021-02-15 11:04:45 -03:00
// Setting execution
_nextSetting = NULL;
_currentSetting = NULL;
2021-02-15 11:04:45 -03:00
_pausedSetting = NULL;
_modified = false;
_mode = -1;
2021-01-17 21:50:42 -03:00
_toTake = false;
2021-02-15 11:04:45 -03:00
// Movies
_nextMovie = NULL;
_nextVS = NULL;
2021-01-17 21:50:42 -03:00
_repeatedMovieExit = new Common::String("");
2021-01-09 16:11:30 -03:00
2021-02-15 11:04:45 -03:00
// Save and load
_saveGameMask = NULL;
_loadGameMask = NULL;
2021-02-07 14:08:28 -03:00
// Interface
2021-02-15 13:49:55 -03:00
_framePath = new Common::String("inface/general/inface2.bmp");
2021-02-07 14:08:28 -03:00
2021-02-15 11:04:45 -03:00
// Police
_policeBustEnabled = false;
_policeBustSetting = NULL;
2021-01-28 23:01:31 -03:00
_numberClicks = 0;
policeVideoIndex = 0;
_sirenSound = Common::String("po/audio/posfx002.wav");
2021-02-11 22:26:04 -03:00
// General sounds
_globalAudioPath = Common::String("global/audio/");
2021-02-07 17:34:19 -03:00
_noStopSounds = false;
2021-01-17 21:50:42 -03:00
2021-02-07 14:08:28 -03:00
// Radios and phone
2021-01-16 15:30:00 -03:00
_policeRadioArea = NULL;
_AMRadioArea = NULL;
_phoneArea = NULL;
2021-01-28 23:01:31 -03:00
// TODO: use this as a default sound for radio
_infaceRadioPath = Common::String("inface/radio/");
_phonePrefix = Common::String("inface/telephon/");
_phoneCallSound = Common::String("phone.wav");
2021-01-07 23:32:17 -03:00
2021-02-07 14:08:28 -03:00
// Dossiers
_dossierPage = 0;
_dossierSuspect = 0;
2021-02-15 11:04:45 -03:00
_dossierNextSuspectMask = NULL;
_dossierPrevSuspectMask = NULL;
2021-02-07 14:08:28 -03:00
// Diary
2021-02-16 11:56:36 -03:00
_diaryLocPrefix = Common::String("inface/diary/loclist/");
2020-12-24 16:10:32 -03:00
}
PrivateEngine::~PrivateEngine() {
// Dispose your resources here
delete _rnd;
2020-12-24 16:10:32 -03:00
2021-02-15 11:04:45 -03:00
// Remove all of our debug levels
DebugMan.clearAllDebugChannels();
2020-12-24 16:10:32 -03:00
}
2021-01-13 19:27:09 -03:00
void PrivateEngine::initializePath(const Common::FSNode &gamePath) {
SearchMan.addDirectory(gamePath.getPath(), gamePath, 0, 10);
}
void PrivateEngine::setOrigin(const int point[2]) {
delete _origin;
_origin = new Common::Point(point[0], point[1]);;
}
2020-12-24 16:10:32 -03:00
Common::Error PrivateEngine::run() {
2021-01-17 21:50:42 -03:00
2021-01-10 20:17:27 -03:00
assert(_installerArchive.open("SUPPORT/ASSETS.Z"));
Common::SeekableReadStream *file = NULL;
2021-01-16 15:30:00 -03:00
// if the full game is used
2021-02-11 22:26:04 -03:00
if (!isDemo()) {
assert(_installerArchive.hasFile("GAME.DAT"));
2021-01-10 20:17:27 -03:00
file = _installerArchive.createReadStreamForMember("GAME.DAT");
2021-02-11 22:26:04 -03:00
} else {
// if the demo from archive.org is used
if (_installerArchive.hasFile("GAME.TXT"))
file = _installerArchive.createReadStreamForMember("GAME.TXT");
2021-01-17 21:50:42 -03:00
2021-02-11 22:26:04 -03:00
// if the demo from the full retail CDROM is used
2021-02-15 10:00:20 -03:00
else {
if (_installerArchive.hasFile("DEMOGAME.DAT"))
file = _installerArchive.createReadStreamForMember("DEMOGAME.DAT");
}
2021-02-11 22:26:04 -03:00
}
2021-01-09 19:11:41 -03:00
2021-02-15 11:04:45 -03:00
// Read assets file
assert(file != NULL);
char *buf = (char *)malloc(file->size()+1);
2021-02-11 22:26:04 -03:00
file->read(buf, file->size()+1);
2021-01-10 11:18:55 -03:00
// Initialize stuff
2021-01-09 22:08:41 -03:00
initInsts();
initFuncs();
2021-01-10 11:18:55 -03:00
initCursors();
parse(buf);
2021-02-11 22:26:04 -03:00
free(buf);
2021-02-13 11:37:03 -03:00
delete file;
assert(constants.size() > 0);
2021-02-15 11:04:45 -03:00
// Initialize graphics
_screenW = 640;
_screenH = 480;
//_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
_pixelFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
2021-01-08 22:42:34 -03:00
_transparentColor = _pixelFormat.RGBToColor(0,255,0);
initGraphics(_screenW, _screenH, &_pixelFormat);
2021-02-13 17:17:24 -03:00
screenRect = new Common::Rect(0, 0, _screenW, _screenH);
2021-01-14 18:48:30 -03:00
changeCursor("default");
2021-01-08 22:42:34 -03:00
_origin = new Common::Point(0, 0);
_image = new Image::BitmapDecoder();
_compositeSurface = new Graphics::ManagedSurface();
_compositeSurface->create(_screenW, _screenH, _pixelFormat);
2021-01-08 22:42:34 -03:00
_compositeSurface->setTransparentColor(_transparentColor);
2021-02-15 13:49:55 -03:00
// Load the game frame once
Common::File frameFile;
assert(frameFile.open(convertPath(*_framePath)));
_image->loadStream(frameFile);
_frame = _image->getSurface()->convertTo(_pixelFormat, _image->getPalette());
// Main event loop
Common::Event event;
Common::Point mousePos;
2021-02-15 11:04:45 -03:00
_videoDecoder = nullptr;
2021-01-13 19:27:09 -03:00
int saveSlot = ConfMan.getInt("save_slot");
2021-01-12 20:49:12 -03:00
if (saveSlot >= 0) { // load the savegame
loadGameState(saveSlot);
} else {
_nextSetting = new Common::String(kGoIntro);
2021-01-12 20:49:12 -03:00
}
while (!shouldQuit()) {
2021-01-16 22:27:13 -03:00
checkPhoneCall();
while (g_system->getEventManager()->pollEvent(event)) {
2021-01-10 11:18:55 -03:00
mousePos = g_system->getEventManager()->getMousePos();
// Events
switch (event.type) {
2021-01-09 19:11:41 -03:00
case Common::EVENT_KEYDOWN:
if (event.kbd.keycode == Common::KEYCODE_ESCAPE && _videoDecoder)
skipVideo();
2021-01-31 20:15:56 -03:00
2021-01-09 19:11:41 -03:00
break;
2021-01-09 19:11:41 -03:00
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
break;
2021-01-09 19:11:41 -03:00
case Common::EVENT_LBUTTONDOWN:
_numberClicks++;
if (selectDossierNextSuspect(mousePos))
2021-02-07 17:36:09 -03:00
break;
else if (selectDossierPrevSuspect(mousePos))
2021-02-07 17:36:09 -03:00
break;
selectPauseMovie(mousePos);
2021-01-16 22:27:13 -03:00
selectPhoneArea(mousePos);
2021-01-16 15:30:00 -03:00
selectPoliceRadioArea(mousePos);
selectAMRadioArea(mousePos);
2021-01-12 20:49:12 -03:00
selectLoadGame(mousePos);
selectSaveGame(mousePos);
2021-01-31 20:15:56 -03:00
if (!_nextSetting)
selectMask(mousePos);
2021-01-09 19:11:41 -03:00
if (!_nextSetting)
selectExit(mousePos);
break;
2021-01-10 11:18:55 -03:00
case Common::EVENT_MOUSEMOVE:
2021-01-14 18:48:30 -03:00
changeCursor("default");
2021-01-31 20:15:56 -03:00
if (cursorPauseMovie(mousePos)) {}
else if (cursorMask(mousePos)) {}
else if (cursorExit(mousePos)) {}
2021-01-10 11:18:55 -03:00
//
break;
2021-01-09 19:11:41 -03:00
default:
2021-02-16 08:19:12 -03:00
break;
}
}
2021-01-28 23:01:31 -03:00
checkPoliceBust();
// Movies
if (_nextMovie != NULL) {
2021-01-31 20:15:56 -03:00
removeTimer();
_videoDecoder = new Video::SmackerDecoder();
playVideo(*_nextMovie);
_nextMovie = NULL;
continue;
}
if (_nextVS != NULL && *_currentSetting == kMainDesktop) {
2021-02-07 14:08:28 -03:00
loadImage(*_nextVS, 160, 120);
2021-01-16 15:30:00 -03:00
}
if (_videoDecoder) {
if (_videoDecoder->getCurFrame() == 0)
stopSound(true);
if (_videoDecoder->endOfVideo()) {
_videoDecoder->close();
delete _videoDecoder;
_videoDecoder = nullptr;
} else if (_videoDecoder->needsUpdate()) {
drawScreen();
}
continue;
}
2021-01-09 19:11:41 -03:00
if (_nextSetting != NULL) {
2021-01-31 20:15:56 -03:00
removeTimer();
debugC(1, kPrivateDebugFunction, "Executing %s", _nextSetting->c_str());
clearAreas();
_currentSetting = _nextSetting;
loadSetting(_nextSetting);
_nextSetting = NULL;
execute(prog);
2021-01-14 18:48:30 -03:00
changeCursor("default");
2021-02-15 13:49:55 -03:00
drawScreen();
}
g_system->updateScreen();
g_system->delayMillis(10);
}
return Common::kNoError;
2020-12-24 16:10:32 -03:00
}
void PrivateEngine::clearAreas() {
_exits.clear();
_masks.clear();
_loadGameMask = NULL;
_saveGameMask = NULL;
_policeRadioArea = NULL;
_AMRadioArea = NULL;
_phoneArea = NULL;
2021-01-31 20:15:56 -03:00
_dossierNextSuspectMask = NULL;
_dossierPrevSuspectMask = NULL;
}
2021-01-28 23:01:31 -03:00
void PrivateEngine::startPoliceBust() {
2021-02-15 12:06:17 -03:00
// This logic was extracted from the binary
2021-02-08 21:46:25 -03:00
int policeIndex = variables.getVal(kPoliceIndex)->u.val;
2021-01-28 23:01:31 -03:00
int r = _rnd->getRandomNumber(0xc);
2021-02-15 12:06:17 -03:00
if (policeIndex > 0x14) {
2021-01-28 23:01:31 -03:00
policeIndex = 0x15;
}
_maxNumberClicks = r + 0x10 + (policeIndex * 0xe) / -0x15;
2021-02-15 12:06:17 -03:00
_sirenWarning = _rnd->getRandomNumber(0x7) + 3;
2021-01-28 23:01:31 -03:00
_numberClicks = 0;
2021-02-15 12:06:17 -03:00
if(_sirenWarning >= _maxNumberClicks)
_sirenWarning = _maxNumberClicks - 1;
2021-01-28 23:01:31 -03:00
}
void PrivateEngine::checkPoliceBust() {
if (!_policeBustEnabled)
return;
if (_numberClicks < _sirenWarning)
return;
2021-02-07 17:36:09 -03:00
2021-01-28 23:01:31 -03:00
if (_numberClicks == _sirenWarning) {
2021-02-07 17:36:09 -03:00
stopSound(true);
playSound(_sirenSound, 0, false, false);
2021-01-28 23:01:31 -03:00
_numberClicks++; // Won't execute again
return;
}
if (_numberClicks == _maxNumberClicks+1) {
2021-02-08 21:46:25 -03:00
uint policeIndex = variables.getVal(kPoliceIndex)->u.val;
_policeBustSetting = _currentSetting;
2021-02-13 11:37:03 -03:00
if (policeIndex <= 13) {
_nextSetting = new Common::String(kPOGoBustMovie);
2021-02-08 21:46:25 -03:00
} else {
_nextSetting = new Common::String(kPoliceBustFromMO);
2021-01-28 23:01:31 -03:00
}
clearAreas();
2021-01-31 20:15:56 -03:00
_policeBustEnabled = false;
2021-01-28 23:01:31 -03:00
}
}
2021-01-10 11:18:55 -03:00
bool PrivateEngine::cursorExit(Common::Point mousePos) {
mousePos = mousePos - *_origin;
if (mousePos.x < 0 || mousePos.y < 0)
return false;
2021-01-17 21:50:42 -03:00
2021-01-14 20:47:06 -03:00
int rs = 100000000;
int cs = 0;
2021-01-10 11:18:55 -03:00
ExitInfo e;
2021-01-14 20:47:06 -03:00
Common::String *cursor = NULL;
2021-01-17 21:50:42 -03:00
2021-01-10 11:18:55 -03:00
for (ExitList::iterator it = _exits.begin(); it != _exits.end(); ++it) {
e = *it;
2021-01-14 20:47:06 -03:00
cs = e.rect->width()*e.rect->height();
2021-01-17 21:50:42 -03:00
if (e.rect->contains(mousePos)) {
2021-01-14 20:47:06 -03:00
if (cs < rs && e.cursor != NULL) {
rs = cs;
cursor = e.cursor;
}
2021-01-10 11:18:55 -03:00
}
}
2021-01-14 20:47:06 -03:00
if (cursor != NULL) {
changeCursor(*cursor);
return true;
}
return false;
2021-01-10 11:18:55 -03:00
}
2021-01-16 15:30:00 -03:00
bool PrivateEngine::inMask(Graphics::ManagedSurface *surf, Common::Point mousePos) {
if (surf == NULL)
return false;
2021-01-10 11:18:55 -03:00
mousePos = mousePos - *_origin;
if (mousePos.x < 0 || mousePos.y < 0)
return false;
2021-01-16 15:30:00 -03:00
if (mousePos.x > surf->w || mousePos.y > surf->h)
return false;
return (*((uint32 *)surf->getBasePtr(mousePos.x, mousePos.y)) != _transparentColor);
2021-01-16 15:30:00 -03:00
}
bool PrivateEngine::cursorMask(Common::Point mousePos) {
2021-01-10 11:18:55 -03:00
MaskInfo m;
bool inside = false;
for (MaskList::iterator it = _masks.begin(); it != _masks.end(); ++it) {
m = *it;
2021-01-16 15:30:00 -03:00
if (inMask(m.surf, mousePos)) {
if (m.cursor != NULL) { // TODO: check this
2021-01-10 11:18:55 -03:00
inside = true;
2021-01-14 18:48:30 -03:00
changeCursor(*m.cursor);
2021-01-10 11:18:55 -03:00
break;
}
}
}
return inside;
}
2021-01-31 20:15:56 -03:00
bool PrivateEngine::cursorPauseMovie(Common::Point mousePos) {
if (_mode == 1) {
Common::Rect window(_origin->x, _origin->y, _screenW - _origin->x, _screenH - _origin->y);
2021-01-31 20:15:56 -03:00
if (!window.contains(mousePos)) {
return true;
}
}
return false;
}
void PrivateEngine::selectPauseMovie(Common::Point mousePos) {
2021-02-07 17:36:09 -03:00
if (_mode == 1) {
2021-01-31 20:15:56 -03:00
Common::Rect window(_origin->x, _origin->y, _screenW - _origin->x, _screenH - _origin->y);
if (!window.contains(mousePos)) {
if ( _pausedSetting == NULL) {
_pausedSetting = _currentSetting;
_nextSetting = new Common::String(kPauseMovie);
}
}
}
}
void PrivateEngine::selectExit(Common::Point mousePos) {
2021-01-09 19:11:41 -03:00
mousePos = mousePos - *_origin;
2021-01-10 11:18:55 -03:00
if (mousePos.x < 0 || mousePos.y < 0)
return;
Common::String *ns = NULL;
2021-01-08 22:42:34 -03:00
int rs = 100000000;
int cs = 0;
2021-01-08 22:42:34 -03:00
ExitInfo e;
for (ExitList::iterator it = _exits.begin(); it != _exits.end(); ++it) {
e = *it;
2021-01-09 19:11:41 -03:00
cs = e.rect->width()*e.rect->height();
2021-01-10 11:18:55 -03:00
//debug("Testing exit %s %d", e.nextSetting->c_str(), cs);
if (e.rect->contains(mousePos)) {
2021-01-14 18:48:30 -03:00
//debug("Inside! %d %d", cs, rs);
2021-01-08 22:42:34 -03:00
if (cs < rs && e.nextSetting != NULL) { // TODO: check this
2021-01-17 21:50:42 -03:00
// an item was not taken
if (_toTake) {
playSound(getLeaveSound(), 1, false, false);
2021-01-17 21:50:42 -03:00
_toTake = false;
}
2021-01-14 18:48:30 -03:00
//debug("Found Exit %s %d", e.nextSetting->c_str(), cs);
rs = cs;
ns = e.nextSetting;
}
}
}
2021-01-08 22:42:34 -03:00
if (ns != NULL) {
2021-01-14 18:48:30 -03:00
//debug("Exit selected %s", ns->c_str());
_nextSetting = ns;
2021-01-08 22:42:34 -03:00
}
}
2021-01-08 22:42:34 -03:00
void PrivateEngine::selectMask(Common::Point mousePos) {
Common::String *ns = NULL;
MaskInfo m;
for (MaskList::iterator it = _masks.begin(); it != _masks.end(); ++it) {
m = *it;
2021-01-10 11:18:55 -03:00
//debug("Testing mask %s", m.nextSetting->c_str());
2021-01-16 15:30:00 -03:00
if (inMask(m.surf, mousePos)) {
2021-01-14 18:48:30 -03:00
//debug("Inside!");
2021-01-08 22:42:34 -03:00
if (m.nextSetting != NULL) { // TODO: check this
2021-01-14 18:48:30 -03:00
//debug("Found Mask %s", m.nextSetting->c_str());
2021-01-08 22:42:34 -03:00
ns = m.nextSetting;
}
if (m.flag1 != NULL) { // TODO: check this
setSymbol(m.flag1, 1);
2021-01-17 21:50:42 -03:00
// an item was taken
if (_toTake) {
playSound(getTakeSound(), 1, false, false);
2021-01-17 21:50:42 -03:00
_toTake = false;
}
2021-01-10 15:38:10 -03:00
}
if (m.flag2 != NULL) {
setSymbol(m.flag2, 1);
}
2021-01-10 15:38:10 -03:00
break;
2021-01-08 22:42:34 -03:00
}
}
if (ns != NULL) {
2021-01-14 18:48:30 -03:00
//debug("Mask selected %s", ns->c_str());
2021-01-08 22:42:34 -03:00
_nextSetting = ns;
}
}
2021-01-16 15:30:00 -03:00
void PrivateEngine::selectAMRadioArea(Common::Point mousePos) {
if (_AMRadioArea == NULL)
2021-01-12 20:49:12 -03:00
return;
2021-01-16 15:30:00 -03:00
if (_AMRadio.empty())
2021-01-12 20:49:12 -03:00
return;
2021-01-17 21:50:42 -03:00
if (inMask(_AMRadioArea->surf, mousePos)) {
Common::String sound = _infaceRadioPath + "comm_/" + _AMRadio.back() + ".wav";
playSound(sound, 1, false, false);
2021-01-16 15:30:00 -03:00
_AMRadio.pop_back();
2021-01-12 20:49:12 -03:00
}
}
2021-01-16 15:30:00 -03:00
void PrivateEngine::selectPoliceRadioArea(Common::Point mousePos) {
if (_policeRadioArea == NULL)
return;
2021-01-12 20:49:12 -03:00
2021-01-16 15:30:00 -03:00
if (_policeRadio.empty())
return;
2021-01-12 20:49:12 -03:00
2021-01-16 15:30:00 -03:00
if (inMask(_policeRadioArea->surf, mousePos)) {
Common::String sound = _infaceRadioPath + "police/" + _policeRadio.back() + ".wav";
playSound(sound, 1, false, false);
2021-01-16 15:30:00 -03:00
_policeRadio.pop_back();
2021-01-12 20:49:12 -03:00
}
}
2021-01-16 22:27:13 -03:00
void PrivateEngine::checkPhoneCall() {
2021-01-17 15:38:45 -03:00
if (_phoneArea == NULL)
return;
2021-01-16 22:27:13 -03:00
if (_phone.empty())
return;
2021-02-07 14:08:28 -03:00
if (!_mixer->isSoundHandleActive(_fgSoundHandle))
playSound(_phonePrefix + _phoneCallSound, 1, false, false);
2021-01-16 22:27:13 -03:00
}
void PrivateEngine::selectPhoneArea(Common::Point mousePos) {
if (_phoneArea == NULL)
return;
if (_phone.empty())
return;
if (inMask(_phoneArea->surf, mousePos)) {
PhoneInfo i = _phone.back();
Common::String sound(*i.sound);
2021-01-17 21:50:42 -03:00
setSymbol(i.flag, i.val);
sound = _phonePrefix + sound + ".wav";
playSound(sound, 1, true, false);
2021-01-16 22:27:13 -03:00
_phone.pop_back();
}
}
void PrivateEngine::loadDossier() {
int x = 40;
int y = 30;
2021-02-16 11:49:01 -03:00
DossierInfo m = _dossiers[_dossierSuspect];
2021-02-16 11:49:01 -03:00
if (_dossierPage == 0) {
loadImage(*m.page1, x, y);
2021-02-16 11:49:01 -03:00
} else if (_dossierPage == 1) {
loadImage(*m.page2, x, y);
} else
assert(0);
}
bool PrivateEngine::selectDossierNextSuspect(Common::Point mousePos) {
if (_dossierNextSuspectMask == NULL)
return false;
if (inMask(_dossierNextSuspectMask->surf, mousePos)) {
if ((_dossierSuspect + 1) < _dossiers.size()) {
_dossierSuspect++;
_dossierPage = 0;
loadDossier();
drawMask(_dossierNextSuspectMask->surf);
drawMask(_dossierPrevSuspectMask->surf);
}
return true;
}
return false;
}
bool PrivateEngine::selectDossierPrevSuspect(Common::Point mousePos) {
if (_dossierPrevSuspectMask == NULL)
return false;
if (inMask(_dossierPrevSuspectMask->surf, mousePos)) {
if (_dossierSuspect > 0) {
_dossierSuspect--;
_dossierPage = 0;
loadDossier();
drawMask(_dossierNextSuspectMask->surf);
drawMask(_dossierPrevSuspectMask->surf);
}
return true;
}
return false;
}
2021-01-16 22:27:13 -03:00
2021-01-16 15:30:00 -03:00
void PrivateEngine::selectLoadGame(Common::Point mousePos) {
if (_loadGameMask == NULL)
2021-01-12 20:49:12 -03:00
return;
2021-01-16 15:30:00 -03:00
if (inMask(_loadGameMask->surf, mousePos)) {
loadGameDialog();
2021-01-12 20:49:12 -03:00
}
}
2021-01-16 15:30:00 -03:00
void PrivateEngine::selectSaveGame(Common::Point mousePos) {
2021-01-12 20:49:12 -03:00
if (_saveGameMask == NULL)
2021-01-16 15:30:00 -03:00
return;
2021-01-17 21:50:42 -03:00
2021-01-16 15:30:00 -03:00
if (inMask(_saveGameMask->surf, mousePos)) {
saveGameDialog();
2021-01-12 20:49:12 -03:00
}
2021-01-16 15:30:00 -03:00
}
2021-01-12 20:49:12 -03:00
2020-12-24 16:10:32 -03:00
bool PrivateEngine::hasFeature(EngineFeature f) const {
2021-02-15 11:04:45 -03:00
return (f == kSupportsReturnToLauncher);
2020-12-24 16:10:32 -03:00
}
2021-01-16 15:30:00 -03:00
void PrivateEngine::restartGame() {
debugC(1, kPrivateDebugFunction, "restartGame");
2021-01-17 21:50:42 -03:00
2021-02-07 14:08:28 -03:00
for (NameList::iterator it = variableList.begin(); it != variableList.end(); ++it) {
2021-01-16 15:30:00 -03:00
Private::Symbol *sym = variables.getVal(*it);
if (*(sym->name) != "kAlternateGame")
2021-01-16 15:30:00 -03:00
sym->u.val = 0;
}
2021-02-13 11:37:03 -03:00
// Diary
for (NameList::iterator it = locationList.begin(); it != locationList.end(); ++it) {
Private::Symbol *sym = locations.getVal(*it);
sym->u.val = 0;
}
inventory.clear();
_dossiers.clear();
// Sounds
_AMRadio.clear();
_policeRadio.clear();
_phone.clear();
_playedPhoneClips.clear();
// Movies
_repeatedMovieExit = NULL;
_playedMovies.clear();
2021-01-16 15:30:00 -03:00
}
2020-12-24 16:10:32 -03:00
Common::Error PrivateEngine::loadGameStream(Common::SeekableReadStream *stream) {
Common::Serializer s(stream, nullptr);
debugC(1, kPrivateDebugFunction, "loadGameStream");
_nextSetting = new Common::String(kStartGame);
2021-01-12 20:49:12 -03:00
int val;
2021-01-17 21:50:42 -03:00
2021-02-07 14:08:28 -03:00
for (NameList::iterator it = variableList.begin(); it != variableList.end(); ++it) {
2021-01-17 21:50:42 -03:00
s.syncAsUint32LE(val);
2021-01-12 20:49:12 -03:00
Private::Symbol *sym = variables.getVal(*it);
sym->u.val = val;
}
2021-02-07 14:08:28 -03:00
// Diary
for (NameList::iterator it = locationList.begin(); it != locationList.end(); ++it) {
s.syncAsUint32LE(val);
Private::Symbol *sym = locations.getVal(*it);
sym->u.val = val;
}
uint32 size = stream->readUint32LE();
2021-02-07 14:08:28 -03:00
for (uint32 i = 0; i < size; ++i) {
inventory.push_back(stream->readString());
2021-02-07 14:08:28 -03:00
}
// Dossiers
size = stream->readUint32LE();
Common::String *file = NULL;
2021-02-07 14:08:28 -03:00
for (uint32 i = 0; i < size; ++i) {
file = new Common::String(stream->readString());
DossierInfo *m = (DossierInfo *)malloc(sizeof(DossierInfo));
2021-02-07 14:08:28 -03:00
m->page1 = file;
2021-02-07 17:36:09 -03:00
file = new Common::String(stream->readString());
2021-02-07 14:08:28 -03:00
if (file->size() == 0) {
2021-02-07 17:36:09 -03:00
m->page2 = NULL;
2021-02-07 14:08:28 -03:00
} else {
m->page2 = file;
}
2021-02-08 21:46:25 -03:00
_dossiers.push_back(*m);
2021-02-07 14:08:28 -03:00
}
// Radios
2021-01-16 22:27:13 -03:00
size = stream->readUint32LE();
_AMRadio.clear();
for (uint32 i = 0; i < size; ++i) {
_AMRadio.push_back(stream->readString());
2021-01-16 22:27:13 -03:00
}
size = stream->readUint32LE();
_policeRadio.clear();
for (uint32 i = 0; i < size; ++i) {
_policeRadio.push_back(stream->readString());
2021-01-16 22:27:13 -03:00
}
size = stream->readUint32LE();
2021-01-17 15:38:45 -03:00
_phone.clear();
2021-01-16 22:27:13 -03:00
for (uint32 j = 0; j < size; ++j) {
PhoneInfo *i = (PhoneInfo *)malloc(sizeof(PhoneInfo));
2021-01-16 22:27:13 -03:00
i->sound = new Common::String(stream->readString());
2021-01-17 21:50:42 -03:00
i->flag = variables.getVal(stream->readString());
2021-01-16 22:27:13 -03:00
i->val = stream->readUint32LE();
_phone.push_back(*i);
}
2021-02-15 11:04:45 -03:00
// Played media
2021-01-17 15:38:45 -03:00
*_repeatedMovieExit = stream->readString();
_playedMovies.clear();
size = stream->readUint32LE();
for (uint32 i = 0; i < size; ++i) {
_playedMovies.setVal(stream->readString(), true);
2021-01-17 21:50:42 -03:00
}
_playedPhoneClips.clear();
size = stream->readUint32LE();
for (uint32 i = 0; i < size; ++i) {
_playedPhoneClips.setVal(stream->readString(), true);
2021-01-17 15:38:45 -03:00
}
return Common::kNoError;
2020-12-24 16:10:32 -03:00
}
Common::Error PrivateEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
debugC(1, kPrivateDebugFunction, "saveGameStream(%d)", isAutosave);
2021-01-12 20:49:12 -03:00
if (isAutosave)
return Common::kNoError;
2021-01-14 18:48:30 -03:00
2021-02-07 14:08:28 -03:00
// Variables
for (NameList::iterator it = variableList.begin(); it != variableList.end(); ++it) {
2021-01-12 20:49:12 -03:00
Private::Symbol *sym = variables.getVal(*it);
stream->writeUint32LE(sym->u.val);
}
2021-01-16 22:27:13 -03:00
2021-02-07 14:08:28 -03:00
// Diary
for (NameList::iterator it = locationList.begin(); it != locationList.end(); ++it) {
Private::Symbol *sym = locations.getVal(*it);
stream->writeUint32LE(sym->u.val);
}
stream->writeUint32LE(inventory.size());
for (NameList::iterator it = inventory.begin(); it != inventory.end(); ++it) {
stream->writeString(*it);
stream->writeByte(0);
}
// Dossiers
stream->writeUint32LE(_dossiers.size());
for (DossierArray::iterator it = _dossiers.begin(); it != _dossiers.end(); ++it) {
stream->writeString(it->page1->c_str());
stream->writeByte(0);
2021-02-07 17:36:09 -03:00
2021-02-07 14:08:28 -03:00
if (it->page2 != NULL)
stream->writeString(it->page2->c_str());
stream->writeByte(0);
}
// Radios
2021-01-16 22:27:13 -03:00
stream->writeUint32LE(_AMRadio.size());
for (SoundList::iterator it = _AMRadio.begin(); it != _AMRadio.end(); ++it) {
stream->writeString(*it);
stream->writeByte(0);
}
stream->writeUint32LE(_policeRadio.size());
for (SoundList::iterator it = _policeRadio.begin(); it != _policeRadio.end(); ++it) {
stream->writeString(*it);
stream->writeByte(0);
}
stream->writeUint32LE(_phone.size());
for (PhoneList::iterator it = _phone.begin(); it != _phone.end(); ++it) {
stream->writeString(*it->sound);
stream->writeByte(0);
stream->writeString(*it->flag->name);
stream->writeByte(0);
stream->writeUint32LE(it->val);
}
2021-02-07 14:08:28 -03:00
// Played media
2021-01-17 15:38:45 -03:00
stream->writeString(*_repeatedMovieExit);
stream->writeByte(0);
stream->writeUint32LE(_playedMovies.size());
2021-01-17 21:50:42 -03:00
for (PlayedMediaTable::iterator it = _playedMovies.begin(); it != _playedMovies.end(); ++it) {
stream->writeString(it->_key);
stream->writeByte(0);
}
stream->writeUint32LE(_playedPhoneClips.size());
for (PlayedMediaTable::iterator it = _playedPhoneClips.begin(); it != _playedPhoneClips.end(); ++it) {
2021-01-17 15:38:45 -03:00
stream->writeString(it->_key);
stream->writeByte(0);
}
return Common::kNoError;
2020-12-24 16:10:32 -03:00
}
2021-01-07 23:32:17 -03:00
Common::String PrivateEngine::convertPath(Common::String name) {
Common::String path(name);
Common::String s1("\\");
Common::String s2("/");
2021-01-07 23:32:17 -03:00
while (path.contains(s1))
Common::replace(path, s1, s2);
2021-01-07 23:32:17 -03:00
s1 = Common::String("\"");
s2 = Common::String("");
2021-01-07 23:32:17 -03:00
Common::replace(path, s1, s2);
Common::replace(path, s1, s2);
path.toLowercase();
return path;
2021-01-07 23:32:17 -03:00
}
2021-02-07 14:08:28 -03:00
void PrivateEngine::playSound(const Common::String &name, uint loops, bool stopOthers, bool background) {
debugC(1, kPrivateDebugFunction, "%s(%s,%d,%d,%d)", __FUNCTION__, name.c_str(), loops, stopOthers, background);
2021-01-02 10:22:07 -03:00
Common::File *file = new Common::File();
Common::String path = convertPath(name);
if (!file->open(path))
error("unable to find sound file %s", path.c_str());
2020-12-24 19:02:12 -03:00
2021-01-09 16:11:30 -03:00
Audio::LoopingAudioStream *stream;
2021-01-14 20:47:06 -03:00
stream = new Audio::LoopingAudioStream(Audio::makeWAVStream(file, DisposeAfterUse::YES), loops);
if (stopOthers) {
2021-02-07 14:08:28 -03:00
stopSound(true);
}
2021-02-07 17:36:09 -03:00
2021-02-07 14:08:28 -03:00
Audio::SoundHandle *sh = NULL;
if (background) {
_mixer->stopHandle(_bgSoundHandle);
sh = &_bgSoundHandle;
} else {
_mixer->stopHandle(_fgSoundHandle);
sh = &_fgSoundHandle;
}
2021-02-07 14:08:28 -03:00
_mixer->playStream(Audio::Mixer::kSFXSoundType, sh, stream, -1, Audio::Mixer::kMaxChannelVolume);
2020-12-24 19:02:12 -03:00
}
void PrivateEngine::playVideo(const Common::String &name) {
debugC(1, kPrivateDebugFunction, "%s(%s)", __FUNCTION__, name.c_str());
//stopSound(true);
Common::File *file = new Common::File();
Common::String path = convertPath(name);
2021-01-04 08:14:40 -03:00
if (!file->open(path))
error("unable to find video file %s", path.c_str());
2020-12-24 19:02:12 -03:00
if (!_videoDecoder->loadStream(file))
error("unable to load video %s", path.c_str());
_videoDecoder->start();
2020-12-24 19:02:12 -03:00
}
2021-01-09 16:11:30 -03:00
void PrivateEngine::skipVideo() {
_videoDecoder->close();
delete _videoDecoder;
_videoDecoder = nullptr;
}
2021-02-07 14:08:28 -03:00
void PrivateEngine::stopSound(bool all) {
debugC(1, kPrivateDebugFunction, "%s(%d)", __FUNCTION__, all);
2021-02-07 14:08:28 -03:00
if (all) {
_mixer->stopHandle(_fgSoundHandle);
_mixer->stopHandle(_bgSoundHandle);
2021-02-13 19:11:11 -03:00
} else {
2021-02-07 14:08:28 -03:00
_mixer->stopHandle(_fgSoundHandle);
}
2020-12-24 19:02:12 -03:00
}
2021-02-07 14:08:28 -03:00
void PrivateEngine::loadImage(const Common::String &name, int x, int y) {
debugC(1, kPrivateDebugFunction, "%s(%s,%d,%d)", __FUNCTION__, name.c_str(), x, y);
Common::File file;
Common::String path = convertPath(name);
if (!file.open(path))
error("unable to load image %s", path.c_str());
_image->loadStream(file);
Graphics::Surface *surf = _image->getSurface()->convertTo(_pixelFormat, _image->getPalette());
_compositeSurface->transBlitFrom(*surf, *_origin + Common::Point(x,y), _transparentColor);
surf->free();
delete surf;
_image->destroy();
2021-02-15 13:49:55 -03:00
//drawScreen();
}
void PrivateEngine::drawScreenFrame(Graphics::Surface *surf) {
2021-02-15 13:49:55 -03:00
surf->copyRectToSurface(*_frame, 0, 0, Common::Rect(0, 0, _screenW, _screenH));
2021-01-09 16:11:30 -03:00
}
2021-01-08 22:42:34 -03:00
Graphics::ManagedSurface *PrivateEngine::loadMask(const Common::String &name, int x, int y, bool drawn) {
debugC(1, kPrivateDebugFunction, "%s(%s,%d,%d,%d)", __FUNCTION__, name.c_str(), x, y, drawn);
2021-01-08 22:42:34 -03:00
Common::File file;
Common::String path = convertPath(name);
if (!file.open(path))
error("unable to load mask %s", path.c_str());
_image->loadStream(file);
Graphics::ManagedSurface *surf = new Graphics::ManagedSurface();
surf->create(_screenW, _screenH, _pixelFormat);
2021-02-13 17:17:24 -03:00
surf->fillRect(*screenRect, _transparentColor);
Graphics::Surface *csurf = _image->getSurface()->convertTo(_pixelFormat, _image->getPalette());
surf->transBlitFrom(*csurf, Common::Point(x,y));
csurf->free();
delete csurf;
_image->destroy();
2021-01-08 22:42:34 -03:00
if (drawn) {
drawMask(surf);
2021-01-08 22:42:34 -03:00
}
2021-01-09 19:11:41 -03:00
return surf;
2021-01-08 22:42:34 -03:00
}
void PrivateEngine::drawMask(Graphics::ManagedSurface *surf) {
_compositeSurface->transBlitFrom(surf->rawSurface(), *_origin, _transparentColor);
2021-02-15 13:49:55 -03:00
//drawScreen();
}
2021-01-10 20:17:27 -03:00
void PrivateEngine::drawScreen() {
2021-01-10 11:18:55 -03:00
Graphics::ManagedSurface *surface = _compositeSurface;
if (_videoDecoder) {
const Graphics::Surface *frame = _videoDecoder->decodeNextFrame();
//frame->create(_videoDecoder->getWidth(), _videoDecoder->getHeight(), _pixelFormat);
//frame->copyFrom(*_videoDecoder->decodeNextFrame());
Graphics::Surface *cframe = frame->convertTo(_pixelFormat, _videoDecoder->getPalette());
2021-01-31 20:15:56 -03:00
Common::Point center((_screenW - _videoDecoder->getWidth())/2, (_screenH - _videoDecoder->getHeight())/2);
surface->transBlitFrom(*cframe, center);
//frame->free();
2021-01-21 20:21:08 -03:00
cframe->free();
//delete frame;
delete cframe;
2021-01-10 11:18:55 -03:00
}
2021-01-08 22:42:34 -03:00
Graphics::Surface *screen = g_system->lockScreen();
2021-01-14 18:48:30 -03:00
if (_mode == 1) {
drawScreenFrame(screen);
}
2021-01-23 09:30:16 -03:00
Common::Rect window(_origin->x, _origin->y, _screenW - _origin->x, _screenH - _origin->y);
screen->copyRectToSurface(*surface, _origin->x, _origin->y, window);
2021-01-10 11:18:55 -03:00
g_system->unlockScreen();
//if (_image->getPalette() != nullptr)
// g_system->getPaletteManager()->setPalette(_image->getPalette(), _image->getPaletteStartIndex(), _image->getPaletteColorCount());
g_system->updateScreen();
2020-12-24 19:02:12 -03:00
}
2021-01-10 15:38:10 -03:00
bool PrivateEngine::getRandomBool(uint p) {
uint r = _rnd->getRandomNumber(100);
2021-01-10 20:17:27 -03:00
return (r <= p);
2021-01-10 15:38:10 -03:00
}
2020-12-24 19:02:12 -03:00
Common::String PrivateEngine::getPaperShuffleSound() {
2021-02-13 11:37:03 -03:00
uint r = _rnd->getRandomNumber(6);
return Common::String::format("%sglsfx0%d.wav", _globalAudioPath.c_str(), kPaperShuffleSound[r]);
2021-01-17 15:38:45 -03:00
}
2020-12-24 19:02:12 -03:00
Common::String PrivateEngine::getTakeSound() {
2021-02-11 22:26:04 -03:00
if (isDemo())
return (Common::String(_globalAudioPath + "mvo007.wav"));
2021-02-11 22:26:04 -03:00
2021-02-15 12:06:17 -03:00
uint r = _rnd->getRandomNumber(4) + 1;
return Common::String::format("%stook%d.wav", _globalAudioPath.c_str(), r);
2021-01-17 21:50:42 -03:00
}
Common::String PrivateEngine::getTakeLeaveSound() {
2021-01-28 23:01:31 -03:00
uint r = _rnd->getRandomNumber(1);
if (r == 0) {
return (Common::String(_globalAudioPath + "mvo001.wav"));
2021-01-28 23:01:31 -03:00
} else {
return (Common::String(_globalAudioPath + "mvo006.wav"));
2021-01-28 23:01:31 -03:00
}
}
Common::String PrivateEngine::getLeaveSound() {
2021-02-11 22:26:04 -03:00
if (isDemo())
return (Common::String(_globalAudioPath + "mvo008.wav"));
2021-02-11 22:26:04 -03:00
2021-02-15 12:06:17 -03:00
uint r = _rnd->getRandomNumber(4) + 1;
return Common::String::format("%sleft%d.wav", _globalAudioPath.c_str(), r);
2021-01-17 21:50:42 -03:00
}
char *PrivateEngine::getRandomPhoneClip(const char *clip, int i, int j) {
2021-01-23 09:30:16 -03:00
uint r = i + _rnd->getRandomNumber(j - i);
char *f = (char *)malloc((strlen(clip) + 3) * sizeof(char));
2021-02-13 13:29:46 -03:00
snprintf(f, 32, "%s%02d", clip, r);
2021-01-23 09:30:16 -03:00
return f;
}
2021-01-31 20:15:56 -03:00
// Timers
void timerCallback(void *refCon) {
g_private->removeTimer();
g_private->_nextSetting = (Common::String *)refCon;
2021-01-31 20:15:56 -03:00
}
bool PrivateEngine::installTimer(uint32 delay, Common::String *ns) {
return g_system->getTimerManager()->installTimerProc(&timerCallback, delay, (void *)ns, "timerCallback");
2021-01-31 20:15:56 -03:00
}
void PrivateEngine::removeTimer() {
g_system->getTimerManager()->removeTimerProc(&timerCallback);
}
2021-01-23 09:30:16 -03:00
2021-02-07 14:08:28 -03:00
// Diary
void PrivateEngine::loadLocations(Common::Rect *rect) {
uint32 i = 0;
int16 offset = 44;
for (NameList::iterator it = locationList.begin(); it != locationList.end(); ++it) {
Private::Symbol *sym = locations.getVal(*it);
i++;
if (sym->u.val) {
offset = offset + 22;
2021-02-16 11:56:36 -03:00
Common::String s =
Common::String::format("%sdryloc%d.bmp", _diaryLocPrefix.c_str(), i);
2021-02-07 14:08:28 -03:00
loadMask(s, rect->left + 120, rect->top + offset, true);
}
}
}
void PrivateEngine::loadInventory(uint32 x, Common::Rect *r1, Common::Rect *r2) {
2021-02-07 17:34:19 -03:00
int16 offset = 0;
2021-02-07 14:08:28 -03:00
for (NameList::iterator it = inventory.begin(); it != inventory.end(); ++it) {
offset = offset + 22;
2021-02-07 17:34:19 -03:00
//debug("%hd %hd", rect->left, rect->top + offset);
loadMask(*it, r1->left, r1->top + offset, true);
2021-02-07 14:08:28 -03:00
}
}
2020-12-24 16:10:32 -03:00
} // End of namespace Private