[Scummvm-git-logs] scummvm master -> e301e69e575d29bda7e44cc0ed77bcbbb799f28a

elasota noreply at scummvm.org
Sun Jul 3 18:08:15 UTC 2022


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
e301e69e57 MTROPOLIS: Add auto-saves for Obsidian


Commit: e301e69e575d29bda7e44cc0ed77bcbbb799f28a
    https://github.com/scummvm/scummvm/commit/e301e69e575d29bda7e44cc0ed77bcbbb799f28a
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-03T14:06:57-04:00

Commit Message:
MTROPOLIS: Add auto-saves for Obsidian

Changed paths:
    engines/mtropolis/hacks.cpp
    engines/mtropolis/hacks.h
    engines/mtropolis/modifiers.cpp
    engines/mtropolis/mtropolis.cpp
    engines/mtropolis/mtropolis.h
    engines/mtropolis/runtime.cpp
    engines/mtropolis/runtime.h
    engines/mtropolis/saveload.cpp
    engines/mtropolis/saveload.h


diff --git a/engines/mtropolis/hacks.cpp b/engines/mtropolis/hacks.cpp
index a9e63161532..2c85b351448 100644
--- a/engines/mtropolis/hacks.cpp
+++ b/engines/mtropolis/hacks.cpp
@@ -20,11 +20,14 @@
  */
 
 #include "common/system.h"
+#include "common/hashmap.h"
 
 #include "mtropolis/assets.h"
 #include "mtropolis/detection.h"
 #include "mtropolis/hacks.h"
 #include "mtropolis/runtime.h"
+#include "mtropolis/modifiers.h"
+#include "mtropolis/saveload.h"
 
 namespace MTropolis {
 
@@ -47,6 +50,14 @@ void Hacks::addAssetHooks(const Common::SharedPtr<AssetHooks> &hooks) {
 	assetHooks.push_back(hooks);
 }
 
+void Hacks::addSceneTransitionHooks(const Common::SharedPtr<SceneTransitionHooks> &hooks) {
+	sceneTransitionHooks.push_back(hooks);
+}
+
+void Hacks::addSaveLoadHooks(const Common::SharedPtr<SaveLoadHooks> &hooks) {
+	saveLoadHooks.push_back(hooks);
+}
+
 namespace HackSuites {
 
 class ObsidianCorruptedAirTowerTransitionFix : public AssetHooks {
@@ -231,6 +242,343 @@ void addObsidianImprovedWidescreen(const MTropolisGameDescription &desc, Hacks &
 	}
 }
 
+// Auto-save triggers for Obsidian.  Basically, we auto-save on reaching specific scenes when conditions are met.
+// There are two types of condition: One is the player reaches the scene from a one-way scene that can not be
+// revisited, such as a chapter transition.
+//
+// The other is a variable latch, which happens if the variable became true since the last time the reset scene
+// (the opening credits) was reached, or since the last time the game was loaded.
+//
+// Variable latches don't work if the latch becomes true and the player saves+reloads their game before they hit
+// the auto-save checkpoint, but that's okay.
+
+struct ObsidianAutoSaveTrigger {
+	const char *sceneName;
+	const char *priorSceneName; // If set, only save when transitioning from this scene
+	const char *varTrueLatch;   // If set, only save when this variable was set to
+};
+
+static ObsidianAutoSaveTrigger kObsidianAutoSaveTriggers[] = {
+	// Arrive at campsite
+	{
+		"103.2L",
+		"102_103_Credits",
+		nullptr,
+	},
+	// Bureau start
+	{
+		"102.0L",
+		nullptr,
+		"cgst.clst.cl100st.bbossspoken"
+	},
+	// Win cube maze from side room
+	{
+		"445.2L",
+		nullptr,
+		"cgst.clst.cl400st.bcubiclewon",
+	},
+	// Win cube maze from back room
+	{
+		"445.0L",
+		nullptr,
+		"cgst.clst.cl400st.bcubiclewon",
+	},
+	// Stamp document and back away
+	{
+		"445.2L",
+		nullptr,
+		"cgst.clst.cinv.bstampedsd",
+	},
+	// Get repair document from cabinet + back away (English version)
+	{
+		"218.4L",
+		nullptr,
+		"cgst.clst.cinv.bhavesd",
+	},
+	// Get repair document from Bridge Repair + back away (non-English versions)
+	{
+		"109.6L",
+		nullptr,
+		"cgst.clst.cinv.bhavesd",
+	},
+	// Give document to Immediate Action + return to light
+	{
+		"306.6L",
+		nullptr,
+		"cgst.clst.cinv.bgavesd",
+	},
+	// Get rebel document + leave cabinet (English version)
+	{
+		"227.4L",
+		nullptr,
+		"cgst.clst.cinv.bhaveom",
+	},
+	// Solve dial puzzle
+	{
+		"699.0L",
+		nullptr,
+		"cgst.clst.cl308st.bwon",
+	},
+
+	// Spider start
+	{
+		"101.2L",
+		"710.0L",
+		nullptr,
+	},
+	// Leave elevator after completing any Spider puzzle
+	{
+		"121.0L",
+		"118.0L_121.0L-s1",
+		nullptr,
+	},
+
+	// Inspiration start
+	{
+		"101.4L",
+		"121.0L_Lunch_Time",
+		nullptr,
+	},
+	// Complete propulsion puzzle and leave engine room
+	{
+		"201.4L",
+		nullptr,
+		"cgst.cbst.cb2st.bengineon",
+	},
+	// Complete Church puzzle and leave robot
+	{
+		"412.4L",
+		nullptr,
+		"cgst.cbst.cb4st.bwon",
+	},
+	// Complete statue canvas puzzle
+	{
+		"523.2L_from_521.2L-s2",
+		nullptr,
+		"cgst.cbst.cb5st.bpaintedblank",
+	},
+
+	// Conductor start
+	{
+		"101.6L",
+		"203.4L_WIN",
+		nullptr,
+	},
+	// Freed Max
+	{
+		"104.0L_Max_Freed",
+		nullptr,
+		"cgst.ccst.cc1st.bmaxfreed",
+	},
+};
+
+class ObsidianAutoSaveVarsState {
+public:
+	ObsidianAutoSaveVarsState();
+
+	static const VariableModifier *findVar(Runtime *runtime, const Common::String &str);
+
+	bool getVarState(const Common::String &varName) const;
+	void resyncAllVars(Runtime *runtime);
+
+private:
+	Common::HashMap<Common::String, bool> _varState;
+};
+
+ObsidianAutoSaveVarsState::ObsidianAutoSaveVarsState() {
+	for (const ObsidianAutoSaveTrigger &trigger : kObsidianAutoSaveTriggers) {
+		if (trigger.varTrueLatch)
+			_varState[trigger.varTrueLatch] = false;
+	}
+}
+
+const VariableModifier *ObsidianAutoSaveVarsState::findVar(Runtime *runtime, const Common::String &str) {
+	size_t scanStartPos = 0;
+
+	const Modifier *modifierScan = nullptr;
+	const IModifierContainer *container = runtime->getProject();
+
+	for (;;) {
+		size_t dotPos = str.findFirstOf('.', scanStartPos);
+		if (dotPos == Common::String::npos)
+			dotPos = str.size();
+
+		Common::String childName = str.substr(scanStartPos, dotPos - scanStartPos);
+		if (!container)
+			return nullptr;
+
+		modifierScan = nullptr;
+		for (const Common::SharedPtr<Modifier> &modifier : container->getModifiers()) {
+			if (caseInsensitiveEqual(childName, modifier->getName())) {
+				modifierScan = modifier.get();
+				break;
+			}
+		}
+
+		if (!modifierScan)
+			return nullptr;
+
+		if (modifierScan->isCompoundVariable())
+			container = static_cast<const CompoundVariableModifier *>(modifierScan);
+
+		if (dotPos == str.size())
+			break;
+
+		scanStartPos = dotPos + 1;
+	}
+
+	if (modifierScan && modifierScan->isVariable())
+		return static_cast<const VariableModifier *>(modifierScan);
+
+	return nullptr;
+}
+
+bool ObsidianAutoSaveVarsState::getVarState(const Common::String &varName) const {
+	Common::HashMap<Common::String, bool>::const_iterator it = _varState.find(varName);
+	if (it == _varState.end())
+		return false;
+	return it->_value;
+}
+
+void ObsidianAutoSaveVarsState::resyncAllVars(Runtime *runtime) {
+	for (Common::HashMap<Common::String, bool>::iterator it = _varState.begin(), itEnd = _varState.end(); it != itEnd; ++it) {
+		const VariableModifier *var = findVar(runtime, it->_key);
+		if (var) {
+			DynamicValue varValue;
+			var->varGetValue(nullptr, varValue);
+			assert(varValue.getType() == DynamicValueTypes::kBoolean);
+
+			it->_value = varValue.getBool();
+		}
+	}
+}
+
+class ObsidianAutoSaveSceneTransitionHooks : public SceneTransitionHooks {
+public:
+	explicit ObsidianAutoSaveSceneTransitionHooks(const Common::SharedPtr<ObsidianAutoSaveVarsState> &vars, IAutoSaveProvider *autoSaveProvider);
+
+	void onSceneTransitionEnded(Runtime *runtime, const Common::WeakPtr<Structural> &newScene) override;
+
+private:
+	Common::SharedPtr<ObsidianAutoSaveVarsState> _varsState;
+	IAutoSaveProvider *_autoSaveProvider;
+
+	Common::String _currentSceneName;
+	Common::String _prevSceneName;
+
+	Common::String _resetSceneName;
+	Common::String _saveVarName;
+};
+
+ObsidianAutoSaveSceneTransitionHooks::ObsidianAutoSaveSceneTransitionHooks(const Common::SharedPtr<ObsidianAutoSaveVarsState> &vars, IAutoSaveProvider *autoSaveProvider)
+	: _varsState(vars), _autoSaveProvider(autoSaveProvider) {
+
+	_resetSceneName = Common::String("101_102_Credits");
+	_saveVarName = Common::String("cgst");
+}
+
+void ObsidianAutoSaveSceneTransitionHooks::onSceneTransitionEnded(Runtime *runtime, const Common::WeakPtr<Structural> &newScene) {
+	bool triggerAutoSave = false;
+
+	if (newScene.expired())
+		return;
+
+	_prevSceneName = _currentSceneName;
+	_currentSceneName = newScene.lock()->getName();
+
+	for (const ObsidianAutoSaveTrigger &trigger : kObsidianAutoSaveTriggers) {
+		Common::String triggerSceneName(trigger.sceneName);
+
+		if (!caseInsensitiveEqual(triggerSceneName, _currentSceneName))
+			continue;
+
+		if (trigger.priorSceneName && !caseInsensitiveEqual(trigger.priorSceneName, _prevSceneName))
+			continue;
+
+		if (trigger.varTrueLatch) {
+			Common::String varName(trigger.varTrueLatch);
+
+			// Variable must must have been false since the last game load or reset
+			if (_varsState->getVarState(varName))
+				continue;
+
+			bool passedLatchTest = false;
+
+			const VariableModifier *var = _varsState->findVar(runtime, varName);
+			if (var) {
+				DynamicValue varValue;
+				var->varGetValue(nullptr, varValue);
+				assert(varValue.getType() == DynamicValueTypes::kBoolean);
+
+				passedLatchTest = varValue.getBool();
+			}
+
+			if (!passedLatchTest)
+				continue;
+		}
+
+		triggerAutoSave = true;
+		break;
+	}
+
+	if (triggerAutoSave) {
+		Common::SharedPtr<Modifier> saveVar;
+
+		for (const Common::SharedPtr<Modifier> &child : runtime->getProject()->getModifiers()) {
+			if (caseInsensitiveEqual(child->getName(), _saveVarName)) {
+				saveVar = child;
+				break;
+			}
+		}
+
+		if (saveVar || saveVar->isModifier()) {
+			Modifier *modifier = static_cast<Modifier *>(saveVar.get());
+			Common::SharedPtr<ModifierSaveLoad> saveLoad = modifier->getSaveLoad();
+
+			if (saveLoad) {
+				CompoundVarSaver saver(saveVar.get());
+				_autoSaveProvider->autoSave(&saver);
+
+				_varsState->resyncAllVars(runtime);
+			}
+		}
+	}
+
+	if (caseInsensitiveEqual(_currentSceneName, _resetSceneName))
+		_varsState->resyncAllVars(runtime);
+}
+
+class ObsidianAutoSaveSaveLoadHooks : public SaveLoadHooks {
+public:
+	explicit ObsidianAutoSaveSaveLoadHooks(const Common::SharedPtr<ObsidianAutoSaveVarsState> &vars);
+
+	void onSave(Runtime *runtime, Modifier *saveLoadModifier, Modifier *varModifier) override;
+	void onLoad(Runtime *runtime, Modifier *saveLoadModifier, Modifier *varModifier) override;
+
+private:
+	Common::SharedPtr<ObsidianAutoSaveVarsState> _varsState;
+};
+
+
+ObsidianAutoSaveSaveLoadHooks::ObsidianAutoSaveSaveLoadHooks(const Common::SharedPtr<ObsidianAutoSaveVarsState> &vars) : _varsState(vars) {
+}
+
+void ObsidianAutoSaveSaveLoadHooks::onSave(Runtime *runtime, Modifier *saveLoadModifier, Modifier *varModifier) {
+	// Reset all variable latches on save
+	_varsState->resyncAllVars(runtime);
+}
+
+void ObsidianAutoSaveSaveLoadHooks::onLoad(Runtime *runtime, Modifier *saveLoadModifier, Modifier *varModifier) {
+	// Reset all variable latches on load
+	_varsState->resyncAllVars(runtime);
+}
+
+void addObsidianAutoSaves(const MTropolisGameDescription &desc, Hacks &hacks, IAutoSaveProvider *autoSaveProvider) {
+	Common::SharedPtr<ObsidianAutoSaveVarsState> varsState(new ObsidianAutoSaveVarsState());
+	hacks.addSceneTransitionHooks(Common::SharedPtr<SceneTransitionHooks>(new ObsidianAutoSaveSceneTransitionHooks(varsState, autoSaveProvider)));
+	hacks.addSaveLoadHooks(Common::SharedPtr<SaveLoadHooks>(new ObsidianAutoSaveSaveLoadHooks(varsState)));
+}
+
 } // End of namespace HackSuites
 
 } // End of namespace MTropolis
diff --git a/engines/mtropolis/hacks.h b/engines/mtropolis/hacks.h
index 3738bbf0a84..3320f2d3087 100644
--- a/engines/mtropolis/hacks.h
+++ b/engines/mtropolis/hacks.h
@@ -30,7 +30,10 @@ namespace MTropolis {
 
 class AssetHooks;
 class ModifierHooks;
+class SaveLoadHooks;
+class SceneTransitionHooks;
 class StructuralHooks;
+struct IAutoSaveProvider;
 struct MTropolisGameDescription;
 
 struct Hacks {
@@ -40,6 +43,8 @@ struct Hacks {
 	void addStructuralHooks(uint32 guid, const Common::SharedPtr<StructuralHooks> &hooks);
 	void addModifierHooks(uint32 guid, const Common::SharedPtr<ModifierHooks> &hooks);
 	void addAssetHooks(const Common::SharedPtr<AssetHooks> &hooks);
+	void addSceneTransitionHooks(const Common::SharedPtr<SceneTransitionHooks> &hooks);
+	void addSaveLoadHooks(const Common::SharedPtr<SaveLoadHooks> &hooks);
 
 	bool ignoreMismatchedProjectNameInObjectLookups;
 
@@ -48,13 +53,15 @@ struct Hacks {
 
 	Common::HashMap<uint32, Common::SharedPtr<StructuralHooks> > structuralHooks;
 	Common::HashMap<uint32, Common::SharedPtr<ModifierHooks> > modifierHooks;
-
+	Common::Array<Common::SharedPtr<SceneTransitionHooks> > sceneTransitionHooks;
 	Common::Array<Common::SharedPtr<AssetHooks> > assetHooks;
+	Common::Array<Common::SharedPtr<SaveLoadHooks> > saveLoadHooks;
 };
 
 namespace HackSuites {
 
 void addObsidianBugFixes(const MTropolisGameDescription &desc, Hacks &hacks);
+void addObsidianAutoSaves(const MTropolisGameDescription &desc, Hacks &hacks, IAutoSaveProvider *autoSaveProvider);
 void addObsidianImprovedWidescreen(const MTropolisGameDescription &desc, Hacks &hacks);
 
 } // End of namespace HackSuites
diff --git a/engines/mtropolis/modifiers.cpp b/engines/mtropolis/modifiers.cpp
index 550dcf019f1..2b223dd6e57 100644
--- a/engines/mtropolis/modifiers.cpp
+++ b/engines/mtropolis/modifiers.cpp
@@ -30,16 +30,6 @@
 
 namespace MTropolis {
 
-class CompoundVarSaver : public ISaveWriter {
-public:
-	explicit CompoundVarSaver(RuntimeObject *object);
-
-	bool writeSave(Common::WriteStream *stream) override;
-
-private:
-	RuntimeObject *_object;
-};
-
 class CompoundVarLoader : public ISaveReader {
 public:
 	explicit CompoundVarLoader(RuntimeObject *object);
@@ -50,22 +40,6 @@ private:
 	RuntimeObject *_object;
 };
 
-CompoundVarSaver::CompoundVarSaver(RuntimeObject *object) : _object(object) {
-}
-
-bool CompoundVarSaver::writeSave(Common::WriteStream *stream) {
-	if (_object == nullptr || !_object->isModifier())
-		return false;
-
-	Modifier *modifier = static_cast<Modifier *>(_object);
-	Common::SharedPtr<ModifierSaveLoad> saveLoad = modifier->getSaveLoad();
-	if (!saveLoad)
-		return false;
-
-	saveLoad->save(modifier, stream);
-	return !stream->err();
-}
-
 CompoundVarLoader::CompoundVarLoader(RuntimeObject *object) : _object(object) {
 }
 
@@ -292,19 +266,29 @@ VThreadState SaveAndRestoreModifier::consumeMessage(Runtime *runtime, const Comm
 	var.resolve(this, objWeak);
 
 	if (objWeak.expired()) {
-		warning("Save failed, couldn't resolve compound var");
+		warning("Save/load failed, couldn't resolve compound var");
 		return kVThreadError;
 	}
 
 	RuntimeObject *obj = objWeak.lock().get();
+	if (!obj->isModifier()) {
+		warning("Save/load failed, source wasn't a modifier");
+		return kVThreadError;
+	}
 
 	if (_saveWhen.respondsTo(msg->getEvent())) {
 		CompoundVarSaver saver(obj);
-		runtime->getSaveProvider()->promptSave(&saver);
+		if (runtime->getSaveProvider()->promptSave(&saver)) {
+			for (const Common::SharedPtr<SaveLoadHooks> &hooks : runtime->getHacks().saveLoadHooks)
+				hooks->onSave(runtime, this, static_cast<Modifier *>(obj));
+		}
 		return kVThreadReturn;
 	} else if (_restoreWhen.respondsTo(msg->getEvent())) {
 		CompoundVarLoader loader(obj);
-		runtime->getLoadProvider()->promptLoad(&loader);
+		if (runtime->getLoadProvider()->promptLoad(&loader)) {
+			for (const Common::SharedPtr<SaveLoadHooks> &hooks : runtime->getHacks().saveLoadHooks)
+				hooks->onLoad(runtime, this, static_cast<Modifier *>(obj));
+		}
 		return kVThreadReturn;
 	}
 
diff --git a/engines/mtropolis/mtropolis.cpp b/engines/mtropolis/mtropolis.cpp
index eece730ea73..a99247a3fd7 100644
--- a/engines/mtropolis/mtropolis.cpp
+++ b/engines/mtropolis/mtropolis.cpp
@@ -120,6 +120,7 @@ Common::Error MTropolisEngine::run() {
 		enhancedColorDepthMode = kColorDepthMode32Bit;
 
 		HackSuites::addObsidianBugFixes(*_gameDescription, _runtime->getHacks());
+		HackSuites::addObsidianAutoSaves(*_gameDescription, _runtime->getHacks(), this);		
 
 		if (ConfMan.getBool("mtropolis_mod_obsidian_widescreen")) {
 			_runtime->getHacks().reportDisplaySize = Common::Point(640, 480);
diff --git a/engines/mtropolis/mtropolis.h b/engines/mtropolis/mtropolis.h
index fe62e4fba92..af743426c69 100644
--- a/engines/mtropolis/mtropolis.h
+++ b/engines/mtropolis/mtropolis.h
@@ -42,7 +42,7 @@ namespace MTropolis {
 class Runtime;
 class RuntimeObject;
 
-class MTropolisEngine : public ::Engine, public ISaveUIProvider, public ILoadUIProvider {
+class MTropolisEngine : public ::Engine, public ISaveUIProvider, public ILoadUIProvider, public IAutoSaveProvider {
 protected:
 	// Engine APIs
 	Common::Error run() override;
@@ -60,6 +60,7 @@ public:
 	Common::Platform getPlatform() const;
 
 	bool promptSave(ISaveWriter *writer) override;
+	bool autoSave(ISaveWriter *writer) override;
 	bool promptLoad(ISaveReader *reader) override;
 
 public:
diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp
index ed24538dd49..abebdcfc820 100644
--- a/engines/mtropolis/runtime.cpp
+++ b/engines/mtropolis/runtime.cpp
@@ -3662,6 +3662,12 @@ const Common::KeyState &KeyboardInputEvent::getKeyState() const {
 Runtime::SceneStackEntry::SceneStackEntry() {
 }
 
+SceneTransitionHooks::~SceneTransitionHooks() {
+}
+
+void SceneTransitionHooks::onSceneTransitionEnded(Runtime *runtime, const Common::WeakPtr<Structural> &newScene) {
+}
+
 Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, ISaveUIProvider *saveProvider, ILoadUIProvider *loadProvider)
 	: _system(system), _mixer(mixer), _saveProvider(saveProvider), _loadProvider(loadProvider),
 	_nextRuntimeGUID(1), _realDisplayMode(kColorDepthModeInvalid), _fakeDisplayMode(kColorDepthModeInvalid),
@@ -3908,6 +3914,9 @@ bool Runtime::runFrame() {
 			for (const SceneStackEntry &sceneStackEntry : _sceneStack)
 				recursiveAutoPlayMedia(sceneStackEntry.scene.get());
 
+			for (const Common::SharedPtr<SceneTransitionHooks> &hooks : _hacks.sceneTransitionHooks)
+				hooks->onSceneTransitionEnded(this, _activeMainScene);
+
 			queueEventAsLowLevelSceneStateTransitionAction(Event::create(EventIDs::kSceneTransitionEnded, 0), _activeMainScene.get(), true, true);
 			continue;
 		}
diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h
index 8410f2c9be5..1b3f4b94ef8 100644
--- a/engines/mtropolis/runtime.h
+++ b/engines/mtropolis/runtime.h
@@ -1452,6 +1452,13 @@ struct DragMotionProperties {
 	bool constrainToParent;
 };
 
+class SceneTransitionHooks {
+public:
+	virtual ~SceneTransitionHooks();
+
+	virtual void onSceneTransitionEnded(Runtime *runtime, const Common::WeakPtr<Structural> &newScene);
+};
+
 class Runtime {
 public:
 	explicit Runtime(OSystem *system, Audio::Mixer *mixer, ISaveUIProvider *saveProvider, ILoadUIProvider *loadProvider);
diff --git a/engines/mtropolis/saveload.cpp b/engines/mtropolis/saveload.cpp
index 3bb93cb45e4..b4d3b28ee5c 100644
--- a/engines/mtropolis/saveload.cpp
+++ b/engines/mtropolis/saveload.cpp
@@ -25,9 +25,36 @@
 #include "gui/saveload.h"
 
 #include "mtropolis/mtropolis.h"
+#include "mtropolis/runtime.h"
 
 namespace MTropolis {
 
+
+CompoundVarSaver::CompoundVarSaver(RuntimeObject *object) : _object(object) {
+}
+
+bool CompoundVarSaver::writeSave(Common::WriteStream *stream) {
+	if (_object == nullptr || !_object->isModifier())
+		return false;
+
+	Modifier *modifier = static_cast<Modifier *>(_object);
+	Common::SharedPtr<ModifierSaveLoad> saveLoad = modifier->getSaveLoad();
+	if (!saveLoad)
+		return false;
+
+	saveLoad->save(modifier, stream);
+	return !stream->err();
+}
+
+SaveLoadHooks::~SaveLoadHooks() {
+}
+
+void SaveLoadHooks::onLoad(Runtime *runtime, Modifier *saveLoadModifier, Modifier *varModifier) {
+}
+
+void SaveLoadHooks::onSave(Runtime *runtime, Modifier *saveLoadModifier, Modifier *varModifier) {
+}
+
 bool MTropolisEngine::promptSave(ISaveWriter *writer) {
 	Common::String desc;
 	int slot;
@@ -77,4 +104,17 @@ bool MTropolisEngine::promptLoad(ISaveReader *reader) {
 	return true;
 }
 
+bool MTropolisEngine::autoSave(ISaveWriter *writer) {
+	const int slot = 0;
+
+	Common::String saveFileName = getSaveStateName(slot);
+	Common::SharedPtr<Common::OutSaveFile> out(_saveFileMan->openForSaving(saveFileName, false));
+	if (!writer->writeSave(out.get()) || out->err())
+		warning("An error occurred while writing file '%s'", saveFileName.c_str());
+
+	getMetaEngine()->appendExtendedSave(out.get(), getTotalPlayTime(), "Auto Save", true);
+
+	return true;
+}
+
 } // End of namespace MTropolis
diff --git a/engines/mtropolis/saveload.h b/engines/mtropolis/saveload.h
index 47159fa97a9..3342f3d9f41 100644
--- a/engines/mtropolis/saveload.h
+++ b/engines/mtropolis/saveload.h
@@ -33,6 +33,8 @@ class WriteStream;
 
 namespace MTropolis {
 
+class Modifier;
+class Runtime;
 class RuntimeObject;
 
 struct ISaveWriter : public IInterfaceBase {
@@ -51,6 +53,28 @@ struct ILoadUIProvider : public IInterfaceBase {
 	virtual bool promptLoad(ISaveReader *reader) = 0;
 };
 
+struct IAutoSaveProvider : public IInterfaceBase {
+	virtual bool autoSave(ISaveWriter *writer) = 0;
+};
+
+class CompoundVarSaver : public ISaveWriter {
+public:
+	explicit CompoundVarSaver(RuntimeObject *object);
+
+	bool writeSave(Common::WriteStream *stream) override;
+
+private:
+	RuntimeObject *_object;
+};
+
+class SaveLoadHooks {
+public:
+	virtual ~SaveLoadHooks();
+
+	virtual void onLoad(Runtime *runtime, Modifier *saveLoadModifier, Modifier *varModifier);
+	virtual void onSave(Runtime *runtime, Modifier *saveLoadModifier, Modifier *varModifier);
+};
+
 } // End of namespace MTropolis
 
 #endif /* MTROPOLIS_SAVELOAD_H */




More information about the Scummvm-git-logs mailing list