From 543f7666f3577eb4cdfe7988873fa731b995d45b Mon Sep 17 00:00:00 2001 From: Ľubomír Remák Date: Thu, 30 Aug 2018 23:38:41 +0200 Subject: MUTATIONOFJB: Basic save/load support. Warning: The save format is subject to change. --- engines/mutationofjb/detection.cpp | 48 +++++++++++- engines/mutationofjb/game.cpp | 12 ++- engines/mutationofjb/game.h | 2 + engines/mutationofjb/gamedata.cpp | 144 ++++++++++++++++++++++++++++++---- engines/mutationofjb/gamedata.h | 114 +++++++++++++++++++++++---- engines/mutationofjb/inventory.cpp | 17 ++++ engines/mutationofjb/inventory.h | 7 +- engines/mutationofjb/mutationofjb.cpp | 75 ++++++++++++++++++ engines/mutationofjb/mutationofjb.h | 13 +++ engines/mutationofjb/script.cpp | 4 + engines/mutationofjb/script.h | 1 + 11 files changed, 400 insertions(+), 37 deletions(-) (limited to 'engines') diff --git a/engines/mutationofjb/detection.cpp b/engines/mutationofjb/detection.cpp index 33664c1f8e..3ec82e83bc 100644 --- a/engines/mutationofjb/detection.cpp +++ b/engines/mutationofjb/detection.cpp @@ -23,6 +23,9 @@ #include "mutationofjb/mutationofjb.h" #include "common/config-manager.h" +#include "common/system.h" +#include "common/savefile.h" +#include "common/serializer.h" #include "engines/advancedDetector.h" @@ -89,20 +92,59 @@ public: _directoryGlobs = mutationofjbDirectoryGlobs; } - virtual const char *getName() const { + virtual const char *getName() const override { return "Mutation of J.B."; } - virtual const char *getOriginalCopyright() const { + virtual const char *getOriginalCopyright() const override { return "Mutation of J.B. (C) 1996 RIKI Computer Games"; } - virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override { if (desc) { *engine = new MutationOfJB::MutationOfJBEngine(syst); } return desc != nullptr; } + + virtual bool hasFeature(MetaEngineFeature f) const override { + if (f == kSupportsListSaves || f == kSimpleSavesNames) { + return true; + } + + return false; + } + + virtual int getMaximumSaveSlot() const override { + return 999; + } + + virtual SaveStateList listSaves(const char *target) const override { + Common::SaveFileManager *const saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String pattern = target; + pattern += ".###"; + + filenames = saveFileMan->listSavefiles(pattern); + + SaveStateList saveList; + int slotNo = 0; + for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + slotNo = atoi(file->c_str() + file->size() - 3); + + Common::InSaveFile *const in = saveFileMan->openForLoading(*file); + if (in) { + Common::Serializer sz(in, nullptr); + + MutationOfJB::SaveHeader saveHdr; + if (saveHdr.sync(sz)) { + saveList.push_back(SaveStateDescriptor(slotNo, saveHdr._description)); + } + } + } + return saveList; + } }; #if PLUGIN_ENABLED_DYNAMIC(MUTATIONOFJB) diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp index 27def7dd1f..ff05bb99eb 100644 --- a/engines/mutationofjb/game.cpp +++ b/engines/mutationofjb/game.cpp @@ -92,7 +92,7 @@ bool Game::loadGameData(bool partB) { return false; } - _gameData->loadFromStream(file); + _gameData->loadInitialState(file); file.close(); @@ -255,4 +255,14 @@ void Game::setActiveSayTask(const TaskPtr &sayTask) { _activeSayTask = sayTask; } +bool Game::loadSaveAllowed() const { + if (_scriptExecCtx.isCommandRunning()) + return false; + + if (isCurrentSceneMap()) + return false; + + return true; +} + } diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h index 9ab52ef5b4..6230b93aff 100644 --- a/engines/mutationofjb/game.h +++ b/engines/mutationofjb/game.h @@ -81,6 +81,8 @@ public: TaskPtr getActiveSayTask() const; void setActiveSayTask(const TaskPtr &sayTask); + bool loadSaveAllowed() const; + private: bool loadGameData(bool partB); void runActiveCommand(); diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp index a669fcffbb..71f58c317b 100644 --- a/engines/mutationofjb/gamedata.cpp +++ b/engines/mutationofjb/gamedata.cpp @@ -21,12 +21,14 @@ */ #include "mutationofjb/gamedata.h" + +#include "common/serializer.h" #include "common/stream.h" #include "common/util.h" namespace MutationOfJB { -static bool readString(Common::ReadStream &stream, char *str) { +static bool readEntityNameString(Common::ReadStream &stream, char *str) { char buf[MAX_ENTITY_NAME_LENGTH]; memset(str, 0, MAX_ENTITY_NAME_LENGTH + 1); @@ -39,12 +41,24 @@ static bool readString(Common::ReadStream &stream, char *str) { return true; } +static void syncEntityNameString(char *cstr, Common::Serializer &sz) { + if (sz.isLoading()) { + Common::String str; + sz.syncString(str); + strncpy(cstr, str.c_str(), MAX_ENTITY_NAME_LENGTH); + cstr[MAX_ENTITY_NAME_LENGTH] = 0; + } else { + Common::String str(cstr); + sz.syncString(str); + } +} + bool Door::isActive() { return *_name != '\0'; } -bool Door::loadFromStream(Common::ReadStream &stream) { - readString(stream, _name); +bool Door::loadInitialState(Common::ReadStream &stream) { + readEntityNameString(stream, _name); _destSceneId = stream.readByte(); _destX = stream.readUint16LE(); @@ -60,7 +74,21 @@ bool Door::loadFromStream(Common::ReadStream &stream) { return true; } -bool Object::loadFromStream(Common::ReadStream &stream) { +void Door::saveLoadWithSerializer(Common::Serializer &sz) { + syncEntityNameString(_name, sz); + sz.syncAsByte(_destSceneId); + sz.syncAsUint16LE(_destX); + sz.syncAsUint16LE(_destY); + sz.syncAsUint16LE(_x); + sz.syncAsByte(_y); + sz.syncAsUint16LE(_width); + sz.syncAsByte(_height); + sz.syncAsUint16LE(_walkToX); + sz.syncAsByte(_walkToY); + sz.syncAsByte(_SP); +} + +bool Object::loadInitialState(Common::ReadStream &stream) { _active = stream.readByte(); _firstFrame = stream.readByte(); _randomFrame = stream.readByte(); @@ -79,9 +107,26 @@ bool Object::loadFromStream(Common::ReadStream &stream) { return true; } -bool Static::loadFromStream(Common::ReadStream &stream) { +void Object::saveLoadWithSerializer(Common::Serializer &sz) { + sz.syncAsByte(_active); + sz.syncAsByte(_firstFrame); + sz.syncAsByte(_randomFrame); + sz.syncAsByte(_numFrames); + sz.syncAsByte(_roomFrameLSB); + sz.syncAsByte(_jumpChance); + sz.syncAsByte(_currentFrame); + sz.syncAsUint16LE(_x); + sz.syncAsByte(_y); + sz.syncAsUint16LE(_width); + sz.syncAsByte(_height); + sz.syncAsUint16LE(_WX); + sz.syncAsByte(_roomFrameMSB); + sz.syncAsByte(_SP); +} + +bool Static::loadInitialState(Common::ReadStream &stream) { _active = stream.readByte(); - readString(stream, _name); + readEntityNameString(stream, _name); _x = stream.readUint16LE(); _y = stream.readByte(); _width = stream.readUint16LE(); @@ -93,7 +138,19 @@ bool Static::loadFromStream(Common::ReadStream &stream) { return true; } -bool Bitmap::loadFromStream(Common::ReadStream &stream) { +void Static::saveLoadWithSerializer(Common::Serializer &sz) { + sz.syncAsByte(_active); + syncEntityNameString(_name, sz); + sz.syncAsUint16LE(_x); + sz.syncAsByte(_y); + sz.syncAsUint16LE(_width); + sz.syncAsByte(_height); + sz.syncAsUint16LE(_walkToX); + sz.syncAsByte(_walkToY); + sz.syncAsByte(_walkToFrame); +} + +bool Bitmap::loadInitialState(Common::ReadStream &stream) { _roomFrame = stream.readByte(); _isVisible = stream.readByte(); _x1 = stream.readUint16LE(); @@ -104,7 +161,16 @@ bool Bitmap::loadFromStream(Common::ReadStream &stream) { return true; } -bool Scene::loadFromStream(Common::ReadStream &stream) { +void Bitmap::saveLoadWithSerializer(Common::Serializer &sz) { + sz.syncAsByte(_roomFrame); + sz.syncAsByte(_isVisible); + sz.syncAsUint16LE(_x1); + sz.syncAsByte(_y1); + sz.syncAsUint16LE(_x2); + sz.syncAsByte(_y2); +} + +bool Scene::loadInitialState(Common::ReadStream &stream) { int i; _startup = stream.readByte(); @@ -116,23 +182,23 @@ bool Scene::loadFromStream(Common::ReadStream &stream) { _noDoors = stream.readByte(); _noDoors = MIN(_noDoors, static_cast(ARRAYSIZE(_doors))); for (i = 0; i < ARRAYSIZE(_doors); ++i) { - _doors[i].loadFromStream(stream); + _doors[i].loadInitialState(stream); } _noObjects = stream.readByte(); _noObjects = MIN(_noObjects, static_cast(ARRAYSIZE(_objects))); for (i = 0; i < ARRAYSIZE(_objects); ++i) { - _objects[i].loadFromStream(stream); + _objects[i].loadInitialState(stream); } _noStatics = stream.readByte(); _noStatics = MIN(_noStatics, static_cast(ARRAYSIZE(_statics))); for (i = 0; i < ARRAYSIZE(_statics); ++i) { - _statics[i].loadFromStream(stream); + _statics[i].loadInitialState(stream); } for (i = 0; i < ARRAYSIZE(_bitmaps); ++i) { - _bitmaps[i].loadFromStream(stream); + _bitmaps[i].loadInitialState(stream); } _obstacleY1 = stream.readUint16LE(); @@ -141,13 +207,50 @@ bool Scene::loadFromStream(Common::ReadStream &stream) { _palRotDelay = stream.readByte(); _exhaustedConvItemNext = stream.readByte(); - for (i = 0; i < 79; ++i) { + for (i = 0; i < ARRAYSIZE(_exhaustedConvItems); ++i) { _exhaustedConvItems[i]._encodedData = stream.readByte(); } return true; } +void Scene::saveLoadWithSerializer(Common::Serializer &sz) { + sz.syncAsByte(_startup); + sz.syncAsByte(_unknown001); + sz.syncAsByte(_unknown002); + sz.syncAsByte(_unknown003); + sz.syncAsByte(_delay); + + sz.syncAsByte(_noDoors); + for (int i = 0; i < ARRAYSIZE(_doors); ++i) { + _doors[i].saveLoadWithSerializer(sz); + } + + sz.syncAsByte(_noObjects); + for (int i = 0; i < ARRAYSIZE(_objects); ++i) { + _objects[i].saveLoadWithSerializer(sz); + } + + sz.syncAsByte(_noStatics); + for (int i = 0; i < ARRAYSIZE(_statics); ++i) { + _statics[i].saveLoadWithSerializer(sz); + } + + for (int i = 0; i < ARRAYSIZE(_bitmaps); ++i) { + _bitmaps[i].saveLoadWithSerializer(sz); + } + + sz.syncAsUint16LE(_obstacleY1); + sz.syncAsByte(_palRotFirst); + sz.syncAsByte(_palRotLast); + sz.syncAsByte(_palRotDelay); + sz.syncAsByte(_exhaustedConvItemNext); + + for (int i = 0; i < ARRAYSIZE(_exhaustedConvItems); ++i) { + sz.syncAsByte(_exhaustedConvItems[i]._encodedData); + } +} + Door *Scene::getDoor(uint8 doorId) { if (doorId == 0 || doorId > _noDoors) { warning("Door %d does not exist", doorId); @@ -270,12 +373,23 @@ Inventory &GameData::getInventory() { return _inventory; } -bool GameData::loadFromStream(Common::ReadStream &stream) { +bool GameData::loadInitialState(Common::ReadStream &stream) { for (int i = 0; i < ARRAYSIZE(_scenes); ++i) { - _scenes[i].loadFromStream(stream); + _scenes[i].loadInitialState(stream); } return true; } +void GameData::saveLoadWithSerializer(Common::Serializer &sz) { + for (int i = 0; i < ARRAYSIZE(_scenes); ++i) { + _scenes[i].saveLoadWithSerializer(sz); + } + + sz.syncAsByte(_currentScene); + sz.syncAsByte(_partB); + _inventory.saveLoadWithSerializer(sz); + sz.syncString(_currentAPK); +} + } diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h index 5d7dea1b05..3ba859b28e 100644 --- a/engines/mutationofjb/gamedata.h +++ b/engines/mutationofjb/gamedata.h @@ -23,9 +23,11 @@ #ifndef MUTATIONOFJB_GAMEDATA_H #define MUTATIONOFJB_GAMEDATA_H -#include "common/scummsys.h" #include "mutationofjb/inventory.h" +#include "common/serializer.h" +#include "common/scummsys.h" + namespace Common { class ReadStream; } @@ -36,7 +38,7 @@ enum { MAX_ENTITY_NAME_LENGTH = 0x14 }; -/** @file gamedata.h +/** @file * There are 4 types of entities present in the game data: * - Door * - Object @@ -47,7 +49,9 @@ enum { /** * An interactable scene changer with no visual representation. */ -struct Door { +struct Door : public Common::Serializable { + virtual ~Door() {} + /** * Door name (NM register). * @@ -80,7 +84,7 @@ struct Door { uint16 _walkToX; /** Y coordinate for position player will walk towards after clicking the door (WY register). */ uint8 _walkToY; - /* Unknown for now - likely not even used. */ + /** Unknown for now - likely not even used. */ uint8 _SP; /** @@ -89,7 +93,20 @@ struct Door { */ bool isActive(); - bool loadFromStream(Common::ReadStream &stream); + /** + * Load initial state from game data file. + * + * @param stream Stream for reading. + * @return True if success, false otherwise. + */ + bool loadInitialState(Common::ReadStream &stream); + + /** + * (De)serialization for save/load. + * + * @param sz Serializer. + */ + virtual void saveLoadWithSerializer(Common::Serializer &sz) override; }; /** @@ -105,7 +122,7 @@ struct Door { * * For details regarding animation playback, see objectanimationtask.cpp. */ -struct Object { +struct Object : public Common::Serializable { /** Controls whether the animation is playing. */ uint8 _active; /** @@ -163,16 +180,29 @@ struct Object { * @see _roomFrameLSB */ uint8 _roomFrameMSB; - /* Unknown. TODO: Figure out what this does. */ + /** Unknown. TODO: Figure out what this does. */ uint8 _SP; - bool loadFromStream(Common::ReadStream &stream); + /** + * Load initial state from game data file. + * + * @param stream Stream for reading. + * @return True if success, false otherwise. + */ + bool loadInitialState(Common::ReadStream &stream); + + /** + * (De)serialization for save/load. + * + * @param sz Serializer. + */ + virtual void saveLoadWithSerializer(Common::Serializer &sz) override; }; /** * An interactable area, usually without a visual representation. */ -struct Static { +struct Static : public Common::Serializable { /** Whether you can mouse over and interact with the static (AC register). */ uint8 _active; /** @@ -206,14 +236,27 @@ struct Static { /** Player frame (rotation) set after the player finishes walking towards the walk to position (SP register). */ uint8 _walkToFrame; - bool loadFromStream(Common::ReadStream &stream); + /** + * Load initial state from game data file. + * + * @param stream Stream for reading. + * @return True if success, false otherwise. + */ + bool loadInitialState(Common::ReadStream &stream); + + /** + * (De)serialization for save/load. + * + * @param sz Serializer. + */ + virtual void saveLoadWithSerializer(Common::Serializer &sz) override; }; /** * A static image that is carved out of a room frame based on its rectangle. * The bitmap rectangle also specifies where to blit it on the screen. */ -struct Bitmap { +struct Bitmap : public Common::Serializable { /** Room frame that this bitmap carves out of. */ uint8 _roomFrame; /** Whether to draw the bitmap. */ @@ -227,7 +270,20 @@ struct Bitmap { /** Y coordinate of the bottom right corner of the bitmap rectangle. */ uint8 _y2; - bool loadFromStream(Common::ReadStream &stream); + /** + * Load initial state from game data file. + * + * @param stream Stream for reading. + * @return True if success, false otherwise. + */ + bool loadInitialState(Common::ReadStream &stream); + + /** + * (De)serialization for save/load. + * + * @param sz Serializer. + */ + virtual void saveLoadWithSerializer(Common::Serializer &sz) override; }; /** @@ -256,7 +312,7 @@ struct ExhaustedConvItem { _encodedData(((context & 0x1) << 7) | ((convItemIndex & 0x7) << 4) | (convGroupIndex & 0xF)) {} }; -struct Scene { +struct Scene : Common::Serializable { Door *getDoor(uint8 objectId); Object *getObject(uint8 objectId, bool ignoreNo = false); Static *getStatic(uint8 staticId, bool ignoreNo = false); @@ -328,7 +384,20 @@ struct Scene { uint8 _exhaustedConvItemNext; ExhaustedConvItem _exhaustedConvItems[79]; - bool loadFromStream(Common::ReadStream &stream); + /** + * Load initial state from game data file. + * + * @param stream Stream for reading. + * @return True if success, false otherwise. + */ + bool loadInitialState(Common::ReadStream &stream); + + /** + * (De)serialization for save/load. + * + * @param sz Serializer. + */ + virtual void saveLoadWithSerializer(Common::Serializer &sz) override; }; struct ConversationInfo { @@ -346,14 +415,27 @@ struct ConversationInfo { uint8 _color; }; -struct GameData { +struct GameData : public Common::Serializable { public: GameData(); Scene *getScene(uint8 sceneId); Scene *getCurrentScene(); Inventory &getInventory(); - bool loadFromStream(Common::ReadStream &stream); + /** + * Load initial state from game data file. + * + * @param stream Stream for reading. + * @return True if success, false otherwise. + */ + bool loadInitialState(Common::ReadStream &stream); + + /** + * (De)serialization for save/load. + * + * @param sz Serializer. + */ + virtual void saveLoadWithSerializer(Common::Serializer &sz) override; uint8 _currentScene; // Persistent. uint8 _lastScene; diff --git a/engines/mutationofjb/inventory.cpp b/engines/mutationofjb/inventory.cpp index 2d7ba285f2..91bfd9f005 100644 --- a/engines/mutationofjb/inventory.cpp +++ b/engines/mutationofjb/inventory.cpp @@ -137,4 +137,21 @@ void Inventory::reverseItems(uint from, uint to) { } } +void Inventory::saveLoadWithSerializer(Common::Serializer &sz) { + if (sz.isLoading()) { + uint32 length = 0; + sz.syncAsUint32LE(length); + if (length) { + _items.resize(length); + } + } else { + uint32 length = static_cast(_items.size()); + sz.syncAsUint32LE(length); + } + + for (Items::size_type i = 0; i < _items.size(); ++i) { + sz.syncString(_items[i]); + } +} + } diff --git a/engines/mutationofjb/inventory.h b/engines/mutationofjb/inventory.h index d75ef5a674..2f9428add7 100644 --- a/engines/mutationofjb/inventory.h +++ b/engines/mutationofjb/inventory.h @@ -23,8 +23,9 @@ #ifndef MUTATIONOFJB_INVENTORY_H #define MUTATIONOFJB_INVENTORY_H -#include "common/scummsys.h" #include "common/array.h" +#include "common/serializer.h" +#include "common/scummsys.h" #include "common/str.h" namespace MutationOfJB { @@ -37,7 +38,7 @@ public: virtual ~InventoryObserver() {} }; -class Inventory { +class Inventory : public Common::Serializable { public: enum { VISIBLE_ITEMS = 6 @@ -59,6 +60,8 @@ public: void setObserver(InventoryObserver *observer); + virtual void saveLoadWithSerializer(Common::Serializer &sz) override; + private: void rotateItemsRight(uint n); void rotateItemsLeft(uint n); diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp index 4e004f0868..1537646f9e 100644 --- a/engines/mutationofjb/mutationofjb.cpp +++ b/engines/mutationofjb/mutationofjb.cpp @@ -28,6 +28,7 @@ #include "common/system.h" #include "common/events.h" #include "common/fs.h" +#include "common/savefile.h" #include "graphics/screen.h" #include "graphics/cursorman.h" @@ -125,6 +126,55 @@ void MutationOfJBEngine::updateCursor() { } } +bool MutationOfJBEngine::hasFeature(Engine::EngineFeature f) const { + if (f == kSupportsLoadingDuringRuntime || f == kSupportsSavingDuringRuntime) { + return true; + } + + return false; +} + +bool MutationOfJBEngine::canLoadGameStateCurrently() { + return _game->loadSaveAllowed(); +} + +Common::Error MutationOfJBEngine::loadGameState(int slot) { + const Common::String saveName = Common::String::format("%s.%03d", _targetName.c_str(), slot); + Common::InSaveFile *const saveFile = g_system->getSavefileManager()->openForLoading(saveName); + + Common::Serializer sz(saveFile, nullptr); + + SaveHeader saveHdr; + saveHdr.sync(sz); + _game->getGameData().saveLoadWithSerializer(sz); + delete saveFile; + + _game->changeScene(_game->getGameData()._currentScene, _game->getGameData()._partB); + _game->getGui().markDirty(); + + return Common::kNoError; +} + +bool MutationOfJBEngine::canSaveGameStateCurrently() { + return _game->loadSaveAllowed(); +} + +Common::Error MutationOfJBEngine::saveGameState(int slot, const Common::String &desc) { + const Common::String saveName = Common::String::format("%s.%03d", _targetName.c_str(), slot); + Common::OutSaveFile *const saveFile = g_system->getSavefileManager()->openForSaving(saveName); + + Common::Serializer sz(nullptr, saveFile); + + SaveHeader saveHdr; + saveHdr._description = desc; + saveHdr.sync(sz); + _game->getGameData().saveLoadWithSerializer(sz); + saveFile->finalize(); + delete saveFile; + + return Common::kNoError; +} + void MutationOfJBEngine::handleNormalScene(const Common::Event &event) { Scene *const scene = _game->getGameData().getCurrentScene(); @@ -255,6 +305,9 @@ Common::Error MutationOfJBEngine::run() { event.kbd.ascii == '~' || event.kbd.ascii == '#') { _console->attach(); } + if (event.kbd.keycode == Common::KEYCODE_F5 && event.kbd.hasFlags(0)) { + openMainMenuDialog(); + } break; } case Common::EVENT_KEYUP: { @@ -297,4 +350,26 @@ Common::Error MutationOfJBEngine::run() { return Common::kNoError; } +bool SaveHeader::sync(Common::Serializer &sz) { + const uint32 SAVE_MAGIC_NUMBER = MKTAG('M', 'O', 'J', 'B'); + const uint32 SAVE_FILE_VERSION = 1; + + if (sz.isLoading()) { + uint32 magic = 0; + sz.syncAsUint32BE(magic); + if (magic != SAVE_MAGIC_NUMBER) { + warning("Invalid save"); + return false; + } + } else { + uint32 magic = SAVE_MAGIC_NUMBER; + sz.syncAsUint32BE(magic); + } + + sz.syncVersion(SAVE_FILE_VERSION); + sz.syncString(_description); + + return true; +} + } diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h index 380325487c..3430f13051 100644 --- a/engines/mutationofjb/mutationofjb.h +++ b/engines/mutationofjb/mutationofjb.h @@ -28,6 +28,7 @@ namespace Common { struct Event; +class Serializer; } namespace Graphics { @@ -39,6 +40,12 @@ namespace MutationOfJB { class Console; class Game; +struct SaveHeader { + bool sync(Common::Serializer &sz); + + Common::String _description; +}; + class MutationOfJBEngine : public Engine { public: enum CursorState { @@ -56,6 +63,12 @@ public: void setCursorState(CursorState cursorState); void updateCursor(); + virtual bool hasFeature(EngineFeature f) const override; + virtual bool canLoadGameStateCurrently() override; + virtual Common::Error loadGameState(int slot) override; + virtual bool canSaveGameStateCurrently() override; + virtual Common::Error saveGameState(int slot, const Common::String &desc) override; + private: bool loadGameData(bool partB); void setupCursor(); diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp index 7538017199..85574dacb5 100644 --- a/engines/mutationofjb/script.cpp +++ b/engines/mutationofjb/script.cpp @@ -207,6 +207,10 @@ Command *ScriptExecutionContext::getExtra(const Common::String &name) const { return cmd; } +bool ScriptExecutionContext::isCommandRunning() const { + return _activeCommand; +} + bool Script::loadFromStream(Common::SeekableReadStream &stream) { destroy(); diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h index 86f2450ce6..d168fdaa24 100644 --- a/engines/mutationofjb/script.h +++ b/engines/mutationofjb/script.h @@ -119,6 +119,7 @@ public: GameData &getGameData(); Command *getMacro(const Common::String &name) const; Command *getExtra(const Common::String &name) const; + bool isCommandRunning() const; private: Game &_game; -- cgit v1.2.3