From 006aaa2c43c1917dedcab3cbb32e03e624b63830 Mon Sep 17 00:00:00 2001 From: David Fioramonti Date: Wed, 23 May 2018 06:10:45 -0700 Subject: [PATCH] MYST3: Add Autosave Support This adds autosaving to a save named Autosave. Autosaving is enabled if the autosave file doesn't exist or is an autosave. The actual autosaving is done on exit or based on the save interval specified in the config file. --- engines/myst3/menu.cpp | 32 ++++++++++++++++++++-------- engines/myst3/menu.h | 8 +++++++ engines/myst3/myst3.cpp | 46 ++++++++++++++++++++++++++++++++++++++++- engines/myst3/myst3.h | 5 +++++ engines/myst3/state.cpp | 20 +++++++++++++++++- engines/myst3/state.h | 12 +++++++++-- 6 files changed, 110 insertions(+), 13 deletions(-) diff --git a/engines/myst3/menu.cpp b/engines/myst3/menu.cpp index b76e0306abc..f01b5dcdd6b 100644 --- a/engines/myst3/menu.cpp +++ b/engines/myst3/menu.cpp @@ -209,7 +209,8 @@ int16 GamepadDialog::update() { Menu::Menu(Myst3Engine *vm) : _vm(vm), - _saveLoadSpotItem(0) { + _saveLoadSpotItem(0), + _thumbnailValid(false) { } Menu::~Menu() { @@ -295,6 +296,23 @@ void Menu::updateMainMenu(uint16 action) { } } +void Menu::saveThumbnail() { + // ... and capture the screen + Graphics::Surface *big = _vm->_gfx->getScreenshot(); + Graphics::Surface *thumb = createThumbnail(big); + _vm->_state->setSaveThumbnail(thumb); + big->free(); + delete big; +} + +bool Menu::getThumbnailValid() { + return _thumbnailValid; +} + +void Menu::setThumbnailValid(bool valid) { + _thumbnailValid = valid; +} + void Menu::goToNode(uint16 node) { if (_vm->_state->getMenuSavedAge() == 0 && _vm->_state->getLocationRoom() != 901) { // Entering menu, save current location ... @@ -302,12 +320,8 @@ void Menu::goToNode(uint16 node) { _vm->_state->setMenuSavedRoom(_vm->_state->getLocationRoom()); _vm->_state->setMenuSavedNode(_vm->_state->getLocationNode()); - // ... and capture the screen - Graphics::Surface *big = _vm->_gfx->getScreenshot(); - Graphics::Surface *thumb = createThumbnail(big); - _vm->_state->setSaveThumbnail(thumb); - big->free(); - delete big; + saveThumbnail(); + _thumbnailValid = true; // Reset some sound variables if (_vm->_state->getLocationAge() == 6 && _vm->_state->getSoundEdannaUnk587() == 1 && _vm->_state->getSoundEdannaUnk1031()) { @@ -614,7 +628,7 @@ void PagingMenu::saveMenuSave() { // Save the state and the thumbnail Common::OutSaveFile *save = _vm->getSaveFileManager()->openForSaving(fileName); _vm->_state->setSaveDescription(_saveName); - _vm->_state->save(save); + _vm->_state->save(save,false); delete save; // Do next action @@ -953,7 +967,7 @@ void AlbumMenu::saveMenuSave() { // Save the state and the thumbnail Common::OutSaveFile *save = _vm->getSaveFileManager()->openForSaving(fileName); _vm->_state->setSaveDescription(saveName); - _vm->_state->save(save); + _vm->_state->save(save,false); delete save; // Do next action diff --git a/engines/myst3/menu.h b/engines/myst3/menu.h index 75324e1d0a8..cf25197366f 100644 --- a/engines/myst3/menu.h +++ b/engines/myst3/menu.h @@ -63,6 +63,12 @@ public: void updateMainMenu(uint16 action); void goToNode(uint16 node); + /* + * Grab a screenshot save it to state and free memory + */ + void saveThumbnail(); + bool getThumbnailValid(); + void setThumbnailValid(bool value); virtual void saveLoadAction(uint16 action, uint16 item) = 0; virtual void setSaveLoadSpotItem(uint16 id, SpotItemFace *spotItem); @@ -73,6 +79,8 @@ protected: SpotItemFace *_saveLoadSpotItem; Common::String _saveLoadAgeName; + bool _thumbnailValid; + uint dialogIdFromType(DialogType type); uint16 dialogConfirmValue(); uint16 dialogSaveValue(); diff --git a/engines/myst3/myst3.cpp b/engines/myst3/myst3.cpp index 6f27a3eb56f..0e2572c0f9b 100644 --- a/engines/myst3/myst3.cpp +++ b/engines/myst3/myst3.cpp @@ -71,7 +71,7 @@ Myst3Engine::Myst3Engine(OSystem *syst, const Myst3GameDescription *version) : _inputSpacePressed(false), _inputEnterPressed(false), _inputEscapePressed(false), _inputTildePressed(false), _inputEscapePressedNotConsumed(false), - _interactive(false), + _interactive(false), _lastSaveTime(0), _menuAction(0), _projectorBackground(0), _shakeEffect(0), _rotationEffect(0), _backgroundSoundScriptLastRoomId(0), _transition(0), _frameLimiter(0), _inventoryManualHide(false) { @@ -210,6 +210,9 @@ Common::Error Myst3Engine::run() { drawFrame(); } + if (!_menu->getThumbnailValid()) + _menu->saveThumbnail(); // Update thumbnail before saving + tryAutoSaving(); //Attempt to autosave before exiting unloadNode(); _archiveNode->close(); @@ -431,6 +434,7 @@ void Myst3Engine::processInput(bool interactive) { } bool shouldInteractWithHoveredElement = false; + _menu->setThumbnailValid(false); // Process events Common::Event event; @@ -538,6 +542,11 @@ void Myst3Engine::processInput(bool interactive) { interactWithHoveredElement(); } + if (shouldPerformAutoSave(_lastSaveTime)) { + _menu->saveThumbnail(); // Update thumbnail before saving + tryAutoSaving(); + } + // Open main menu // This is not checked directly in the event handling code // because menu open requests done while in lookOnly mode @@ -1479,6 +1488,14 @@ void Myst3Engine::dragItem(uint16 statusVar, uint16 movie, uint16 frame, uint16 } } +bool Myst3Engine::canSaveGameStateCurrently() { + return canLoadGameStateCurrently() && !(_state->getLocationRoom() == 901 && _state->getMenuSavedAge()==0); +} + +bool Myst3Engine::canSaveCurrently() { + return canLoadGameStateCurrently() && !(_state->getLocationRoom() == 901 && _state->getMenuSavedAge()==0); +} + bool Myst3Engine::canLoadGameStateCurrently() { // Loading from the GMM is only possible when the game is interactive // This is to prevent loading from inner loops. Loading while @@ -1487,6 +1504,33 @@ bool Myst3Engine::canLoadGameStateCurrently() { return _interactive; } +void Myst3Engine::tryAutoSaving() { + if (!canSaveGameStateCurrently()) { + return; // Can't save right now, try again on the next frame + } + + _lastSaveTime = _system->getMillis(); + + Common::String saveName = "Autosave"; + + Common::String fileName = Saves::buildName(saveName.c_str(),getPlatform()); + Common::ScopedPtr saveFile(getSaveFileManager()->openForLoading(fileName)); + + if (!_state->isAutoSaveAllowed(saveFile.get())) { + return; // Can't autosave ever, try again after the next autosave delay + } + + // Save the state and the thumbnail + Common::OutSaveFile *save = getSaveFileManager()->openForSaving(fileName); + const Common::String prevSaveName = _state->getSaveDescription(); + _state->setSaveDescription(saveName); + + if (!_state->save(save,true)) + warning("Attempt to autosave has failed."); + _state->setSaveDescription(prevSaveName); + delete save; +} + Common::Error Myst3Engine::loadGameState(int slot) { Common::StringArray filenames = Saves::list(_saveFileMan, getPlatform()); return loadGameState(filenames[slot], kTransitionNone); diff --git a/engines/myst3/myst3.h b/engines/myst3/myst3.h index 296f8aecb92..efe2ee2f930 100644 --- a/engines/myst3/myst3.h +++ b/engines/myst3/myst3.h @@ -119,7 +119,10 @@ public: uint32 getGameLocalizationType() const; bool isWideScreenModEnabled() const; + bool canSaveGameStateCurrently() override; // Determines autosave saveability bool canLoadGameStateCurrently() override; + bool canSaveCurrently() override; // Determines GMM saveability + void tryAutoSaving(); Common::Error loadGameState(int slot) override; Common::Error loadGameState(Common::String fileName, TransitionType transition); @@ -219,6 +222,8 @@ private: bool _interactive; + uint32 _lastSaveTime; + uint32 _backgroundSoundScriptLastRoomId; /** diff --git a/engines/myst3/state.cpp b/engines/myst3/state.cpp index 5d9f736927e..905fc89f159 100644 --- a/engines/myst3/state.cpp +++ b/engines/myst3/state.cpp @@ -25,6 +25,7 @@ #include "engines/myst3/gfx.h" #include "common/debug-channels.h" +#include "common/ptr.h" #include "common/savefile.h" #include "graphics/surface.h" @@ -77,6 +78,7 @@ GameState::StateData::StateData() { saveYear = 0; saveHour = 0; saveMinute = 0; + autoSave = false; } GameState::GameState(const Common::Platform platform, Database *database): @@ -472,6 +474,7 @@ void GameState::StateData::syncWithSaveGame(Common::Serializer &s) { s.syncAsUint16LE(saveYear, 149); s.syncAsByte(saveHour, 149); s.syncAsByte(saveMinute, 149); + s.syncAsByte(autoSave, 150); s.syncString(saveDescription, 149); #ifdef SCUMM_BIG_ENDIAN @@ -531,7 +534,21 @@ bool GameState::load(Common::InSaveFile *saveFile) { return true; } -bool GameState::save(Common::OutSaveFile *saveFile) { +bool GameState::isAutoSaveAllowed(Common::InSaveFile *saveFile) { + // Check if file exists + if (!saveFile) { // The autosave file doesn't exist or is corrupt + return true; + } + + // Get autoSave value from saved file + StateData data; + Common::Serializer s = Common::Serializer(saveFile, 0); + data.syncWithSaveGame(s); + + return data.autoSave; // The autosave file exists and is either an autosave or not +} + +bool GameState::save(Common::OutSaveFile *saveFile, bool autosave) { Common::Serializer s = Common::Serializer(0, saveFile); // Update save creation info @@ -542,6 +559,7 @@ bool GameState::save(Common::OutSaveFile *saveFile) { _data.saveDay = t.tm_mday; _data.saveHour = t.tm_hour; _data.saveMinute = t.tm_min; + _data.autoSave = autosave; _data.gameRunning = false; _data.syncWithSaveGame(s); diff --git a/engines/myst3/state.h b/engines/myst3/state.h index 8b4811a6adb..e7b4ea3ab31 100644 --- a/engines/myst3/state.h +++ b/engines/myst3/state.h @@ -53,7 +53,13 @@ public: void newGame(); bool load(Common::InSaveFile *saveFile); - bool save(Common::OutSaveFile *saveFile); + bool save(Common::OutSaveFile *saveFile, bool autosave); + + /* + * Autosaving will be enabled if The autosave file is an autosave or if The autosave file doesn't exist + * The autosave file name is version dependent (PC vs. Xbox) + */ + bool isAutoSaveAllowed(Common::InSaveFile *saveFile); int32 getVar(uint16 var); void setVar(uint16 var, int32 value); @@ -338,6 +344,7 @@ public: void setSaveThumbnail(Graphics::Surface *thumb); Common::String formatSaveTime(); void setSaveDescription(const Common::String &description) { _data.saveDescription = description; } + const Common::String getSaveDescription() { return _data.saveDescription; } Common::Array getInventory(); void updateInventory(const Common::Array &items); @@ -379,6 +386,7 @@ public: uint8 saveHour; uint8 saveMinute; + bool autoSave; Common::String saveDescription; @@ -397,7 +405,7 @@ private: const Common::Platform _platform; Database *_db; - static const uint32 kSaveVersion = 149; + static const uint32 kSaveVersion = 150; StateData _data;