[Scummvm-git-logs] scummvm master -> d2bcab3b06dc1df81c35e02e20845083e858af0e
elasota
noreply at scummvm.org
Thu Jul 21 22:23:10 UTC 2022
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
011c4ccd79 MTROPOLIS: Add element fade transitions.
d2bcab3b06 MTROPOLIS: Improve autosave behavior.
Commit: 011c4ccd79df803d9720a09f176e0d7a98ca49a2
https://github.com/scummvm/scummvm/commit/011c4ccd79df803d9720a09f176e0d7a98ca49a2
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-21T18:22:14-04:00
Commit Message:
MTROPOLIS: Add element fade transitions.
Changed paths:
engines/mtropolis/elements.cpp
engines/mtropolis/modifiers.cpp
engines/mtropolis/modifiers.h
engines/mtropolis/runtime.cpp
engines/mtropolis/runtime.h
engines/mtropolis/vthread.cpp
engines/mtropolis/vthread.h
diff --git a/engines/mtropolis/elements.cpp b/engines/mtropolis/elements.cpp
index e818c039f91..6814df980ce 100644
--- a/engines/mtropolis/elements.cpp
+++ b/engines/mtropolis/elements.cpp
@@ -1040,11 +1040,18 @@ void ImageElement::render(Window *window) {
Common::Rect srcRect(optimized->w, optimized->h);
Common::Rect destRect(_cachedAbsoluteOrigin.x, _cachedAbsoluteOrigin.y, _cachedAbsoluteOrigin.x + _rect.width(), _cachedAbsoluteOrigin.y + _rect.height());
+ uint8 alpha = _transitionProps.getAlpha();
+
if (inkMode == VisualElementRenderProperties::kInkModeBackgroundMatte || inkMode == VisualElementRenderProperties::kInkModeBackgroundTransparent) {
const ColorRGB8 transColorRGB8 = _renderProps.getBackColor();
uint32 transColor = optimized->format.ARGBToColor(255, transColorRGB8.r, transColorRGB8.g, transColorRGB8.b);
- window->getSurface()->transBlitFrom(*optimized, srcRect, destRect, transColor);
+ window->getSurface()->transBlitFrom(*optimized, srcRect, destRect, transColor, false, 0, alpha);
} else if (inkMode == VisualElementRenderProperties::kInkModeDefault || inkMode == VisualElementRenderProperties::kInkModeCopy) {
+ if (alpha != 255) {
+ warning("Alpha fade was applied to a default or copy image, this isn't supported yet");
+ _transitionProps.setAlpha(255);
+ }
+
window->getSurface()->blitFrom(*optimized, srcRect, destRect);
} else {
warning("Unimplemented image ink mode");
diff --git a/engines/mtropolis/modifiers.cpp b/engines/mtropolis/modifiers.cpp
index 10291c26020..8e623f50da8 100644
--- a/engines/mtropolis/modifiers.cpp
+++ b/engines/mtropolis/modifiers.cpp
@@ -953,6 +953,17 @@ const char *SceneTransitionModifier::getDefaultName() const {
return "Scene Transition Modifier";
}
+ElementTransitionModifier::ElementTransitionModifier() : _enableWhen(Event::create()), _disableWhen(Event::create()), _rate(0), _steps(0),
+ _transitionType(kTransitionTypeFade), _revealType(kRevealTypeReveal), _transitionStartTime(0), _currentStep(0) {
+}
+
+ElementTransitionModifier::~ElementTransitionModifier() {
+ if (_scheduledEvent) {
+ _scheduledEvent->cancel();
+ _scheduledEvent.reset();
+ }
+}
+
bool ElementTransitionModifier::load(ModifierLoaderContext &context, const Data::ElementTransitionModifier &data) {
if (!loadTypicalHeader(data.modHeader))
return false;
@@ -1017,6 +1028,9 @@ VThreadState ElementTransitionModifier::consumeMessage(Runtime *runtime, const C
}
_scheduledEvent = runtime->getScheduler().scheduleMethod<ElementTransitionModifier, &ElementTransitionModifier::continueTransition>(runtime->getPlayTime() + 1, this);
+ _transitionStartTime = runtime->getPlayTime();
+ _currentStep = 0;
+ setTransitionProgress(0, _steps);
// Pushed tasks, so these are executed in reverse order (Show -> Transition Started)
{
@@ -1060,13 +1074,28 @@ const char *ElementTransitionModifier::getDefaultName() const {
}
void ElementTransitionModifier::continueTransition(Runtime *runtime) {
- // TODO: Make this functional
- completeTransition(runtime);
+ _scheduledEvent.reset();
+
+ const uint64 playTime = runtime->getPlayTime();
+ const uint64 timeSinceStart = playTime - _transitionStartTime;
+
+ uint32 step = static_cast<uint32>(timeSinceStart * _rate / 1000);
+
+ if (step >= _steps || _rate == 0) {
+ completeTransition(runtime);
+ return;
+ }
+
+ if (step != _currentStep) {
+ setTransitionProgress(step, _steps);
+ _currentStep = step;
+ }
+
+ runtime->setSceneGraphDirty();
+ _scheduledEvent = runtime->getScheduler().scheduleMethod<ElementTransitionModifier, &ElementTransitionModifier::continueTransition>(playTime + 1, this);
}
void ElementTransitionModifier::completeTransition(Runtime *runtime) {
- _scheduledEvent.reset();
-
// Pushed tasks, so these are executed in reverse order (Hide -> Transition Ended)
{
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kTransitionEnded, 0), DynamicValue(), getSelfReference()));
@@ -1079,6 +1108,31 @@ void ElementTransitionModifier::completeTransition(Runtime *runtime) {
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, findStructuralOwner(), false, false, true));
runtime->sendMessageOnVThread(dispatch);
}
+
+ setTransitionProgress(( _revealType == kRevealTypeReveal) ? 1 : 0, 1);
+ runtime->setSceneGraphDirty();
+}
+
+void ElementTransitionModifier::setTransitionProgress(uint32 step, uint32 maxSteps) {
+ Structural *structural = findStructuralOwner();
+ if (structural && structural->isElement() && static_cast<Element *>(structural)->isVisual()) {
+ VisualElement *visual = static_cast<VisualElement *>(structural);
+ VisualElementTransitionProperties props = visual->getTransitionProperties();
+
+ if (_transitionType == kTransitionTypeFade) {
+ if (step > maxSteps)
+ step = maxSteps;
+
+ uint32 alpha = step * 255 / maxSteps;
+ if (_revealType == kRevealTypeConceal)
+ alpha = 255 - alpha;
+
+ props.setAlpha(alpha);
+ visual->setTransitionProperties(props);
+ } else {
+ warning("Unsupported transition type");
+ }
+ }
}
bool IfMessengerModifier::load(ModifierLoaderContext &context, const Data::IfMessengerModifier &data) {
diff --git a/engines/mtropolis/modifiers.h b/engines/mtropolis/modifiers.h
index e6c14906870..9ba409b8754 100644
--- a/engines/mtropolis/modifiers.h
+++ b/engines/mtropolis/modifiers.h
@@ -383,6 +383,9 @@ private:
class ElementTransitionModifier : public Modifier {
public:
+ ElementTransitionModifier();
+ ~ElementTransitionModifier();
+
bool load(ModifierLoaderContext &context, const Data::ElementTransitionModifier &data);
bool respondsToEvent(const Event &evt) const override;
@@ -412,14 +415,19 @@ private:
void continueTransition(Runtime *runtime);
void completeTransition(Runtime *runtime);
+ void setTransitionProgress(uint32 steps, uint32 maxSteps);
+
Event _enableWhen;
Event _disableWhen;
- uint32 _rate; // 1-100, higher is faster
+ uint32 _rate; // Steps per second
uint16 _steps;
TransitionType _transitionType;
RevealType _revealType;
+ uint64 _transitionStartTime;
+ uint32 _currentStep;
+
Common::SharedPtr<ScheduledEvent> _scheduledEvent;
};
diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp
index c3503483e32..cf56fdc93c4 100644
--- a/engines/mtropolis/runtime.cpp
+++ b/engines/mtropolis/runtime.cpp
@@ -5692,6 +5692,38 @@ void Runtime::setSaveScreenshotOverride(const Common::SharedPtr<Graphics::Surfac
_saveScreenshotOverride = screenshot;
}
+bool Runtime::isIdle() const {
+ // The runtime is idle if nothing is happening except for scheduled events and the OS queue
+ if (_vthread->hasTasks())
+ return false;
+
+ if (_sceneTransitionState != kSceneTransitionStateNotTransitioning)
+ return false;
+
+ if (_forceCursorRefreshOnce)
+ return false;
+
+ if (_queuedProjectDesc)
+ return false;
+
+ if (_pendingTeardowns.size() > 0)
+ return false;
+
+ if (_pendingLowLevelTransitions.size() > 0)
+ return false;
+
+ if (_messageQueue.size() > 0)
+ return false;
+
+ if (_pendingSceneTransitions.size() > 0)
+ return false;
+
+ if (_isQuitting)
+ return false;
+
+ return true;
+}
+
void Runtime::ensureMainWindowExists() {
// Maybe there's a better spot for this
if (_mainWindow.expired() && _project) {
@@ -7004,6 +7036,26 @@ bool Element::resolveMediaMarkerLabel(const Label& label, int32 &outResolution)
return false;
}
+VisualElementTransitionProperties::VisualElementTransitionProperties() : _isDirty(true), _alpha(255) {
+}
+
+uint8 VisualElementTransitionProperties::getAlpha() const {
+ return _alpha;
+}
+
+void VisualElementTransitionProperties::setAlpha(uint8 alpha) {
+ _isDirty = true;
+ _alpha = alpha;
+}
+
+bool VisualElementTransitionProperties::isDirty() const {
+ return _isDirty;
+}
+
+void VisualElementTransitionProperties::clearDirty() {
+ _isDirty = false;
+}
+
VisualElementRenderProperties::VisualElementRenderProperties()
: _inkMode(kInkModeDefault), _shape(kShapeRect), _foreColor(ColorRGB8::create(0, 0, 0)), _backColor(ColorRGB8::create(255, 255, 255)),
_borderColor(ColorRGB8::create(0, 0, 0)), _shadowColor(ColorRGB8::create(0, 0, 0)), _borderSize(0), _shadowSize(0), _isDirty(true) {
@@ -7459,6 +7511,14 @@ VThreadState VisualElement::offsetTranslateTask(const OffsetTranslateTaskData &d
return kVThreadReturn;
}
+void VisualElement::setTransitionProperties(const VisualElementTransitionProperties &props) {
+ _transitionProps = props;
+}
+
+const VisualElementTransitionProperties &VisualElement::getTransitionProperties() const {
+ return _transitionProps;
+}
+
void VisualElement::setRenderProperties(const VisualElementRenderProperties &props, const Common::WeakPtr<GraphicModifier> &primaryGraphicModifier) {
_renderProps = props;
_primaryGraphicModifier = primaryGraphicModifier;
diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h
index 1cfcb235d73..8c4dd9f0b11 100644
--- a/engines/mtropolis/runtime.h
+++ b/engines/mtropolis/runtime.h
@@ -100,6 +100,7 @@ struct IPlugInModifierFactory;
struct IPlugInModifierFactoryAndDataFactory;
struct IPostEffect;
struct ISaveUIProvider;
+struct ISaveWriter;
struct IStructuralReferenceVisitor;
struct MessageProperties;
struct ModifierLoaderContext;
@@ -1601,6 +1602,8 @@ public:
const Common::SharedPtr<Graphics::Surface> &getSaveScreenshotOverride() const;
void setSaveScreenshotOverride(const Common::SharedPtr<Graphics::Surface> &screenshot);
+ bool isIdle() const;
+
#ifdef MTROPOLIS_DEBUG_ENABLE
void debugSetEnabled(bool enabled);
void debugBreak();
@@ -2400,6 +2403,21 @@ protected:
bool _haveCheckedAutoPlay;
};
+class VisualElementTransitionProperties {
+public:
+ VisualElementTransitionProperties();
+
+ uint8 getAlpha() const;
+ void setAlpha(uint8 alpha);
+
+ bool isDirty() const;
+ void clearDirty();
+
+private:
+ uint8 _alpha;
+ bool _isDirty;
+};
+
class VisualElementRenderProperties {
public:
VisualElementRenderProperties();
@@ -2536,6 +2554,9 @@ public:
const VisualElementRenderProperties &getRenderProperties() const;
const Common::WeakPtr<GraphicModifier> &getPrimaryGraphicModifier() const;
+ void setTransitionProperties(const VisualElementTransitionProperties &props);
+ const VisualElementTransitionProperties &getTransitionProperties() const;
+
bool needsRender() const;
virtual void render(Window *window) = 0;
void finalizeRender();
@@ -2583,10 +2604,13 @@ protected:
Common::SharedPtr<DragMotionProperties> _dragProps;
- // Quirk: When a graphic modifier is applied, it needs to be
+ // Quirk: When a graphic modifier is applied, it becomes the primary graphic modifier, and disabling it
+ // will only take effect if it's the primary graphic modifier.
VisualElementRenderProperties _renderProps;
Common::WeakPtr<GraphicModifier> _primaryGraphicModifier;
+ VisualElementTransitionProperties _transitionProps;
+
Common::Rect _prevRect;
bool _contentsDirty;
};
diff --git a/engines/mtropolis/vthread.cpp b/engines/mtropolis/vthread.cpp
index 3623fc7b8e3..33b80308122 100644
--- a/engines/mtropolis/vthread.cpp
+++ b/engines/mtropolis/vthread.cpp
@@ -74,6 +74,10 @@ VThreadState VThread::step() {
return kVThreadReturn;
}
+bool VThread::hasTasks() const {
+ return _used > 0;
+}
+
void VThread::reserveFrame(size_t size, size_t alignment, void *&outFramePtr, void *&outUnadjustedDataPtr, size_t &outPrevFrameOffset) {
const size_t frameAlignment = alignof(VThreadStackFrame);
const size_t frameAlignmentMask = frameAlignment - 1;
diff --git a/engines/mtropolis/vthread.h b/engines/mtropolis/vthread.h
index 99c88aef040..915c5367f57 100644
--- a/engines/mtropolis/vthread.h
+++ b/engines/mtropolis/vthread.h
@@ -138,6 +138,8 @@ public:
VThreadState step();
+ bool hasTasks() const;
+
private:
template<typename TClass, typename TData>
TData *pushTaskWithFaultHandler(const VThreadFaultIdentifier *faultID, const char *name, TClass *obj, VThreadState (TClass::*method)(const TData &data));
Commit: d2bcab3b06dc1df81c35e02e20845083e858af0e
https://github.com/scummvm/scummvm/commit/d2bcab3b06dc1df81c35e02e20845083e858af0e
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-21T18:22:15-04:00
Commit Message:
MTROPOLIS: Improve autosave behavior.
Changed paths:
engines/mtropolis/detection.cpp
engines/mtropolis/detection_tables.h
engines/mtropolis/hacks.cpp
engines/mtropolis/hacks.h
engines/mtropolis/metaengine.cpp
engines/mtropolis/mtropolis.cpp
engines/mtropolis/mtropolis.h
engines/mtropolis/saveload.cpp
engines/mtropolis/saveload.h
diff --git a/engines/mtropolis/detection.cpp b/engines/mtropolis/detection.cpp
index b67612ce9a3..388b979f15d 100644
--- a/engines/mtropolis/detection.cpp
+++ b/engines/mtropolis/detection.cpp
@@ -60,11 +60,11 @@ static const ADExtraGuiOptionsMap optionsList[] = {
}
},
{
- GAMEOPTION_AUTO_SAVE,
+ GAMEOPTION_AUTO_SAVE_AT_CHECKPOINTS,
{
- _s("Save progress automatically"),
- _s("Automatically saves the game at certain progress points."),
- "mtropolis_mod_auto_save",
+ _s("Autosave at progress checkpoints"),
+ _s("Automatically saves the game at progress."),
+ "mtropolis_mod_auto_save_at_checkpoints",
true,
0,
0
diff --git a/engines/mtropolis/detection_tables.h b/engines/mtropolis/detection_tables.h
index 0cd0eeed3e0..c92897ee35b 100644
--- a/engines/mtropolis/detection_tables.h
+++ b/engines/mtropolis/detection_tables.h
@@ -30,7 +30,7 @@
#define GAMEOPTION_DYNAMIC_MIDI GUIO_GAMEOPTIONS2
#define GAMEOPTION_LAUNCH_DEBUG GUIO_GAMEOPTIONS3
#define GAMEOPTION_LAUNCH_BREAK GUIO_GAMEOPTIONS4
-#define GAMEOPTION_AUTO_SAVE GUIO_GAMEOPTIONS5
+#define GAMEOPTION_AUTO_SAVE_AT_CHECKPOINTS GUIO_GAMEOPTIONS5
#define GAMEOPTION_ENABLE_SHORT_TRANSITIONS GUIO_GAMEOPTIONS6
namespace MTropolis {
@@ -53,7 +53,7 @@ static const MTropolisGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformMacintosh,
ADGF_UNSTABLE,
- GUIO2(GAMEOPTION_WIDESCREEN_MOD, GAMEOPTION_AUTO_SAVE)
+ GUIO2(GAMEOPTION_WIDESCREEN_MOD, GAMEOPTION_AUTO_SAVE_AT_CHECKPOINTS)
},
GID_OBSIDIAN,
0,
@@ -80,7 +80,7 @@ static const MTropolisGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO2(GAMEOPTION_WIDESCREEN_MOD, GAMEOPTION_AUTO_SAVE)
+ GUIO2(GAMEOPTION_WIDESCREEN_MOD, GAMEOPTION_AUTO_SAVE_AT_CHECKPOINTS)
},
GID_OBSIDIAN,
0,
diff --git a/engines/mtropolis/hacks.cpp b/engines/mtropolis/hacks.cpp
index 2ab5807dcbf..d3ac763042e 100644
--- a/engines/mtropolis/hacks.cpp
+++ b/engines/mtropolis/hacks.cpp
@@ -64,6 +64,10 @@ void Hacks::addSaveLoadHooks(const Common::SharedPtr<SaveLoadHooks> &hooks) {
saveLoadHooks.push_back(hooks);
}
+void Hacks::addSaveLoadMechanismHooks(const Common::SharedPtr<SaveLoadMechanismHooks> &hooks) {
+ saveLoadMechanismHooks.push_back(hooks);
+}
+
namespace HackSuites {
class ObsidianCorruptedAirTowerTransitionFix : public AssetHooks {
@@ -914,6 +918,100 @@ void addObsidianAutoSaves(const MTropolisGameDescription &desc, Hacks &hacks, IA
hacks.addSaveLoadHooks(Common::SharedPtr<SaveLoadHooks>(new ObsidianAutoSaveSaveLoadHooks(varsState)));
}
+class ObsidianSaveLoadMechanism : public SaveLoadMechanismHooks {
+public:
+ bool canSaveNow(Runtime *runtime) override;
+ Common::SharedPtr<ISaveWriter> createSaveWriter(Runtime *runtime) override;
+};
+
+bool ObsidianSaveLoadMechanism::canSaveNow(Runtime *runtime) {
+ Project *project = runtime->getProject();
+
+ // Check that we're in a game section
+ Structural *mainScene = runtime->getActiveMainScene().get();
+
+ if (!mainScene)
+ return false;
+
+ const Common::String disallowedSections[] = {
+ Common::String("Start Obsidian"), // Intro videos/screens
+ Common::String("End Obsidian"), // Credits
+ Common::String("GUI"), // Menus
+ };
+
+ Common::String sectionName = mainScene->getParent()->getParent()->getName();
+
+ for (const Common::String &disallowedSection : disallowedSections) {
+ if (caseInsensitiveEqual(disallowedSection, sectionName))
+ return false;
+ }
+
+ // Check that the g.bESC flag is set, meaning we can go to the menu
+ Common::String gName("g");
+ Common::String bEscName("bESC");
+
+ Modifier *gCompoundVar = nullptr;
+ for (const Common::SharedPtr<Modifier> &child : project->getModifiers()) {
+ if (caseInsensitiveEqual(child->getName(), gName)) {
+ gCompoundVar = child.get();
+ break;
+ }
+ }
+
+ if (!gCompoundVar)
+ return false;
+
+ IModifierContainer *container = gCompoundVar->getChildContainer();
+ if (!container)
+ return false;
+
+ Modifier *bEscVar = nullptr;
+ for (const Common::SharedPtr<Modifier> &child : container->getModifiers()) {
+ if (caseInsensitiveEqual(child->getName(), bEscName)) {
+ bEscVar = child.get();
+ break;
+ }
+ }
+
+ if (!bEscVar || !bEscVar->isVariable())
+ return false;
+
+ DynamicValue bEscValue;
+ static_cast<VariableModifier *>(bEscVar)->varGetValue(nullptr, bEscValue);
+
+ if (bEscValue.getType() != DynamicValueTypes::kBoolean || !bEscValue.getBool())
+ return false;
+
+ return true;
+}
+
+Common::SharedPtr<ISaveWriter> ObsidianSaveLoadMechanism::createSaveWriter(Runtime *runtime) {
+ Project *project = runtime->getProject();
+
+ Common::String cgstName("cGSt");
+
+ Modifier *cgstCompoundVar = nullptr;
+ for (const Common::SharedPtr<Modifier> &child : project->getModifiers()) {
+ if (caseInsensitiveEqual(child->getName(), cgstName)) {
+ cgstCompoundVar = child.get();
+ break;
+ }
+ }
+
+ if (!cgstCompoundVar)
+ return nullptr;
+
+ if (cgstCompoundVar->getSaveLoad())
+ return Common::SharedPtr<CompoundVarSaver>(new CompoundVarSaver(cgstCompoundVar));
+
+ return nullptr;
+}
+
+void addObsidianSaveMechanism(const MTropolisGameDescription &desc, Hacks &hacks) {
+ Common::SharedPtr<ObsidianSaveLoadMechanism> mechanism(new ObsidianSaveLoadMechanism());
+ hacks.addSaveLoadMechanismHooks(mechanism);
+}
+
} // End of namespace HackSuites
} // End of namespace MTropolis
diff --git a/engines/mtropolis/hacks.h b/engines/mtropolis/hacks.h
index 8df373e9a99..94683e1cae8 100644
--- a/engines/mtropolis/hacks.h
+++ b/engines/mtropolis/hacks.h
@@ -33,6 +33,7 @@ class ModifierHooks;
class SaveLoadHooks;
class SceneTransitionHooks;
class StructuralHooks;
+class SaveLoadMechanismHooks;
struct IAutoSaveProvider;
struct MTropolisGameDescription;
@@ -45,6 +46,7 @@ struct Hacks {
void addAssetHooks(const Common::SharedPtr<AssetHooks> &hooks);
void addSceneTransitionHooks(const Common::SharedPtr<SceneTransitionHooks> &hooks);
void addSaveLoadHooks(const Common::SharedPtr<SaveLoadHooks> &hooks);
+ void addSaveLoadMechanismHooks(const Common::SharedPtr<SaveLoadMechanismHooks> &hooks);
bool ignoreMismatchedProjectNameInObjectLookups;
uint midiVolumeScale; // 256 = 1.0
@@ -59,6 +61,7 @@ struct Hacks {
Common::Array<Common::SharedPtr<SceneTransitionHooks> > sceneTransitionHooks;
Common::Array<Common::SharedPtr<AssetHooks> > assetHooks;
Common::Array<Common::SharedPtr<SaveLoadHooks> > saveLoadHooks;
+ Common::Array<Common::SharedPtr<SaveLoadMechanismHooks> > saveLoadMechanismHooks;
};
namespace HackSuites {
@@ -66,6 +69,7 @@ namespace HackSuites {
void addObsidianQuirks(const MTropolisGameDescription &desc, Hacks &hacks);
void addObsidianBugFixes(const MTropolisGameDescription &desc, Hacks &hacks);
void addObsidianAutoSaves(const MTropolisGameDescription &desc, Hacks &hacks, IAutoSaveProvider *autoSaveProvider);
+void addObsidianSaveMechanism(const MTropolisGameDescription &desc, Hacks &hacks);
void addObsidianImprovedWidescreen(const MTropolisGameDescription &desc, Hacks &hacks);
} // End of namespace HackSuites
diff --git a/engines/mtropolis/metaengine.cpp b/engines/mtropolis/metaengine.cpp
index 107b787d609..e98feb03602 100644
--- a/engines/mtropolis/metaengine.cpp
+++ b/engines/mtropolis/metaengine.cpp
@@ -88,15 +88,6 @@ bool MTropolisMetaEngine::hasFeature(MetaEngineFeature f) const {
}
}
-bool MTropolis::MTropolisEngine::hasFeature(EngineFeature f) const {
- switch (f) {
- case kSupportsReturnToLauncher:
- return true;
- default:
- return false;
- };
-}
-
Common::Error MTropolisMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
*engine = new MTropolis::MTropolisEngine(syst, reinterpret_cast<const MTropolis::MTropolisGameDescription *>(desc));
return Common::kNoError;
diff --git a/engines/mtropolis/mtropolis.cpp b/engines/mtropolis/mtropolis.cpp
index 0b9e8e0f5de..b979208316d 100644
--- a/engines/mtropolis/mtropolis.cpp
+++ b/engines/mtropolis/mtropolis.cpp
@@ -50,7 +50,7 @@
namespace MTropolis {
-MTropolisEngine::MTropolisEngine(OSystem *syst, const MTropolisGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
+MTropolisEngine::MTropolisEngine(OSystem *syst, const MTropolisGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc), _saveWriter(nullptr), _isTriggeredAutosave(false) {
const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "Resource");
@@ -121,8 +121,9 @@ Common::Error MTropolisEngine::run() {
HackSuites::addObsidianQuirks(*_gameDescription, _runtime->getHacks());
HackSuites::addObsidianBugFixes(*_gameDescription, _runtime->getHacks());
+ HackSuites::addObsidianSaveMechanism(*_gameDescription, _runtime->getHacks());
- if (ConfMan.getBool("mtropolis_mod_auto_save"))
+ if (ConfMan.getBool("mtropolis_mod_auto_save_at_checkpoints"))
HackSuites::addObsidianAutoSaves(*_gameDescription, _runtime->getHacks(), this);
if (ConfMan.getBool("mtropolis_mod_obsidian_widescreen")) {
@@ -262,4 +263,16 @@ void MTropolisEngine::pauseEngineIntern(bool pause) {
Engine::pauseEngineIntern(pause);
}
+
+
+bool MTropolisEngine::hasFeature(EngineFeature f) const {
+ switch (f) {
+ case kSupportsReturnToLauncher:
+ case kSupportsSavingDuringRuntime:
+ return true;
+ default:
+ return false;
+ };
+}
+
} // End of namespace MTropolis
diff --git a/engines/mtropolis/mtropolis.h b/engines/mtropolis/mtropolis.h
index c014eb939de..027e0966c35 100644
--- a/engines/mtropolis/mtropolis.h
+++ b/engines/mtropolis/mtropolis.h
@@ -65,6 +65,10 @@ public:
const Graphics::Surface *getSavegameScreenshot() const;
+ Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave) override;
+ bool canSaveAutosaveCurrently() override;
+ bool canSaveGameStateCurrently() override;
+
public:
void handleEvents();
@@ -75,6 +79,9 @@ private:
static const uint kCurrentSaveFileVersion = 1;
static const uint kSavegameSignature = 0x6d545356; // mTSV
+ ISaveWriter *_saveWriter;
+ bool _isTriggeredAutosave;
+
Common::ScopedPtr<Runtime> _runtime;
};
diff --git a/engines/mtropolis/saveload.cpp b/engines/mtropolis/saveload.cpp
index 7c05c2eb361..75d458e9c9c 100644
--- a/engines/mtropolis/saveload.cpp
+++ b/engines/mtropolis/saveload.cpp
@@ -58,6 +58,17 @@ void SaveLoadHooks::onLoad(Runtime *runtime, Modifier *saveLoadModifier, Modifie
void SaveLoadHooks::onSave(Runtime *runtime, Modifier *saveLoadModifier, Modifier *varModifier) {
}
+SaveLoadMechanismHooks::~SaveLoadMechanismHooks() {
+}
+
+bool SaveLoadMechanismHooks::canSaveNow(Runtime *runtime) {
+ return false;
+}
+
+Common::SharedPtr<ISaveWriter> SaveLoadMechanismHooks::createSaveWriter(Runtime *runtime) {
+ return nullptr;
+}
+
bool MTropolisEngine::promptSave(ISaveWriter *writer, const Graphics::Surface *screenshotOverride) {
Common::String desc;
int slot;
@@ -78,11 +89,13 @@ bool MTropolisEngine::promptSave(ISaveWriter *writer, const Graphics::Surface *s
Common::String saveFileName = getSaveStateName(slot);
Common::SharedPtr<Common::OutSaveFile> out(_saveFileMan->openForSaving(saveFileName, false));
- out->writeUint32BE(kSavegameSignature);
- out->writeUint32BE(kCurrentSaveFileVersion);
+ ISaveWriter *oldWriter = _saveWriter;
+
+ _saveWriter = writer;
+
+ saveGameStream(out.get(), false);
- if (!writer->writeSave(out.get()) || out->err())
- warning("An error occurred while writing file '%s'", saveFileName.c_str());
+ _saveWriter = oldWriter;
getMetaEngine()->appendExtendedSave(out.get(), getTotalPlayTime(), desc, false);
@@ -142,20 +155,16 @@ bool MTropolisEngine::promptLoad(ISaveReader *reader) {
}
bool MTropolisEngine::autoSave(ISaveWriter *writer) {
- const int slot = 0;
+ ISaveWriter *oldWriter = _saveWriter;
+ bool oldIsTriggeredAutosave = _isTriggeredAutosave;
- Common::String saveFileName = getSaveStateName(slot);
- Common::SharedPtr<Common::OutSaveFile> out(_saveFileMan->openForSaving(saveFileName, false));
-
- out->writeUint32BE(kSavegameSignature);
- out->writeUint32BE(kCurrentSaveFileVersion);
-
- if (!writer->writeSave(out.get()) || out->err())
- warning("An error occurred while writing file '%s'", saveFileName.c_str());
+ _saveWriter = writer;
+ _isTriggeredAutosave = true;
- getMetaEngine()->appendExtendedSave(out.get(), getTotalPlayTime(), "Auto Save", true);
+ saveAutosaveIfEnabled();
- g_system->displayMessageOnOSD(_("Progress Saved"));
+ _saveWriter = oldWriter;
+ _isTriggeredAutosave = oldIsTriggeredAutosave;
return true;
}
@@ -172,4 +181,52 @@ const Graphics::Surface *MTropolisEngine::getSavegameScreenshot() const {
}
}
+Common::Error MTropolisEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
+ ISaveWriter *saveWriter = _saveWriter;
+
+ Common::SharedPtr<ISaveWriter> mechanismHookWriter;
+ if (!saveWriter) {
+ for (Common::SharedPtr<SaveLoadMechanismHooks> &hooks : _runtime->getHacks().saveLoadMechanismHooks) {
+ if (hooks->canSaveNow(_runtime.get())) {
+ mechanismHookWriter = hooks->createSaveWriter(_runtime.get());
+ saveWriter = mechanismHookWriter.get();
+ break;
+ }
+ }
+ }
+
+ if (!saveWriter)
+ return Common::Error(Common::kWritingFailed, Common::convertFromU32String(_("An internal error occurred while attempting to write save game data")));
+
+ assert(saveWriter);
+
+ stream->writeUint32BE(kSavegameSignature);
+ stream->writeUint32BE(kCurrentSaveFileVersion);
+
+ if (!saveWriter->writeSave(stream) || stream->err())
+ return Common::Error(Common::kWritingFailed, Common::convertFromU32String(_("An error occurred while writing the save game")));
+
+ return Common::kNoError;
+}
+
+bool MTropolisEngine::canSaveAutosaveCurrently() {
+ // Triggered autosaves are always safe
+ if (_isTriggeredAutosave)
+ return true;
+
+ return canSaveGameStateCurrently();
+}
+
+bool MTropolisEngine::canSaveGameStateCurrently() {
+ if (!_runtime->isIdle())
+ return false;
+
+ for (Common::SharedPtr<SaveLoadMechanismHooks> &hooks : _runtime->getHacks().saveLoadMechanismHooks) {
+ if (hooks->canSaveNow(_runtime.get()))
+ return true;
+ }
+
+ return false;
+}
+
} // End of namespace MTropolis
diff --git a/engines/mtropolis/saveload.h b/engines/mtropolis/saveload.h
index ea9bed54f48..bc94ead80d4 100644
--- a/engines/mtropolis/saveload.h
+++ b/engines/mtropolis/saveload.h
@@ -81,6 +81,14 @@ public:
virtual void onSave(Runtime *runtime, Modifier *saveLoadModifier, Modifier *varModifier);
};
+class SaveLoadMechanismHooks {
+public:
+ virtual ~SaveLoadMechanismHooks();
+
+ virtual bool canSaveNow(Runtime *runtime);
+ virtual Common::SharedPtr<ISaveWriter> createSaveWriter(Runtime *runtime);
+};
+
} // End of namespace MTropolis
#endif /* MTROPOLIS_SAVELOAD_H */
More information about the Scummvm-git-logs
mailing list