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;