diff options
author | Peter Kohaut | 2018-11-21 23:16:15 +0100 |
---|---|---|
committer | Peter Kohaut | 2018-11-24 08:39:03 +0100 |
commit | 824ecc0aad325c54f34c8fb7f64cf4df71c53090 (patch) | |
tree | 109abb14609bacaac2c0b1b2ed42e67a8cd2c5ec /engines | |
parent | 44b68a0aeb92d6dc6b6d1b3260ec5f82c529b9f0 (diff) | |
download | scummvm-rg350-824ecc0aad325c54f34c8fb7f64cf4df71c53090.tar.gz scummvm-rg350-824ecc0aad325c54f34c8fb7f64cf4df71c53090.tar.bz2 scummvm-rg350-824ecc0aad325c54f34c8fb7f64cf4df71c53090.zip |
BLADERUNNER: Preliminary saving & loading support
Saving and loading is accessible via ScummVM dialogs.
No in-game UI support yet.
It is possible to load saves from original game via debugger console.
ScummVM saves have additional header and are incompatibile with original
game.
Diffstat (limited to 'engines')
49 files changed, 693 insertions, 188 deletions
diff --git a/engines/bladerunner/actor.cpp b/engines/bladerunner/actor.cpp index 4899ea9527..26a7a80d6e 100644 --- a/engines/bladerunner/actor.cpp +++ b/engines/bladerunner/actor.cpp @@ -51,7 +51,8 @@ Actor::Actor(BladeRunnerEngine *vm, int actorId) { _walkInfo = new ActorWalk(vm); _movementTrack = new MovementTrack(); - _clues = new ActorClues(vm, (actorId && actorId != 99) ? 2 : 4); + _cluesLimit = (actorId == 0 || actorId == 99) ? 4 : 2; + _clues = new ActorClues(vm, _cluesLimit); _combatInfo = new ActorCombat(vm); _friendlinessToOther.resize(_vm->_gameInfo->getActorCount()); @@ -1105,6 +1106,10 @@ void Actor::addClueToDatabase(int clueId, int weight, bool clueAcquired, bool un _clues->add(_id, clueId, weight, clueAcquired, unknownFlag, fromActorId); } +bool Actor::canAcquireClue(int clueId) const { + return _clues->exists(clueId); +} + void Actor::acquireClue(int clueId, bool unknownFlag, int fromActorId) { bool hasAlready = hasClue(clueId); _clues->acquire(clueId, unknownFlag, fromActorId); @@ -1124,7 +1129,7 @@ bool Actor::hasClue(int clueId) const { void Actor::copyClues(int actorId) { Actor *otherActor = _vm->_actors[actorId]; for (int i = 0; i < (int)_vm->_gameInfo->getClueCount(); i++) { - if (hasClue(i) && !_clues->isPrivate(i) && !otherActor->hasClue(i)) { + if (hasClue(i) && !_clues->isPrivate(i) && otherActor->canAcquireClue(i) && !otherActor->hasClue(i)) { int fromActorId = _id; if (_id == BladeRunnerEngine::kActorVoiceOver) { fromActorId = _clues->getFromActorId(i); @@ -1258,8 +1263,9 @@ void Actor::save(SaveFileWriteStream &f) { f.writeInt(_honesty); f.writeInt(_intelligence); - f.writeInt(_stability); f.writeInt(_combatAggressiveness); + f.writeInt(_stability); + f.writeInt(_goalNumber); f.writeInt(_currentHP); @@ -1271,7 +1277,8 @@ void Actor::save(SaveFileWriteStream &f) { f.writeInt(_movementTrackNextAngle); f.writeBool(_movementTrackNextRunning); - f.writeInt(0); // TODO: _clueType + f.writeInt(_cluesLimit); + f.writeBool(_isMoving); f.writeBool(_isTarget); f.writeBool(_inCombat); @@ -1302,7 +1309,7 @@ void Actor::save(SaveFileWriteStream &f) { uint32 now = _vm->getTotalPlayTime(); // TODO: should be last lock time for (int i = 0; i < 7; ++i) { - f.writeInt(_timersLast[i] - now); + f.writeInt(now - _timersLast[i]); } int actorCount = _vm->_gameInfo->getActorCount(); @@ -1316,9 +1323,10 @@ void Actor::save(SaveFileWriteStream &f) { _walkInfo->save(f); - f.writeBoundingBox(_bbox); + f.writeBoundingBox(_bbox, false); _combatInfo->save(f); + f.writeInt(_animationModeCombatIdle); f.writeInt(_animationModeCombatWalk); f.writeInt(_animationModeCombatRun); @@ -1334,8 +1342,9 @@ void Actor::load(SaveFileReadStream &f) { _honesty = f.readInt(); _intelligence = f.readInt(); - _stability = f.readInt(); _combatAggressiveness = f.readInt(); + _stability = f.readInt(); + _goalNumber = f.readInt(); _currentHP = f.readInt(); @@ -1347,7 +1356,8 @@ void Actor::load(SaveFileReadStream &f) { _movementTrackNextAngle = f.readInt(); _movementTrackNextRunning = f.readBool(); - f.skip(4); // TODO: _clueType + _cluesLimit = f.readInt(); + _isMoving = f.readBool(); _isTarget = f.readBool(); _inCombat = f.readBool(); @@ -1378,7 +1388,7 @@ void Actor::load(SaveFileReadStream &f) { uint32 now = _vm->getTotalPlayTime(); // TODO: should be last lock time for (int i = 0; i < 7; ++i) { - _timersLast[i] = f.readInt() + now; + _timersLast[i] = now - f.readInt(); } int actorCount = _vm->_gameInfo->getActorCount(); @@ -1392,7 +1402,7 @@ void Actor::load(SaveFileReadStream &f) { _walkInfo->load(f); - _bbox = f.readBoundingBox(); + _bbox = f.readBoundingBox(false); _combatInfo->load(f); diff --git a/engines/bladerunner/actor.h b/engines/bladerunner/actor.h index 6540c0593a..896f3a2e52 100644 --- a/engines/bladerunner/actor.h +++ b/engines/bladerunner/actor.h @@ -70,6 +70,7 @@ private: int _targetFacing; int _walkboxId; + int _cluesLimit; int _timer4RemainDefault; // Flags @@ -242,9 +243,10 @@ public: bool isSpeeching(); void addClueToDatabase(int clueId, int unknown, bool clueAcquired, bool unknownFlag, int fromActorId); + bool canAcquireClue(int clueId) const; void acquireClue(int clueId, bool unknownFlag, int fromActorId); void loseClue(int clueId); - bool hasClue(int clueId) const; + bool hasClue(int clueId) const; void copyClues(int actorId); void acquireCluesByRelations(); diff --git a/engines/bladerunner/actor_clues.cpp b/engines/bladerunner/actor_clues.cpp index 70d3ad50c9..07df81e823 100644 --- a/engines/bladerunner/actor_clues.cpp +++ b/engines/bladerunner/actor_clues.cpp @@ -33,11 +33,11 @@ namespace BladeRunner { -ActorClues::ActorClues(BladeRunnerEngine *vm, int cluesType) { +ActorClues::ActorClues(BladeRunnerEngine *vm, int cluesLimit) { _vm = vm; _count = 0; _maxCount = 0; - switch (cluesType) { + switch (cluesLimit) { case 4: _maxCount = _vm->_gameInfo->getClueCount(); break; @@ -83,11 +83,11 @@ bool ActorClues::isAcquired(int clueId) const { if (clueIndex == -1) { return false; } -#if BLADERUNNER_DEBUG_GAME - return true; -#else +// #if BLADERUNNER_DEBUG_GAME +// return true; +// #else return _clues[clueIndex].flags & 0x01; -#endif +// #endif } int ActorClues::getWeight(int clueId) const { @@ -346,6 +346,10 @@ void ActorClues::add(int actorId, int clueId, int weight, bool acquired, bool un ++_count; } +bool ActorClues::exists(int clueId) const { + return findClueIndex(clueId) != -1; +} + void ActorClues::remove(int index) { if (_vm->_crimesDatabase) { debug("Actor removed clue: \"%s\"", _vm->_crimesDatabase->getClueText(_clues[index].clueId)); @@ -367,7 +371,7 @@ void ActorClues::remove(int index) { void ActorClues::save(SaveFileWriteStream &f) { f.writeInt(_count); f.writeInt(_maxCount); - for (int i = 0; i < _count; ++i) { + for (int i = 0; i < _maxCount; ++i) { Clue &c = _clues[i]; f.writeInt(c.clueId); f.writeInt(c.weight); @@ -387,7 +391,7 @@ void ActorClues::load(SaveFileReadStream &f) { _maxCount = f.readInt(); _clues.clear(); _clues.resize(_maxCount); - for (int i = 0; i < _count; ++i) { + for (int i = 0; i < _maxCount; ++i) { Clue &c = _clues[i]; c.clueId = f.readInt(); c.weight = f.readInt(); @@ -402,8 +406,4 @@ void ActorClues::load(SaveFileReadStream &f) { } } -bool ActorClues::exists(int clueId) const { - return findClueIndex(clueId) != -1; -} - } // End of namespace BladeRunner diff --git a/engines/bladerunner/actor_clues.h b/engines/bladerunner/actor_clues.h index 76690329d3..88af222724 100644 --- a/engines/bladerunner/actor_clues.h +++ b/engines/bladerunner/actor_clues.h @@ -60,9 +60,10 @@ public: }; public: - ActorClues(BladeRunnerEngine *_vm, int cluesType); + ActorClues(BladeRunnerEngine *_vm, int cluesLimit); void add(int actorId, int clueId, int unknown, bool acquired, bool unknownFlag, int fromActorId); + bool exists(int clueId) const; void acquire(int clueId, bool flag2, int fromActorId); void lose(int clueId); @@ -95,7 +96,6 @@ public: void load(SaveFileReadStream &f); private: - bool exists(int clueId) const; int findClueIndex(int clueId) const; void remove(int clueIndex); }; diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp index 362e0c4b6c..1c08b793b8 100644 --- a/engines/bladerunner/bladerunner.cpp +++ b/engines/bladerunner/bladerunner.cpp @@ -203,7 +203,80 @@ BladeRunnerEngine::~BladeRunnerEngine() { } bool BladeRunnerEngine::hasFeature(EngineFeature f) const { - return f == kSupportsRTL; + return + f == kSupportsRTL || + f == kSupportsLoadingDuringRuntime || + f == kSupportsSavingDuringRuntime; +} + +bool BladeRunnerEngine::canLoadGameStateCurrently() { + return + playerHasControl() && + !_sceneScript->isInsideScript() && + !_aiScripts->isInsideScript() && + !_kia->isOpen() && + !_spinner->isOpen() && + !_vk->isOpen() && + !_elevator->isOpen(); +} + +Common::Error BladeRunnerEngine::loadGameState(int slot) { + const Common::String saveName = Common::String::format("%s.%03d", _targetName.c_str(), slot); + + Common::InSaveFile *saveFile = getSaveFileManager()->openForLoading(saveName); + if (saveFile == nullptr || saveFile->err()) { + delete saveFile; + return Common::kReadingFailed; + } + + BladeRunner::SaveFileHeader header; + if (!BladeRunner::SaveFile::readHeader(*saveFile, header)) { + error("Invalid savegame"); + } + + loadGame(*saveFile); + + delete saveFile; + + return Common::kNoError; +} + +bool BladeRunnerEngine::canSaveGameStateCurrently() { + return + playerHasControl() && + !_sceneScript->isInsideScript() && + !_aiScripts->isInsideScript() && + !_kia->isOpen() && + !_spinner->isOpen() && + !_vk->isOpen() && + !_elevator->isOpen(); +} + +Common::Error BladeRunnerEngine::saveGameState(int slot, const Common::String &desc) { + const Common::String saveName = Common::String::format("%s.%03d", _targetName.c_str(), slot); + + Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(saveName); + if (saveFile == nullptr || saveFile->err()) { + delete saveFile; + return Common::kReadingFailed; + } + + byte *thumbnail = new byte[SaveFile::kThumbnailSize]; + generateThumbnail(thumbnail); + + BladeRunner::SaveFileHeader header; + header._name = desc; + BladeRunner::SaveFile::writeHeader(*saveFile, header); + + saveGame(*saveFile, thumbnail); + + saveFile->finalize(); + + delete[] thumbnail; + + delete saveFile; + + return Common::kNoError; } Common::Error BladeRunnerEngine::run() { @@ -315,8 +388,8 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { // Seed rand // TODO: Sine and cosine lookup tables for intervals of 1.0, 4.0, and 12.0 - _cosTable1024 = new Common::CosineTable(1024); // 10-bits = 1024 points for 2*PI; - _sinTable1024 = new Common::SineTable(1024); + _cosTable1024 = new Common::CosineTable(1024); // 10-bits = 1024 points for 2*PI; + _sinTable1024 = new Common::SineTable(1024); _view = new View(); @@ -1640,22 +1713,16 @@ void BladeRunnerEngine::playerGainsControl() { } } -bool BladeRunnerEngine::saveGame(const Common::String &filename, byte *thumbnail) { - warning("BladeRunnerEngine::saveGame not finished"); - +bool BladeRunnerEngine::saveGame(Common::WriteStream &stream, const void *thumbnail) { if (!playerHasControl() || _sceneScript->isInsideScript() || _aiScripts->isInsideScript()) { return false; } - Common::OutSaveFile *commonSaveFile = getSaveFileManager()->openForSaving(filename, false); - if (commonSaveFile->err()) { - return false; - } - - SaveFileWriteStream s; + Common::MemoryWriteStreamDynamic memoryStream(DisposeAfterUse::Flag::YES); + SaveFileWriteStream s(memoryStream); - s.padBytes(9600); // TODO: thumbnail - s.writeFloat(-1.0f); + s.write(thumbnail, SaveFile::kThumbnailSize); + s.writeFloat(1.0f); _settings->save(s); _scene->save(s); _scene->_exits->save(s); @@ -1691,49 +1758,43 @@ bool BladeRunnerEngine::saveGame(const Common::String &filename, byte *thumbnail s.writeInt(nextAnimation); } _actors[kActorVoiceOver]->save(s); - _policeMaze->save(s); _crimesDatabase->save(s); s.finalize(); - assert(0 && "ok"); - commonSaveFile->writeUint32LE(s.size() + 4); - commonSaveFile->write(s.getData(), s.size()); + stream.writeUint32LE(memoryStream.size() + 4); + stream.write(memoryStream.getData(), memoryStream.size()); + stream.flush(); - return !commonSaveFile->err(); + return true; } -void BladeRunnerEngine::loadGame(const Common::String &filename, byte *thumbnail) { - warning("BladeRunnerEngine::loadGame not finished"); - +bool BladeRunnerEngine::loadGame(Common::SeekableReadStream &stream) { if (!playerHasControl() || _sceneScript->isInsideScript() || _aiScripts->isInsideScript()) { - return; - } - - Common::InSaveFile *commonSaveFile = getSaveFileManager()->openForLoading(filename); - if (commonSaveFile->err()) { - return; + return false; } - void *buf = malloc(commonSaveFile->size()); - int dataSize = commonSaveFile->read(buf, commonSaveFile->size()); - - SaveFileReadStream s((const byte*)buf, dataSize); + SaveFileReadStream s(stream); _ambientSounds->removeAllNonLoopingSounds(true); _ambientSounds->removeAllLoopingSounds(1); _music->stop(2); _audioSpeech->stopSpeech(); _actorDialogueQueue->flush(true, false); + _screenEffects->_entries.clear(); int size = s.readInt(); - if (size != dataSize) { - return; + if (size != s.size() - s.pos() + 4) { + _gameIsLoading = false; + return false; } - s.skip(9600); // thumbnail + _gameIsLoading = true; + _settings->setLoadingGame(); + s.skip(SaveFile::kThumbnailSize); // skip the thumbnail + s.skip(4);// always float 1.0, but never used _settings->load(s); _scene->load(s); _scene->_exits->load(s); @@ -1757,7 +1818,6 @@ void BladeRunnerEngine::loadGame(const Common::String &filename, byte *thumbnail _obstacles->load(s); _actorDialogueQueue->load(s); _waypoints->load(s); - for (uint i = 0; i != _gameInfo->getActorCount(); ++i) { _actors[i]->load(s); @@ -1768,23 +1828,71 @@ void BladeRunnerEngine::loadGame(const Common::String &filename, byte *thumbnail _aiScripts->setAnimationState(i, animationState, animationFrame, animationStateNext, nextAnimation); } _actors[kActorVoiceOver]->load(s); - _policeMaze->load(s); _crimesDatabase->load(s); + _gameIsLoading = false; + _settings->setNewSetAndScene(_settings->getSet(), _settings->getScene()); _settings->setChapter(_settings->getChapter()); + + return true; +} + +void BladeRunnerEngine::newGame(int difficulty) { + _settings->reset(); + _combat->reset(); + + for (uint i = 0; i < _gameInfo->getActorCount(); ++i) { + _actors[i]->setup(i); + } + _actors[kActorVoiceOver]->setup(99); + + for (uint i = 0; i < _gameInfo->getSuspectCount(); ++i) { + _suspectsDatabase->get(i)->reset(); + } + + _gameFlags->clear(); + + _gameInfo->getGlobalVarCount(); + + for (uint i = 0; i < _gameInfo->getGlobalVarCount(); ++i) { + _gameVars[i] = 0; + } + + _items->reset(); + _scores->reset(); + _kia->reset(); + _dialogueMenu->clear(); + _scene->_exits->enable(); + + if (difficulty >= 0 && difficulty < 3) { + _settings->setDifficulty(difficulty); + } + + _settings->setStartingGame(); } void BladeRunnerEngine::ISez(const Common::String &str) { debug("\t%s", str.c_str()); } -void BladeRunnerEngine::blitToScreen(const Graphics::Surface &src) { +void BladeRunnerEngine::blitToScreen(const Graphics::Surface &src) const { _system->copyRectToScreen(src.getPixels(), src.pitch, 0, 0, src.w, src.h); _system->updateScreen(); } +void BladeRunnerEngine::generateThumbnail(void *thumbnail) const { + uint16 *dstPixels = (uint16*)thumbnail; + + for (int y = 0; y < 480; y += 8) { + for (int x = 0; x < 640; x += 8) { + *dstPixels = *(const uint16 *)_surfaceFront.getBasePtr(x, y); + ++dstPixels; + } + } +} + GUI::Debugger *BladeRunnerEngine::getDebugger() { return _debugger; } diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h index 8d45d6a806..817c3066e0 100644 --- a/engines/bladerunner/bladerunner.h +++ b/engines/bladerunner/bladerunner.h @@ -24,6 +24,7 @@ #define BLADERUNNER_BLADERUNNER_H #include "bladerunner/archive.h" +#include "bladerunner/savefile.h" #include "common/array.h" #include "common/cosinetables.h" @@ -37,7 +38,7 @@ //TODO: remove these when game is playable #define BLADERUNNER_DEBUG_CONSOLE 0 -#define BLADERUNNER_DEBUG_GAME 0 +#define BLADERUNNER_DEBUG_GAME 1 namespace Common { struct Event; @@ -224,7 +225,11 @@ public: BladeRunnerEngine(OSystem *syst, const ADGameDescription *desc); ~BladeRunnerEngine(); - bool hasFeature(EngineFeature f) const; + bool hasFeature(EngineFeature f) const override; + bool canLoadGameStateCurrently() override; + Common::Error loadGameState(int slot) override; + bool canSaveGameStateCurrently() override; + Common::Error saveGameState(int slot, const Common::String &desc) override; Common::Error run(); @@ -271,14 +276,15 @@ public: void playerLosesControl(); void playerGainsControl(); - bool saveGame(const Common::String &filename, byte *thumbnail); - void loadGame(const Common::String &filename, byte *thumbnail); - void newGame(); + bool saveGame(Common::WriteStream &stream, const void *thumbnail); + bool loadGame(Common::SeekableReadStream &stream); + void newGame(int difficulty); void autoSaveGame(); void ISez(const Common::String &str); - void blitToScreen(const Graphics::Surface &src); + void blitToScreen(const Graphics::Surface &src) const; + void generateThumbnail(void *thumbnail) const; GUI::Debugger *getDebugger(); }; diff --git a/engines/bladerunner/crimes_database.cpp b/engines/bladerunner/crimes_database.cpp index e9faa22b41..91def98aba 100644 --- a/engines/bladerunner/crimes_database.cpp +++ b/engines/bladerunner/crimes_database.cpp @@ -73,14 +73,14 @@ const char *CrimesDatabase::getClueText(int clueId) const { void CrimesDatabase::save(SaveFileWriteStream &f) { for (int i = 0; i < _crimeCount; ++i) { - uint8 c = _crimes[i]; - f.writeByte(c); + int8 c = _crimes[i]; + f.writeSByte(c); } } void CrimesDatabase::load(SaveFileReadStream &f) { for (int i = 0; i < _crimeCount; ++i) { - _crimes[i] = f.readByte(); + _crimes[i] = f.readSByte(); } } diff --git a/engines/bladerunner/debugger.cpp b/engines/bladerunner/debugger.cpp index 0355f9d0f5..6f7e5d14a2 100644 --- a/engines/bladerunner/debugger.cpp +++ b/engines/bladerunner/debugger.cpp @@ -71,6 +71,8 @@ Debugger::Debugger(BladeRunnerEngine *vm) : GUI::Debugger() { registerCmd("say", WRAP_METHOD(Debugger, cmdSay)); registerCmd("scene", WRAP_METHOD(Debugger, cmdScene)); registerCmd("var", WRAP_METHOD(Debugger, cmdVariable)); + registerCmd("load", WRAP_METHOD(Debugger, cmdLoad)); + registerCmd("save", WRAP_METHOD(Debugger, cmdSave)); } Debugger::~Debugger() { @@ -358,6 +360,59 @@ bool Debugger::cmdVariable(int argc, const char **argv) { return true; } +bool Debugger::cmdLoad(int argc, const char **argv) { + if (argc != 2) { + debugPrintf("Loads a save game from original format.\n"); + debugPrintf("Usage: %s <file path>\n", argv[0]); + return true; + } + + Common::FSNode fs(argv[1]); + + if (!fs.isReadable()) { + debugPrintf("Warning: File %s does not exist or is not readable\n", argv[1]); + return true; + } + + Common::SeekableReadStream *saveFile = fs.createReadStream(); + + _vm->loadGame(*saveFile); + + delete saveFile; + + return false; +} + +bool Debugger::cmdSave(int argc, const char **argv) { + if (argc != 2) { + debugPrintf("Saves game to original format.\n"); + debugPrintf("Usage: %s <file path>\n", argv[0]); + return true; + } + + Common::FSNode fs(argv[1]); + + if (fs.exists() && !fs.isWritable()) { + debugPrintf("Warning: File %s is not writable\n", argv[1]); + return true; + } + + Common::WriteStream *saveFile = fs.createWriteStream(); + + uint16 *thumbnail = new uint16[SaveFile::kThumbnailSize]; + _vm->generateThumbnail(thumbnail); + + _vm->saveGame(*saveFile, thumbnail); + + saveFile->finalize(); + + delete[] thumbnail; + + delete saveFile; + + return true; +} + void Debugger::drawBBox(Vector3 start, Vector3 end, View *view, Graphics::Surface *surface, int color) { Vector3 bfl = view->calculateScreenPosition(Vector3(start.x, start.y, start.z)); Vector3 bfr = view->calculateScreenPosition(Vector3(start.x, end.y, start.z)); diff --git a/engines/bladerunner/debugger.h b/engines/bladerunner/debugger.h index 64e312ae20..fef545eda1 100644 --- a/engines/bladerunner/debugger.h +++ b/engines/bladerunner/debugger.h @@ -59,6 +59,8 @@ public: bool cmdSay(int argc, const char **argv); bool cmdScene(int argc, const char **argv); bool cmdVariable(int argc, const char **argv); + bool cmdLoad(int argc, const char **argv); + bool cmdSave(int argc, const char **argv); void drawBBox(Vector3 start, Vector3 end, View *view, Graphics::Surface *surface, int color); void drawSceneObjects(); diff --git a/engines/bladerunner/detection.cpp b/engines/bladerunner/detection.cpp index 934eea7759..8cd4288a0d 100644 --- a/engines/bladerunner/detection.cpp +++ b/engines/bladerunner/detection.cpp @@ -20,12 +20,17 @@ * */ -#include "base/plugins.h" - -#include "engines/advancedDetector.h" #include "bladerunner/bladerunner.h" #include "bladerunner/detection_tables.h" +#include "bladerunner/savefile.h" + +#include "common/config-manager.h" +#include "common/system.h" +#include "common/savefile.h" +#include "common/serializer.h" + +#include "engines/advancedDetector.h" namespace BladeRunner { @@ -38,19 +43,32 @@ static const PlainGameDescriptor bladeRunnerGames[] = { class BladeRunnerMetaEngine : public AdvancedMetaEngine { public: - BladeRunnerMetaEngine() : AdvancedMetaEngine(BladeRunner::gameDescriptions, sizeof(BladeRunner::gameDescriptions[0]), BladeRunner::bladeRunnerGames) { - } + BladeRunnerMetaEngine(); - virtual const char *getName() const { - return "Blade Runner Engine"; - } + const char *getName() const override; + const char *getOriginalCopyright() const override; + bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override; + bool hasFeature(MetaEngineFeature f) const override; + SaveStateList listSaves(const char *target) const override; + int getMaximumSaveSlot() const override; + void removeSaveState(const char *target, int slot) const override; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override; +}; - virtual const char *getOriginalCopyright() const { - return "Blade Runner (C) Westwood Studios."; - } +BladeRunnerMetaEngine::BladeRunnerMetaEngine() + : AdvancedMetaEngine( + BladeRunner::gameDescriptions, + sizeof(BladeRunner::gameDescriptions[0]), + BladeRunner::bladeRunnerGames) {} - virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; -}; + +const char *BladeRunnerMetaEngine::getName() const { + return "Blade Runner Engine"; +} + +const char *BladeRunnerMetaEngine::getOriginalCopyright() const { + return "Blade Runner (C) 1997 Westwood Studios"; +} bool BladeRunnerMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { *engine = new BladeRunner::BladeRunnerEngine(syst, desc); @@ -58,6 +76,71 @@ bool BladeRunnerMetaEngine::createInstance(OSystem *syst, Engine **engine, const return true; } +bool BladeRunnerMetaEngine::hasFeature(MetaEngineFeature f) const { + return + f == kSupportsListSaves || + f == kSupportsLoadingDuringStartup || + f == kSupportsDeleteSave || + f == kSavesSupportMetaInfo || + f == kSavesSupportThumbnail || + f == kSimpleSavesNames; +} + +SaveStateList BladeRunnerMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray files = saveFileMan->listSavefiles(Common::String::format("%s.###", target)); + + SaveStateList saveList; + for (Common::StringArray::const_iterator fileName = files.begin(); fileName != files.end(); ++fileName) { + Common::InSaveFile *saveFile = saveFileMan->openForLoading(*fileName); + if (saveFile == nullptr || saveFile->err()) { + warning("Cannot open save file '%s'", fileName->c_str()); + continue; + } + + BladeRunner::SaveFileHeader header; + BladeRunner::SaveFile::readHeader(*saveFile, header); + + int slotNum = atoi(fileName->c_str() + fileName->size() - 3); + saveList.push_back(SaveStateDescriptor(slotNum, header._name)); + } + + // Sort saves based on slot number. + Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator()); + return saveList; +} + +int BladeRunnerMetaEngine::getMaximumSaveSlot() const { + return 999; +} + +void BladeRunnerMetaEngine::removeSaveState(const char *target, int slot) const { + Common::String filename = Common::String::format("%s.%03d", target, slot); + g_system->getSavefileManager()->removeSavefile(filename); +} + +SaveStateDescriptor BladeRunnerMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String filename = Common::String::format("%s.%03d", target, slot); + Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(filename); + + if (saveFile == nullptr || saveFile->err()) { + return SaveStateDescriptor(); + } + + BladeRunner::SaveFileHeader header; + if (!BladeRunner::SaveFile::readHeader(*saveFile, header, false)) { + delete saveFile; + return SaveStateDescriptor(); + } + delete saveFile; + + SaveStateDescriptor desc(slot, header._name); + desc.setThumbnail(header._thumbnail); + desc.setSaveDate(header._year, header._month, header._day); + desc.setSaveTime(header._hour, header._minute); + return desc; +} + #if PLUGIN_ENABLED_DYNAMIC(BLADERUNNER) REGISTER_PLUGIN_DYNAMIC(BLADERUNNER, PLUGIN_TYPE_ENGINE, BladeRunnerMetaEngine); #else diff --git a/engines/bladerunner/detection_tables.h b/engines/bladerunner/detection_tables.h index 3c509f4314..5d1a851598 100644 --- a/engines/bladerunner/detection_tables.h +++ b/engines/bladerunner/detection_tables.h @@ -23,6 +23,8 @@ #ifndef BLADERUNNER_DETECTION_TABLES_H #define BLADERUNNER_DETECTION_TABLES_H +#include "engines/advancedDetector.h" + namespace BladeRunner { static const ADGameDescription gameDescriptions[] = { diff --git a/engines/bladerunner/dialogue_menu.cpp b/engines/bladerunner/dialogue_menu.cpp index d9a7c62866..4f82326564 100644 --- a/engines/bladerunner/dialogue_menu.cpp +++ b/engines/bladerunner/dialogue_menu.cpp @@ -183,7 +183,7 @@ int DialogueMenu::queryInput() { } _vm->gameTick(); - } while (_waitingForInput); + } while (_vm->_gameIsRunning && _waitingForInput); } else if (agenda == kPlayerAgendaErratic) { int tries = 0; bool searching = true; diff --git a/engines/bladerunner/dialogue_menu.h b/engines/bladerunner/dialogue_menu.h index 6dfd3efdc6..4029fe465b 100644 --- a/engines/bladerunner/dialogue_menu.h +++ b/engines/bladerunner/dialogue_menu.h @@ -81,6 +81,8 @@ public: DialogueMenu(BladeRunnerEngine *vm); ~DialogueMenu(); + void clear(); + bool loadText(const Common::String &name); bool show(); @@ -106,7 +108,6 @@ private: int getAnswerIndex(int answer) const; const char *getText(int id) const; void calculatePosition(int unusedX = 0, int unusedY = 0); - void clear(); void reset(); static void darkenRect(Graphics::Surface &s, int x1, int y1, int x2, int y2); diff --git a/engines/bladerunner/game_flags.cpp b/engines/bladerunner/game_flags.cpp index 6fdcb89363..e2ae3c3259 100644 --- a/engines/bladerunner/game_flags.cpp +++ b/engines/bladerunner/game_flags.cpp @@ -36,14 +36,19 @@ GameFlags::~GameFlags() { delete[] _flags; } +void GameFlags::clear() { + for (int i = 0; i <= _flagCount; ++i) { + reset(i); + } +} + void GameFlags::setFlagCount(int count) { assert(count > 0); _flagCount = count; _flags = new uint32[count / 32 + 1](); - for (int i = 0; i <= _flagCount; ++i) - reset(i); + clear(); } void GameFlags::set(int flag) { diff --git a/engines/bladerunner/game_flags.h b/engines/bladerunner/game_flags.h index 837537f689..3b0d93f8c8 100644 --- a/engines/bladerunner/game_flags.h +++ b/engines/bladerunner/game_flags.h @@ -38,6 +38,7 @@ public: GameFlags(); ~GameFlags(); + void clear(); void setFlagCount(int count); void set(int flag); diff --git a/engines/bladerunner/item.cpp b/engines/bladerunner/item.cpp index 2f82fc0b35..751978bfd3 100644 --- a/engines/bladerunner/item.cpp +++ b/engines/bladerunner/item.cpp @@ -177,7 +177,7 @@ bool Item::isUnderMouse(int mouseX, int mouseY) const { void Item::save(SaveFileWriteStream &f) { f.writeInt(_setId); f.writeInt(_itemId); - f.writeBoundingBox(_boundingBox); + f.writeBoundingBox(_boundingBox, false); f.writeRect(_screenRectangle); f.writeInt(_animationId); f.writeVector3(_position); @@ -199,7 +199,7 @@ void Item::save(SaveFileWriteStream &f) { void Item::load(SaveFileReadStream &f) { _setId = f.readInt(); _itemId = f.readInt(); - _boundingBox = f.readBoundingBox(); + _boundingBox = f.readBoundingBox(false); _screenRectangle = f.readRect(); _animationId = f.readInt(); _position = f.readVector3(); diff --git a/engines/bladerunner/items.cpp b/engines/bladerunner/items.cpp index a2cb9da194..32594515e6 100644 --- a/engines/bladerunner/items.cpp +++ b/engines/bladerunner/items.cpp @@ -35,6 +35,10 @@ Items::Items(BladeRunnerEngine *vm) { } Items::~Items() { + reset(); +} + +void Items::reset() { for (int i = _items.size() - 1; i >= 0; i--) { delete _items.remove_at(i); } diff --git a/engines/bladerunner/items.h b/engines/bladerunner/items.h index ce29a77787..a2c7720b62 100644 --- a/engines/bladerunner/items.h +++ b/engines/bladerunner/items.h @@ -42,6 +42,8 @@ public: Items(BladeRunnerEngine *vm); ~Items(); + void reset(); + void getXYZ(int itemId, float *x, float *y, float *z) const; void setXYZ(int itemId, Vector3 position); void getWidthHeight(int itemId, int *width, int *height) const; diff --git a/engines/bladerunner/outtake.cpp b/engines/bladerunner/outtake.cpp index 6d76162dd9..187f46c0bd 100644 --- a/engines/bladerunner/outtake.cpp +++ b/engines/bladerunner/outtake.cpp @@ -44,9 +44,9 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co resName = resName + ".VQA"; - VQAPlayer vqa_player(_vm, &_vm->_surfaceFront); + VQAPlayer vqa_player(_vm, &_vm->_surfaceFront, resName); - vqa_player.open(resName); + vqa_player.open(); _vm->_mixer->stopAll(); while (!_vm->shouldQuit()) { diff --git a/engines/bladerunner/overlays.cpp b/engines/bladerunner/overlays.cpp index 65ba83f15d..5bbac87c36 100644 --- a/engines/bladerunner/overlays.cpp +++ b/engines/bladerunner/overlays.cpp @@ -69,14 +69,15 @@ int Overlays::play(const Common::String &name, int loopId, bool loopForever, boo _videos[index].loaded = true; _videos[index].name = name; _videos[index].hash = hash; - _videos[index].vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront); + _videos[index].loopId = loopId; + _videos[index].loopForever = loopForever; + _videos[index].vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront, Common::String::format("%s.VQA", name.c_str())); // repeat forever _videos[index].vqaPlayer->setBeginAndEndFrame(0, 0, -1, kLoopSetModeJustStart, nullptr, nullptr); } - Common::String resourceName = Common::String::format("%s.VQA", name.c_str()); - _videos[index].vqaPlayer->open(resourceName); + _videos[index].vqaPlayer->open(); _videos[index].vqaPlayer->setLoop( loopId, loopForever ? -1 : 0, @@ -86,6 +87,29 @@ int Overlays::play(const Common::String &name, int loopId, bool loopForever, boo return index; } +void Overlays::resume(bool isLoadingGame) { + + for (int i = 0; i < kOverlayVideos; ++i) { + if (_videos[i].loaded && isLoadingGame) { + _videos[i].vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront, Common::String::format("%s.VQA", _videos[i].name.c_str())); + if (!_videos[i].vqaPlayer) { + resetSingle(i); + continue; + } + + _videos[i].vqaPlayer->open(); + _videos[i].vqaPlayer->setLoop( + _videos[i].loopId, + _videos[i].loopForever ? -1 : 0, + kLoopSetModeImmediate, + nullptr, nullptr); + + _videos[i].vqaPlayer->seekToFrame(_videos[i].frame); + _videos[i].vqaPlayer->update(true); + } + } +} + void Overlays::remove(const Common::String &name) { int index = findByHash(MIXArchive::getHash(name)); if (index >= 0) { @@ -104,8 +128,8 @@ void Overlays::removeAll() { void Overlays::tick() { for (int i = 0; i < kOverlayVideos; ++i) { if (_videos[i].loaded) { - int frame = _videos[i].vqaPlayer->update(true); - if (frame < 0) { + _videos[i].frame = _videos[i].vqaPlayer->update(true); + if (_videos[i].frame < 0) { resetSingle(i); } } @@ -138,7 +162,7 @@ void Overlays::resetSingle(int i) { } _videos[i].loaded = false; _videos[i].hash = 0; - _videos[i].field2 = -1; + _videos[i].frame = -1; _videos[i].name.clear(); } @@ -155,9 +179,9 @@ void Overlays::save(SaveFileWriteStream &f) { f.writeInt(0); // vqaPlayer pointer f.writeStringSz(ov.name, 13); f.writeSint32LE(ov.hash); - f.writeInt(ov.field0); - f.writeInt(ov.field1); - f.writeInt(ov.field2); + f.writeInt(ov.loopId); + f.writeBool(ov.loopForever); + f.writeInt(ov.frame); } } @@ -171,9 +195,9 @@ void Overlays::load(SaveFileReadStream &f) { ov.vqaPlayer = nullptr; ov.name = f.readStringSz(13); ov.hash = f.readSint32LE(); - ov.field0 = f.readInt(); - ov.field1 = f.readInt(); - ov.field2 = f.readInt(); + ov.loopId = f.readInt(); + ov.loopForever = f.readBool(); + ov.frame = f.readInt(); } } diff --git a/engines/bladerunner/overlays.h b/engines/bladerunner/overlays.h index 405acbc264..e250db9810 100644 --- a/engines/bladerunner/overlays.h +++ b/engines/bladerunner/overlays.h @@ -45,9 +45,9 @@ class Overlays { VQAPlayer *vqaPlayer; Common::String name; int32 hash; - int field0; - int field1; - int field2; + int loopId; + bool loopForever; + int frame; }; BladeRunnerEngine *_vm; @@ -59,6 +59,7 @@ public: ~Overlays(); int play(const Common::String &name, int loopId, bool loopForever, bool startNow, int a6); + void resume(bool isLoadingGame); void remove(const Common::String &name); void removeAll(); void tick(); diff --git a/engines/bladerunner/savefile.cpp b/engines/bladerunner/savefile.cpp index 3528a6bb82..b0fb1e7fd6 100644 --- a/engines/bladerunner/savefile.cpp +++ b/engines/bladerunner/savefile.cpp @@ -27,13 +27,76 @@ #include "common/rect.h" #include "common/savefile.h" +#include "common/system.h" + +#include "graphics/thumbnail.h" namespace BladeRunner { -SaveFileWriteStream::SaveFileWriteStream() - : MemoryWriteStreamDynamic(DisposeAfterUse::YES) { +bool SaveFile::readHeader(Common::SeekableReadStream &in, SaveFileHeader &header, bool skipThumbnail) { + SaveFileReadStream s(in); + + if (s.readUint32BE() != kTag) { + warning("No header found in save file"); + return false; + } + + header._version = s.readByte(); + if (header._version != kVersion) { + warning("Unsupported version of save file %u, supported is %u", header._version, kVersion); + return false; + } + + header._name = s.readStringSz(kNameLength); + + header._year = s.readUint16LE(); + header._month = s.readUint16LE(); + header._day = s.readUint16LE(); + header._hour = s.readUint16LE(); + header._minute = s.readUint16LE(); + + header._thumbnail = nullptr; + + if (!skipThumbnail) { + header._thumbnail = new Graphics::Surface(); + + int32 pos = s.pos(); + + s.skip(4); //skip size; + + void *thumbnailData = new byte[kThumbnailSize]; + s.read(thumbnailData, kThumbnailSize); + + // TODO: cleanup - remove magic constants + header._thumbnail->init(80, 60, 160, thumbnailData, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); + + s.seek(pos); + } + + return true; +} + +bool SaveFile::writeHeader(Common::WriteStream &out, SaveFileHeader &header) { + SaveFileWriteStream s(out); + + s.writeUint32BE(kTag); + s.writeByte(kVersion); + + s.writeStringSz(header._name, kNameLength); + + TimeDate td; + g_system->getTimeAndDate(td); + s.writeUint16LE(td.tm_year + 1900); + s.writeUint16LE(td.tm_mon + 1); + s.writeUint16LE(td.tm_mday); + s.writeUint16LE(td.tm_hour); + s.writeUint16LE(td.tm_min); + + return true; } +SaveFileWriteStream::SaveFileWriteStream(Common::WriteStream &s) : _s(s) {} + void SaveFileWriteStream::debug(char *p) { write(p, strlen(p) + 1); } @@ -48,7 +111,7 @@ void SaveFileWriteStream::writeInt(int v) { writeUint32LE(v); } -void SaveFileWriteStream::writeFloat(int v) { +void SaveFileWriteStream::writeFloat(float v) { writeFloatLE(v); } @@ -80,7 +143,7 @@ void SaveFileWriteStream::writeRect(const Common::Rect &v) { writeUint32LE(v.bottom); } -void SaveFileWriteStream::writeBoundingBox(const BoundingBox &v) { +void SaveFileWriteStream::writeBoundingBox(const BoundingBox &v, bool serialized) { float x0, y0, z0, x1, y1, z1; v.getXYZ(&x0, &y0, &z0, &x1, &y1, &z1); @@ -92,14 +155,13 @@ void SaveFileWriteStream::writeBoundingBox(const BoundingBox &v) { writeFloatLE(z1); // Bounding boxes have a lot of extra data that's never actually used - for (int i = 0; i != 96; ++i) { + int count = serialized ? 96 : 64; + for (int i = 0; i < count; ++i) { writeFloatLE(0.0f); } } -SaveFileReadStream::SaveFileReadStream(const byte *dataPtr, uint32 dataSize) - : MemoryReadStream(dataPtr, dataSize, DisposeAfterUse::YES) { -} +SaveFileReadStream::SaveFileReadStream(Common::SeekableReadStream &s) : _s(s) {} int SaveFileReadStream::readInt() { return readUint32LE(); @@ -114,10 +176,10 @@ bool SaveFileReadStream::readBool() { } Common::String SaveFileReadStream::readStringSz(int sz) { - char *buf = (char *)malloc(sz); + char *buf = new char[sz]; read(buf, sz); Common::String result = buf; - free(buf); + delete[] buf; return result; } @@ -145,18 +207,22 @@ Common::Rect SaveFileReadStream::readRect() { return result; } -BoundingBox SaveFileReadStream::readBoundingBox() { +BoundingBox SaveFileReadStream::readBoundingBox(bool serialized) { float x0, y0, z0, x1, y1, z1; x0 = readFloatLE(); y0 = readFloatLE(); z0 = readFloatLE(); + x1 = readFloatLE(); y1 = readFloatLE(); z1 = readFloatLE(); - // Bounding boxes have a lot of extra data that's never actually used - skip(384); + // Bounding boxes have a lot of extra data that's never actually used, and there two formats for storing bounding boxes. + int count = serialized ? 96 : 64; + for (int i = 0; i < count; ++i) { + readFloatLE(); + } return BoundingBox(x0, y0, z0, x1, y1, z1); } diff --git a/engines/bladerunner/savefile.h b/engines/bladerunner/savefile.h index 4dfdb20bd4..bcd1619496 100644 --- a/engines/bladerunner/savefile.h +++ b/engines/bladerunner/savefile.h @@ -26,6 +26,8 @@ #include "common/memstream.h" #include "common/types.h" +#include "graphics/surface.h" + namespace Common { class OutSaveFile; class String; @@ -38,27 +40,67 @@ class Vector2; class Vector3; class BoundingBox; -class SaveFileWriteStream : public Common::MemoryWriteStreamDynamic { +struct SaveFileHeader { + uint8 _version; + Common::String _name; + int _year; + int _month; + int _day; + int _hour; + int _minute; + Graphics::Surface *_thumbnail; +}; + +class SaveFile { +private: + static const uint32 kTag = MKTAG('B', 'R', 'S', 'V'); + static const uint32 kVersion = 1; + static const uint32 kNameLength = 32; + +public: + static const uint32 kThumbnailSize = 9600; // 80x60x16bpp + + static bool readHeader(Common::SeekableReadStream &in, SaveFileHeader &header, bool skipThumbnail = true); + static bool writeHeader(Common::WriteStream &out, SaveFileHeader &header); +}; + +class SaveFileWriteStream : public Common::WriteStream { +private: + Common::WriteStream &_s; + public: - SaveFileWriteStream(); + SaveFileWriteStream(Common::WriteStream &s); + + uint32 write(const void *dataPtr, uint32 dataSize) override { return _s.write(dataPtr, dataSize); } + bool flush() override { return _s.flush(); } + int32 pos() const override { return _s.pos(); } void debug(char *p); void padBytes(int count); void writeInt(int v); - void writeFloat(int v); + void writeFloat(float v); void writeBool(bool v); void writeStringSz(const Common::String &s, int sz); void writeVector2(const Vector2 &v); void writeVector3(const Vector3 &v); void writeRect(const Common::Rect &v); - void writeBoundingBox(const BoundingBox &v); + void writeBoundingBox(const BoundingBox &v, bool serialized); }; -class SaveFileReadStream : public Common::MemoryReadStream { +class SaveFileReadStream : public Common::SeekableReadStream { +private: + Common::SeekableReadStream &_s; + public: - SaveFileReadStream(const byte *dataPtr, uint32 dataSize); + SaveFileReadStream(Common::SeekableReadStream &s); + + bool eos() const override { return _s.eos(); } + uint32 read(void *dataPtr, uint32 dataSize) override { return _s.read(dataPtr, dataSize); } + int32 pos() const override { return _s.pos(); } + int32 size() const override { return _s.size(); } + bool seek(int32 offset, int whence = SEEK_SET) override { return _s.seek(offset, whence); } int readInt(); float readFloat(); @@ -67,7 +109,7 @@ public: Vector2 readVector2(); Vector3 readVector3(); Common::Rect readRect(); - BoundingBox readBoundingBox(); + BoundingBox readBoundingBox(bool serialized); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/scene.cpp b/engines/bladerunner/scene.cpp index fe8dbc7b61..09764c9e50 100644 --- a/engines/bladerunner/scene.cpp +++ b/engines/bladerunner/scene.cpp @@ -87,7 +87,7 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) { const Common::String sceneName = _vm->_gameInfo->getSceneName(_sceneId); if (isLoadingGame) { - // TODO: _vm->overlays->resume() + _vm->_overlays->resume(true); } else { _regions->clear(); _exits->clear(); @@ -113,7 +113,7 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) { delete _vqaPlayer; } - _vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack); + _vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, vqaName); if (!_vm->_sceneScript->open(sceneName)) { return false; @@ -138,7 +138,7 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) { return true; } - if (!_vqaPlayer->open(vqaName)) { + if (!_vqaPlayer->open()) { return false; } @@ -253,7 +253,9 @@ void Scene::resume(bool isLoadingGame) { int targetFrame = _frame; - if (!isLoadingGame) { + if (isLoadingGame) { + _vqaPlayer->open(); + } else { _vm->_zbuffer->disable(); } diff --git a/engines/bladerunner/scene_objects.cpp b/engines/bladerunner/scene_objects.cpp index 30802a8d64..51538cd6fc 100644 --- a/engines/bladerunner/scene_objects.cpp +++ b/engines/bladerunner/scene_objects.cpp @@ -331,7 +331,7 @@ void SceneObjects::save(SaveFileWriteStream &f) { for (int i = 0; i < kSceneObjectCount; ++i) { f.writeInt(_sceneObjects[i].id); f.writeInt(_sceneObjects[i].type); - f.writeBoundingBox(_sceneObjects[i].boundingBox); + f.writeBoundingBox(_sceneObjects[i].boundingBox, true); f.writeRect(_sceneObjects[i].screenRectangle); f.writeFloat(_sceneObjects[i].distanceToCamera); f.writeBool(_sceneObjects[i].isPresent); @@ -352,7 +352,8 @@ void SceneObjects::load(SaveFileReadStream &f) { for (int i = 0; i < kSceneObjectCount; ++i) { _sceneObjects[i].id = f.readInt(); _sceneObjects[i].type = (SceneObjectType)f.readInt(); - _sceneObjects[i].boundingBox = f.readBoundingBox(); + _sceneObjects[i].boundingBox = f.readBoundingBox(true); + debug("screenRectangle[%i]: %08x", i, f.pos()); _sceneObjects[i].screenRectangle = f.readRect(); _sceneObjects[i].distanceToCamera = f.readFloat(); _sceneObjects[i].isPresent = f.readBool(); diff --git a/engines/bladerunner/script/ai_script.cpp b/engines/bladerunner/script/ai_script.cpp index b328824de1..6addc4382b 100644 --- a/engines/bladerunner/script/ai_script.cpp +++ b/engines/bladerunner/script/ai_script.cpp @@ -364,7 +364,6 @@ void AIScripts::queryAnimationState(int actor, int *animationState, int *animati _inScriptCounter++; if (_AIScripts[actor]) { - _AIScripts[actor]->FledCombat(); _AIScripts[actor]->QueryAnimationState(animationState, animationFrame, animationStateNext, animationNext); } _inScriptCounter--; diff --git a/engines/bladerunner/script/police_maze.cpp b/engines/bladerunner/script/police_maze.cpp index a85fa07451..284b55dcb0 100644 --- a/engines/bladerunner/script/police_maze.cpp +++ b/engines/bladerunner/script/police_maze.cpp @@ -175,7 +175,7 @@ void PoliceMazeTargetTrack::clear(bool isLoadingGame) { void PoliceMazeTargetTrack::add(int trackId, float startX, float startY, float startZ, float endX, float endY, float endZ, int steps, const int *instructions, bool isActive) { _data = (const int *)instructions; - if (true /* !GameIsLoading */) { // TODO: FIXME + if (!_vm->_gameIsLoading) { _itemId = trackId; _pointCount = steps; _dataIndex = 0; diff --git a/engines/bladerunner/script/scene/rc01.cpp b/engines/bladerunner/script/scene/rc01.cpp index db89807022..b217d030f9 100644 --- a/engines/bladerunner/script/scene/rc01.cpp +++ b/engines/bladerunner/script/scene/rc01.cpp @@ -50,7 +50,7 @@ void SceneScriptRC01::InitializeScene() { #if BLADERUNNER_DEBUG_GAME //TODO: not part of game, remove Game_Flag_Set(kFlagIntroPlayed); // force skip intro - Game_Flag_Set(kFlagRC02toRC01); // no landing + Game_Flag_Set(kFlagRC02toRC01); // no landing // Game_Flag_Set(kFlagRC01PoliceDone); // Game_Flag_Set(kFlagKIAPrivacyAddon); // Game_Flag_Set(kFlagZubenRetired); @@ -74,7 +74,7 @@ void SceneScriptRC01::InitializeScene() { // Global_Variable_Set(kVariableChapter, 2); // Chapter_Enter(2, kSetRC03, kSceneRC03); - Set_Enter(14, 73); + // Set_Enter(14, 73); #endif diff --git a/engines/bladerunner/script/script.cpp b/engines/bladerunner/script/script.cpp index 92fbc2b4a0..9d9243afa5 100644 --- a/engines/bladerunner/script/script.cpp +++ b/engines/bladerunner/script/script.cpp @@ -589,7 +589,7 @@ void ScriptBase::Loop_Actor_Travel_Stairs(int actorId, int stepCount, bool up, i break; } } - } while (true); + } while (_vm->_gameIsRunning); actor->setImmunityToObstacles(immunityToObstacles); actor->setAtXYZ(Vector3(actor->getX(), targetY, actor->getZ()), actor->getFacing(), true, false, false); @@ -632,7 +632,7 @@ void ScriptBase::Loop_Actor_Travel_Ladder(int actorId, int stepCount, bool up, i break; } } - } while (true); + } while (_vm->_gameIsRunning); actor->setImmunityToObstacles(immunityToObstacles); actor->setAtXYZ(Vector3(actor->getX(), targetY, actor->getZ()), actor->getFacing(), true, false, false); @@ -730,7 +730,7 @@ int ScriptBase::Animation_Skip_To_Frame() { void ScriptBase::Delay(int miliseconds) { Player_Loses_Control(); int endTime = _vm->getTotalPlayTime() + miliseconds; - while ((int)_vm->getTotalPlayTime() < endTime) { + while (_vm->_gameIsRunning && (int)_vm->getTotalPlayTime() < endTime) { _vm->gameTick(); } Player_Gains_Control(); diff --git a/engines/bladerunner/set.cpp b/engines/bladerunner/set.cpp index ac026a4d76..8111d0b38c 100644 --- a/engines/bladerunner/set.cpp +++ b/engines/bladerunner/set.cpp @@ -340,7 +340,7 @@ void Set::save(SaveFileWriteStream &f) { for (int i = 0; i != _objectCount; ++i) { f.writeStringSz(_objects[i].name, 20); - f.writeBoundingBox(_objects[i].bbox); + f.writeBoundingBox(_objects[i].bbox, true); f.writeBool(_objects[i].isObstacle); f.writeBool(_objects[i].isClickable); f.writeBool(_objects[i].isHotMouse); @@ -375,7 +375,7 @@ void Set::load(SaveFileReadStream &f) { for (int i = 0; i != _objectCount; ++i) { _objects[i].name = f.readStringSz(20); - _objects[i].bbox = f.readBoundingBox(); + _objects[i].bbox = f.readBoundingBox(true); _objects[i].isObstacle = f.readBool(); _objects[i].isClickable = f.readBool(); _objects[i].isHotMouse = f.readBool(); diff --git a/engines/bladerunner/settings.cpp b/engines/bladerunner/settings.cpp index 071adf6dd5..6c3e5a7e33 100644 --- a/engines/bladerunner/settings.cpp +++ b/engines/bladerunner/settings.cpp @@ -73,6 +73,13 @@ Settings::Settings(BladeRunnerEngine *vm) { _learyMode = false; } +void Settings::reset() { + _ammoType = 0; + _ammoAmounts[0] = 1; + _ammoAmounts[1] = 0; + _ammoAmounts[2] = 0; +} + bool Settings::openNewScene() { if (_newSet == -1) { assert(_newScene == -1); @@ -108,8 +115,9 @@ bool Settings::openNewScene() { return false; } _chapter = newChapter; - if (_startingGame) + if (_startingGame) { _startingGame = false; + } } if (!_vm->_scene->open(newSet, newScene, _loadingGame)) { @@ -184,6 +192,10 @@ int Settings::getDifficulty() const { return _difficulty; } +void Settings::setDifficulty(int difficulty) { + _difficulty = difficulty; +} + int Settings::getPlayerAgenda() const { return _playerAgenda; } diff --git a/engines/bladerunner/settings.h b/engines/bladerunner/settings.h index 413786e2e5..3c7048f02b 100644 --- a/engines/bladerunner/settings.h +++ b/engines/bladerunner/settings.h @@ -70,6 +70,8 @@ class Settings { public: Settings(BladeRunnerEngine *vm); + void reset(); + void setGamma(float gamma) { _gamma = gamma; } @@ -109,16 +111,16 @@ public: _newChapter = newChapter; } - void setLoadingGame(bool loadingGame) { - _loadingGame = loadingGame; + void setLoadingGame() { + _loadingGame = true; } - bool getLoadingGame() const { + bool isLoadingGame() const { return _loadingGame; } - void setStartingGame(bool startingGame) { - _startingGame = startingGame; + void setStartingGame() { + _startingGame = true; } bool openNewScene(); @@ -130,6 +132,7 @@ public: void decreaseAmmo(); int getDifficulty() const; + void setDifficulty(int difficulty); int getPlayerAgenda() const; void setPlayerAgenda(int agenda); diff --git a/engines/bladerunner/slice_renderer.cpp b/engines/bladerunner/slice_renderer.cpp index 932e02bee8..5cf818c1ff 100644 --- a/engines/bladerunner/slice_renderer.cpp +++ b/engines/bladerunner/slice_renderer.cpp @@ -207,15 +207,13 @@ void SliceRenderer::calculateBoundingRect() { * Calculate min and max X */ - /* TODO, there is something off with X scaling when Y is high like in rc02, on sides, x seems to be ofsetted a bit more than it should. Start/top vector is doing that */ - Matrix3x2 facingRotation = calculateFacingRotationMatrix(); Matrix3x2 mProjection(_view->_viewportPosition.z / bottom.z, 0.0f, 0.0f, 0.0f, 25.5f, 0.0f); Matrix3x2 mOffset(1.0f, 0.0f, _framePos.x, - 0.0f, 1.0f, _framePos.y); + 0.0f, 1.0f, _framePos.y); Matrix3x2 mScale(_frameScale.x, 0.0f, 0.0f, 0.0f, _frameScale.y, 0.0f); @@ -281,7 +279,6 @@ void SliceRenderer::loadFrame(int animation, int frame) { } struct SliceLineIterator { - // int _sliceMatrix[2][3]; Matrix3x2 _sliceMatrix; int _startY; int _endY; diff --git a/engines/bladerunner/suspects_database.h b/engines/bladerunner/suspects_database.h index c4ed08998e..3eff7a7ba3 100644 --- a/engines/bladerunner/suspects_database.h +++ b/engines/bladerunner/suspects_database.h @@ -96,7 +96,6 @@ public: int getPhotoShapeId(int photoId) const; int getPhotoNotUsed(int photoId) const; -private: void reset(); }; diff --git a/engines/bladerunner/ui/elevator.cpp b/engines/bladerunner/ui/elevator.cpp index 7a6ab3ca35..c7432368f2 100644 --- a/engines/bladerunner/ui/elevator.cpp +++ b/engines/bladerunner/ui/elevator.cpp @@ -66,8 +66,8 @@ int Elevator::activate(int elevatorId) { return 0; } - _vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack); - if (!_vqaPlayer->open(vqaName)) { + _vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, vqaName); + if (!_vqaPlayer->open()) { return 0; } @@ -161,7 +161,7 @@ int Elevator::activate(int elevatorId) { _buttonClicked = -1; do { _vm->gameTick(); - } while (_buttonClicked == -1); + } while (_vm->_gameIsRunning && _buttonClicked == -1); _imagePicker->deactivate(); diff --git a/engines/bladerunner/ui/esper.cpp b/engines/bladerunner/ui/esper.cpp index 338ee148f3..6803ce5c5c 100644 --- a/engines/bladerunner/ui/esper.cpp +++ b/engines/bladerunner/ui/esper.cpp @@ -107,8 +107,8 @@ void ESPER::open(Graphics::Surface *surface) { _shapesPhotos.resize(10); - _vqaPlayerMain = new VQAPlayer(_vm, &_vm->_surfaceBack); - if (!_vqaPlayerMain->open("ESPER.VQA")) { + _vqaPlayerMain = new VQAPlayer(_vm, &_vm->_surfaceBack, "ESPER.VQA"); + if (!_vqaPlayerMain->open()) { return; } _vqaPlayerMain->setLoop(2, -1, kLoopSetModeJustStart, nullptr, nullptr); @@ -534,7 +534,7 @@ void ESPER::wait(int timeout) { if (!_isWaiting) { _isWaiting = true; uint timeEnd = timeout + _vm->getTotalPlayTime(); - while (_vm->getTotalPlayTime() < timeEnd) { + while (_vm->_gameIsRunning && _vm->getTotalPlayTime() < timeEnd) { _vm->gameTick(); } _isWaiting = false; @@ -866,8 +866,8 @@ void ESPER::drawPhotoZoomOut(Graphics::Surface &surface) { void ESPER::drawVideoZooming(Graphics::Surface &surface) { if (_vqaPlayerPhoto == nullptr) { - _vqaPlayerPhoto = new VQAPlayer(_vm, &_surfaceViewport); - if (!_vqaPlayerPhoto->open(Common::String(_regions[_regionSelected].name) + ".VQA")) { + _vqaPlayerPhoto = new VQAPlayer(_vm, &_surfaceViewport, Common::String(_regions[_regionSelected].name) + ".VQA"); + if (!_vqaPlayerPhoto->open()) { setStatePhoto(kEsperPhotoStateShow); _vm->_mouse->enable(); diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp index 756f1bd2cd..4b95607541 100644 --- a/engines/bladerunner/ui/kia.cpp +++ b/engines/bladerunner/ui/kia.cpp @@ -88,6 +88,8 @@ KIA::KIA(BladeRunnerEngine *vm) { _pogoPos = 0; + _thumbnail = nullptr; + _buttons = new UIImagePicker(_vm, 22); _crimesSection = new KIASectionCrimes(_vm, _vm->_playerActor->_clues); @@ -124,6 +126,18 @@ KIA::~KIA() { delete _script; } +void KIA::reset() { + _lastSectionIdKIA = kKIASectionCrimes; + _lastSectionIdOptions = kKIASectionSettings; + _playerVqaFrame = 0; + _playerVisualizerState = 0; + _playerSliceModelAngle = 0.0f; + + _crimesSection->reset(); + _suspectsSection->reset(); + _cluesSection->reset(); +} + void KIA::openLastOpened() { open(_lastSectionIdKIA); } @@ -169,8 +183,8 @@ void KIA::open(KIASections sectionId) { _mainVqaPlayer = nullptr; } - _mainVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack); - _mainVqaPlayer->open(name); + _mainVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, name); + _mainVqaPlayer->open(); } if (_transitionId) { @@ -621,6 +635,9 @@ void KIA::loopEnded(void *callbackData, int frame, int loopId) { } void KIA::init() { + _thumbnail = new byte[SaveFile::kThumbnailSize]; + _vm->generateThumbnail(_thumbnail); + if (!_vm->openArchive("MODE.MIX")) { return; } @@ -640,8 +657,8 @@ void KIA::init() { _vm->_mouse->setCursor(0); if (_playerVqaPlayer == nullptr) { - _playerVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront); - _playerVqaPlayer->open("kiaover.vqa"); + _playerVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceFront, "kiaover.vqa"); + _playerVqaPlayer->open(); _playerVqaPlayer->setLoop(0, -1, kLoopSetModeJustStart, nullptr, nullptr); } _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(501), 70, 0, 0, 50, 0); @@ -650,6 +667,9 @@ void KIA::init() { } void KIA::unload() { + delete[] _thumbnail; + _thumbnail = nullptr; + if (!isOpen()) { return; } @@ -684,7 +704,7 @@ void KIA::unload() { // TODO: Unfreeze game time - if (!_vm->_settings->getLoadingGame() && _vm->_gameIsRunning) { + if (!_vm->_settings->isLoadingGame() && _vm->_gameIsRunning) { _vm->_scene->resume(); } } diff --git a/engines/bladerunner/ui/kia.h b/engines/bladerunner/ui/kia.h index 1c2dc19d9f..0612234db1 100644 --- a/engines/bladerunner/ui/kia.h +++ b/engines/bladerunner/ui/kia.h @@ -117,6 +117,8 @@ class KIA { int _pogoPos; + byte *_thumbnail; + public: KIALog *_log; KIAScript *_script; @@ -126,6 +128,8 @@ public: KIA(BladeRunnerEngine *vm); ~KIA(); + void reset(); + void openLastOpened(); void open(KIASections sectionId); bool isOpen() const; diff --git a/engines/bladerunner/ui/kia_section_clues.cpp b/engines/bladerunner/ui/kia_section_clues.cpp index e003e9bb28..7485d8ebcb 100644 --- a/engines/bladerunner/ui/kia_section_clues.cpp +++ b/engines/bladerunner/ui/kia_section_clues.cpp @@ -80,6 +80,18 @@ KIASectionClues::~KIASectionClues() { delete _uiContainer; } +void KIASectionClues::reset() { + _debugIntangible = false; + _debugNop = 0; + + _mouseX = 0; + _mouseY = 0; + + for (int i = 0; i < _filterCount; ++i) { + _filters[i] = true; + } +} + void KIASectionClues::open() { _isOpen = true; diff --git a/engines/bladerunner/ui/kia_section_clues.h b/engines/bladerunner/ui/kia_section_clues.h index 3f6a13d135..afd8b67433 100644 --- a/engines/bladerunner/ui/kia_section_clues.h +++ b/engines/bladerunner/ui/kia_section_clues.h @@ -67,6 +67,8 @@ public: KIASectionClues(BladeRunnerEngine *vm, ActorClues *clues); ~KIASectionClues(); + void reset(); + void open(); void close(); diff --git a/engines/bladerunner/ui/kia_section_crimes.cpp b/engines/bladerunner/ui/kia_section_crimes.cpp index 96075fbc22..ff6352b3e0 100644 --- a/engines/bladerunner/ui/kia_section_crimes.cpp +++ b/engines/bladerunner/ui/kia_section_crimes.cpp @@ -41,7 +41,6 @@ #include "bladerunner/ui/ui_image_picker.h" #include "bladerunner/ui/ui_scroll_box.h" - #include "graphics/surface.h" namespace BladeRunner { @@ -71,6 +70,7 @@ KIASectionCrimes::KIASectionCrimes(BladeRunnerEngine *vm, ActorClues *clues) : K _suspectSelected = -1; _suspectPhotoShapeId = -1; + _suspectPhotoNotUsed = -1; _suspectPhotoShape = nullptr; _suspectsFoundCount = 0; _suspectsFound.resize(_vm->_gameInfo->getSuspectCount()); @@ -87,6 +87,18 @@ KIASectionCrimes::~KIASectionCrimes() { delete _uiContainer; } +void KIASectionCrimes::reset() { + _acquiredClueCount = 0; + _crimesFoundCount = 0; + _suspectsFoundCount = 0; + _mouseX = 0; + _mouseY = 0; + _suspectSelected = -1; + _crimeSelected = -1; + _suspectPhotoShapeId = -1; + _suspectPhotoNotUsed = -1; +} + void KIASectionCrimes::open() { _scheduledSwitch = false; @@ -277,7 +289,7 @@ void KIASectionCrimes::populateAcquiredClues() { ++_acquiredClueCount; } } - // sort clues by name, is it necessary + // sort clues by name, is it necessary? } void KIASectionCrimes::populateCrimes() { @@ -391,18 +403,20 @@ void KIASectionCrimes::updateSuspectPhoto() { SuspectDatabaseEntry *suspect = _vm->_suspectsDatabase->get(_suspectSelected); _suspectPhotoShapeId = -1; + _suspectPhotoNotUsed = -1; int photoCluesCount = suspect->getPhotoCount(); if (photoCluesCount > 0) { for (int i = 0 ; i < photoCluesCount; i++) { - //TODO: weird stuff going on here... it's using index instead id, also some field is used but its always -1 + //TODO: weird stuff going on here... original game is using internal clue index instead id if (_clues->isAcquired(suspect->getPhotoClueId(i))) { _suspectPhotoShapeId = suspect->getPhotoShapeId(i); + _suspectPhotoNotUsed = suspect->getPhotoNotUsed(i); break; } } } - if (_suspectPhotoShapeId == -1) { + if (_suspectPhotoShapeId == -1 && _suspectPhotoNotUsed == -1) { if (suspect->getSex()) { _suspectPhotoShapeId = 14; } else { diff --git a/engines/bladerunner/ui/kia_section_crimes.h b/engines/bladerunner/ui/kia_section_crimes.h index 23983b8d39..bd2a2f2d80 100644 --- a/engines/bladerunner/ui/kia_section_crimes.h +++ b/engines/bladerunner/ui/kia_section_crimes.h @@ -69,6 +69,7 @@ class KIASectionCrimes : public KIASectionBase { int _mouseY; int _suspectPhotoShapeId; + int _suspectPhotoNotUsed; Shape *_suspectPhotoShape; public: @@ -78,6 +79,8 @@ public: KIASectionCrimes(BladeRunnerEngine *vm, ActorClues *clues); ~KIASectionCrimes(); + void reset(); + void open(); void close(); diff --git a/engines/bladerunner/ui/kia_section_suspects.cpp b/engines/bladerunner/ui/kia_section_suspects.cpp index 54e33cb62b..460f744a2a 100644 --- a/engines/bladerunner/ui/kia_section_suspects.cpp +++ b/engines/bladerunner/ui/kia_section_suspects.cpp @@ -86,6 +86,7 @@ KIASectionSuspects::KIASectionSuspects(BladeRunnerEngine *vm, ActorClues *clues) _suspectSelected = -1; _suspectPhotoShapeId = -1; + _suspectPhotoNotUsed = -1; _suspectPhotoShape = nullptr; _suspectsFoundCount = 0; _suspectsFound.resize(_vm->_gameInfo->getSuspectCount()); @@ -108,6 +109,22 @@ KIASectionSuspects::~KIASectionSuspects() { delete _uiContainer; } +void KIASectionSuspects::reset() { + _acquiredClueCount = 0; + _suspectsFoundCount = 0; + _mouseX = 0; + _mouseY = 0; + _suspectSelected = -1; + _crimeSelected = -1; + _suspectPhotoShapeId = -1; + _suspectPhotoNotUsed = -1; + _whereaboutsFilter = true; + _MOFilter = true; + _replicantFilter = true; + _nonReplicantFilter = true; + _othersFilter = true; +} + void KIASectionSuspects::open() { _scheduledSwitch = false; @@ -169,7 +186,6 @@ void KIASectionSuspects::draw(Graphics::Surface &surface) { _uiContainer->draw(surface); - _vm->_mainFont->drawColor(_vm->_textKIA->getText(0), surface, 300, 162, 0x77DF); _vm->_mainFont->drawColor(_vm->_textKIA->getText(46), surface, 142, 248, 0x77DF); _vm->_mainFont->drawColor(_vm->_textKIA->getText(47), surface, 142, 308, 0x77DF); @@ -479,18 +495,20 @@ void KIASectionSuspects::updateSuspectPhoto() { SuspectDatabaseEntry *suspect = _vm->_suspectsDatabase->get(_suspectSelected); _suspectPhotoShapeId = -1; + _suspectPhotoNotUsed = -1; int photoCluesCount = suspect->getPhotoCount(); if (photoCluesCount > 0) { for (int i = 0 ; i < photoCluesCount; i++) { - //TODO: weird stuff going on here... it's using index instead id, also some field is used but its always -1 + //TODO: weird stuff going on here... original game is using internal clue index instead id if (_clues->isAcquired(suspect->getPhotoClueId(i))) { _suspectPhotoShapeId = suspect->getPhotoShapeId(i); + _suspectPhotoNotUsed = suspect->getPhotoNotUsed(i); break; } } } - if (_suspectPhotoShapeId == -1) { + if (_suspectPhotoShapeId == -1 && _suspectPhotoNotUsed == -1) { if (suspect->getSex()) { _suspectPhotoShapeId = 14; } else { diff --git a/engines/bladerunner/ui/kia_section_suspects.h b/engines/bladerunner/ui/kia_section_suspects.h index 0cc957f609..22a3accbee 100644 --- a/engines/bladerunner/ui/kia_section_suspects.h +++ b/engines/bladerunner/ui/kia_section_suspects.h @@ -78,6 +78,7 @@ class KIASectionSuspects : public KIASectionBase { int _mouseY; int _suspectPhotoShapeId; + int _suspectPhotoNotUsed; Shape *_suspectPhotoShape; public: @@ -87,6 +88,8 @@ public: KIASectionSuspects(BladeRunnerEngine *vm, ActorClues *clues); ~KIASectionSuspects(); + void reset(); + void open(); void close(); diff --git a/engines/bladerunner/ui/scores.cpp b/engines/bladerunner/ui/scores.cpp index c4a7df778c..8fc3378207 100644 --- a/engines/bladerunner/ui/scores.cpp +++ b/engines/bladerunner/ui/scores.cpp @@ -50,9 +50,9 @@ void Scores::open() { return; } - _vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack); + _vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, "SCORE.VQA"); - if (!_vqaPlayer->open("SCORE.VQA")) { + if (!_vqaPlayer->open()) { return; } diff --git a/engines/bladerunner/ui/spinner.cpp b/engines/bladerunner/ui/spinner.cpp index 9e491f719c..32809e8740 100644 --- a/engines/bladerunner/ui/spinner.cpp +++ b/engines/bladerunner/ui/spinner.cpp @@ -74,14 +74,14 @@ int Spinner::chooseDestination(int loopId, bool immediately) { } else { _vm->playerLosesControl(); _vm->_scene->loopStartSpecial(kSceneLoopModeSpinner, loopId, immediately); - while (!_isOpen) { + while (_vm->_gameIsRunning && !_isOpen) { _vm->gameTick(); } _vm->playerGainsControl(); } - _vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack); - if (!_vqaPlayer->open("SPINNER.VQA")) { + _vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, "SPINNER.VQA"); + if (!_vqaPlayer->open()) { return 0; } @@ -159,7 +159,7 @@ int Spinner::chooseDestination(int loopId, bool immediately) { _selectedDestination = -1; do { _vm->gameTick(); - } while (_selectedDestination == -1); + } while (_vm->_gameIsRunning && _selectedDestination == -1); _imagePicker->deactivate(); diff --git a/engines/bladerunner/ui/vk.cpp b/engines/bladerunner/ui/vk.cpp index ec5013ca4c..ae776b7e55 100644 --- a/engines/bladerunner/ui/vk.cpp +++ b/engines/bladerunner/ui/vk.cpp @@ -87,8 +87,8 @@ void VK::open(int actorId, int calibrationRatio) { _shapes[i]->open("VK.SHP", i); } - _vqaPlayerMain = new VQAPlayer(_vm, &_vm->_surfaceBack); - if (!_vqaPlayerMain->open("VK.VQA")) { + _vqaPlayerMain = new VQAPlayer(_vm, &_vm->_surfaceBack, "VK.VQA"); + if (!_vqaPlayerMain->open()) { return; } @@ -114,8 +114,8 @@ void VK::open(int actorId, int calibrationRatio) { } _surfaceEye.create(172, 116, createRGB555()); - _vqaPlayerEye = new VQAPlayer(_vm, &_surfaceEye); - if (!_vqaPlayerEye->open(eyeVqa)) { + _vqaPlayerEye = new VQAPlayer(_vm, &_surfaceEye, eyeVqa); + if (!_vqaPlayerEye->open()) { return; } if (!_vqaPlayerEye->setLoop(0, -1, kLoopSetModeEnqueue, nullptr, nullptr)) { diff --git a/engines/bladerunner/vqa_player.cpp b/engines/bladerunner/vqa_player.cpp index 5f25a6387c..68a4e9491d 100644 --- a/engines/bladerunner/vqa_player.cpp +++ b/engines/bladerunner/vqa_player.cpp @@ -30,8 +30,8 @@ namespace BladeRunner { -bool VQAPlayer::open(const Common::String &name) { - _s = _vm->getResourceStream(name); +bool VQAPlayer::open() { + _s = _vm->getResourceStream(_name); if (!_s) { return false; } diff --git a/engines/bladerunner/vqa_player.h b/engines/bladerunner/vqa_player.h index b2c6ff2d4d..c4dd182697 100644 --- a/engines/bladerunner/vqa_player.h +++ b/engines/bladerunner/vqa_player.h @@ -49,6 +49,7 @@ class VQAPlayer { friend class Debugger; BladeRunnerEngine *_vm; + Common::String _name; Common::SeekableReadStream *_s; VQADecoder _decoder; Audio::QueuingAudioStream *_audioStream; @@ -77,8 +78,9 @@ class VQAPlayer { public: - VQAPlayer(BladeRunnerEngine *vm, Graphics::Surface *surface) + VQAPlayer(BladeRunnerEngine *vm, Graphics::Surface *surface, const Common::String &name) : _vm(vm), + _name(name), _s(nullptr), _surface(surface), _decoder(), @@ -103,7 +105,7 @@ public: close(); } - bool open(const Common::String &name); + bool open(); void close(); int update(bool forceDraw = false, bool advanceFrame = true, Graphics::Surface *customSurface = nullptr); |