[Scummvm-git-logs] scummvm master -> b5a067415a0e0d354603ed47966dedbff3e31c69
elasota
noreply at scummvm.org
Sat Jul 6 04:46:32 UTC 2024
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:
dbc1f8f4cf MTROPOLIS: Add "section" attrib, force-load Sound subsection in SPQR
b5a067415a MTROPOLIS: Add object reparenting, cloning, and killing
Commit: dbc1f8f4cfb49e2fb380c910891aa30da11cf4b6
https://github.com/scummvm/scummvm/commit/dbc1f8f4cfb49e2fb380c910891aa30da11cf4b6
Author: elasota (1137273+elasota at users.noreply.github.com)
Date: 2024-07-06T00:45:58-04:00
Commit Message:
MTROPOLIS: Add "section" attrib, force-load Sound subsection in SPQR
Changed paths:
engines/mtropolis/hacks.cpp
engines/mtropolis/hacks.h
engines/mtropolis/mtropolis.cpp
engines/mtropolis/runtime.cpp
engines/mtropolis/runtime.h
diff --git a/engines/mtropolis/hacks.cpp b/engines/mtropolis/hacks.cpp
index e3fddc92a5b..96164220414 100644
--- a/engines/mtropolis/hacks.cpp
+++ b/engines/mtropolis/hacks.cpp
@@ -1137,6 +1137,43 @@ void addMTIQuirks(const MTropolisGameDescription &desc, Hacks &hacks) {
hacks.addSceneTransitionHooks(Common::SharedPtr<SceneTransitionHooks>(new MTIMolassesSceneTransitionHooks(molassesHandler)));
}
+class SPQRSoundPreloadHooks : public SceneTransitionHooks {
+public:
+ void onProjectStarted(Runtime *runtime) override;
+};
+
+void SPQRSoundPreloadHooks::onProjectStarted(Runtime *runtime) {
+ Project *project = runtime->getProject();
+
+ Structural *worldSection = nullptr;
+ Structural *soundSubsection = nullptr;
+
+ for (const Common::SharedPtr<Structural> &child : project->getChildren()) {
+ if (child->getName() == "World") {
+ worldSection = child.get();
+ break;
+ }
+ }
+
+ if (worldSection) {
+ for (const Common::SharedPtr<Structural> &child : worldSection->getChildren()) {
+ if (child->getName() == "Sound") {
+ soundSubsection = child.get();
+ break;
+ }
+ }
+ }
+
+ if (soundSubsection) {
+ for (const Common::SharedPtr<Structural> &child : soundSubsection->getChildren())
+ runtime->addSceneStateTransition(HighLevelSceneTransition(child, HighLevelSceneTransition::kTypeForceLoadScene, false, false));
+ }
+}
+
+void addSPQRQuirks(const MTropolisGameDescription &desc, Hacks &hacks) {
+ hacks.addSceneTransitionHooks(Common::SharedPtr<SceneTransitionHooks>(new SPQRSoundPreloadHooks()));
+}
+
} // End of namespace HackSuites
} // End of namespace MTropolis
diff --git a/engines/mtropolis/hacks.h b/engines/mtropolis/hacks.h
index 3c16e9bc69b..c4acd69ac58 100644
--- a/engines/mtropolis/hacks.h
+++ b/engines/mtropolis/hacks.h
@@ -81,6 +81,7 @@ void addObsidianSaveMechanism(const MTropolisGameDescription &desc, Hacks &hacks
void addObsidianImprovedWidescreen(const MTropolisGameDescription &desc, Hacks &hacks);
void addMTIQuirks(const MTropolisGameDescription &desc, Hacks &hacks);
+void addSPQRQuirks(const MTropolisGameDescription &desc, Hacks &hacks);
} // End of namespace HackSuites
diff --git a/engines/mtropolis/mtropolis.cpp b/engines/mtropolis/mtropolis.cpp
index d15fd15f385..f42cded8eba 100644
--- a/engines/mtropolis/mtropolis.cpp
+++ b/engines/mtropolis/mtropolis.cpp
@@ -332,6 +332,8 @@ Common::Error MTropolisEngine::run() {
Palette pal;
pal.initDefaultPalette(2);
_runtime->setGlobalPalette(pal);
+ } else if (_gameDescription->gameID == GID_SPQR) {
+ HackSuites::addSPQRQuirks(*_gameDescription, _runtime->getHacks());
}
diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp
index d4bec6ea2f5..a6b69025d96 100644
--- a/engines/mtropolis/runtime.cpp
+++ b/engines/mtropolis/runtime.cpp
@@ -3151,9 +3151,7 @@ bool Structural::readAttribute(MiniscriptThread *thread, DynamicValue &result, c
} else if (attrib == "scene") {
result.clear();
- // Scene returns the scene of the Miniscript modifier, even though it's looked up
- // as if it's an element property, because it's treated like a keyword.
- RuntimeObject *possibleScene = thread->getModifier();
+ RuntimeObject *possibleScene = this;
while (possibleScene) {
if (possibleScene->isModifier()) {
possibleScene = static_cast<Modifier *>(possibleScene)->getParent().lock().get();
@@ -3173,11 +3171,39 @@ bool Structural::readAttribute(MiniscriptThread *thread, DynamicValue &result, c
assert(false);
break;
}
+
if (possibleScene)
result.setObject(possibleScene->getSelfReference());
else
result.clear();
return true;
+ } else if (attrib == "section") {
+ result.clear();
+
+ RuntimeObject *possibleSection = this;
+ while (possibleSection) {
+ if (possibleSection->isSection())
+ break;
+
+ if (possibleSection->isModifier()) {
+ possibleSection = static_cast<Modifier *>(possibleSection)->getParent().lock().get();
+ continue;
+ }
+
+ if (possibleSection->isStructural()) {
+ possibleSection = static_cast<Structural *>(possibleSection)->getParent();
+ continue;
+ }
+
+ assert(false);
+ break;
+ }
+
+ if (possibleSection)
+ result.setObject(possibleSection->getSelfReference());
+ else
+ result.clear();
+ return true;
} else if (attrib == "subsection") {
result.clear();
@@ -4320,6 +4346,9 @@ void SceneTransitionHooks::onSceneTransitionSetup(Runtime *runtime, const Common
void SceneTransitionHooks::onSceneTransitionEnded(Runtime *runtime, const Common::WeakPtr<Structural> &newScene) {
}
+void SceneTransitionHooks::onProjectStarted(Runtime *runtime) {
+}
+
Palette::Palette() {
initDefaultPalette(1);
@@ -4599,6 +4628,9 @@ bool Runtime::runFrame() {
Common::SharedPtr<MessageDispatch> psDispatch(new MessageDispatch(psProps, _project.get(), false, true, false));
queueMessage(psDispatch);
+ for (const Common::SharedPtr<SceneTransitionHooks> &hook : _hacks.sceneTransitionHooks)
+ hook->onProjectStarted(this);
+
_pendingSceneTransitions.push_back(HighLevelSceneTransition(firstSubsection->getChildren()[1], HighLevelSceneTransition::kTypeChangeToScene, false, false));
continue;
}
@@ -5219,6 +5251,11 @@ void Runtime::executeHighLevelSceneTransition(const HighLevelSceneTransition &tr
_sharedSceneWasSetExplicitly = true;
} break;
+ case HighLevelSceneTransition::kTypeForceLoadScene: {
+ _pendingLowLevelTransitions.push_back(LowLevelSceneStateTransitionAction(transition.scene, LowLevelSceneStateTransitionAction::kLoad));
+ queueEventAsLowLevelSceneStateTransitionAction(Event(EventIDs::kParentEnabled, 0), transition.scene.get(), true, true);
+ queueEventAsLowLevelSceneStateTransitionAction(Event(EventIDs::kSceneStarted, 0), transition.scene.get(), true, true);
+ } break;
default:
error("Unknown high-level scene transition type");
break;
diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h
index d66e251f3a1..ac6f2d1e8da 100644
--- a/engines/mtropolis/runtime.h
+++ b/engines/mtropolis/runtime.h
@@ -1359,6 +1359,7 @@ struct HighLevelSceneTransition {
kTypeReturn,
kTypeChangeToScene,
kTypeChangeSharedScene,
+ kTypeForceLoadScene,
};
HighLevelSceneTransition(const Common::SharedPtr<Structural> &hlst_scene, Type hlst_type, bool hlst_addToDestinationScene, bool hlst_addToReturnList);
@@ -1569,6 +1570,7 @@ public:
virtual void onSceneTransitionSetup(Runtime *runtime, const Common::WeakPtr<Structural> &oldScene, const Common::WeakPtr<Structural> &newScene);
virtual void onSceneTransitionEnded(Runtime *runtime, const Common::WeakPtr<Structural> &newScene);
+ virtual void onProjectStarted(Runtime *runtime);
};
class Palette {
Commit: b5a067415a0e0d354603ed47966dedbff3e31c69
https://github.com/scummvm/scummvm/commit/b5a067415a0e0d354603ed47966dedbff3e31c69
Author: elasota (1137273+elasota at users.noreply.github.com)
Date: 2024-07-06T00:45:58-04:00
Commit Message:
MTROPOLIS: Add object reparenting, cloning, and killing
Changed paths:
engines/mtropolis/elements.cpp
engines/mtropolis/elements.h
engines/mtropolis/hacks.cpp
engines/mtropolis/hacks.h
engines/mtropolis/modifiers.cpp
engines/mtropolis/modifiers.h
engines/mtropolis/runtime.cpp
engines/mtropolis/runtime.h
diff --git a/engines/mtropolis/elements.cpp b/engines/mtropolis/elements.cpp
index 488b6b3de83..69032e99d65 100644
--- a/engines/mtropolis/elements.cpp
+++ b/engines/mtropolis/elements.cpp
@@ -43,6 +43,10 @@ namespace MTropolis {
GraphicElement::GraphicElement() : _cacheBitmap(false) {
}
+GraphicElement::GraphicElement(const GraphicElement &other)
+ : VisualElement(other), _cacheBitmap(other._cacheBitmap), _mask(nullptr) {
+}
+
GraphicElement::~GraphicElement() {
}
@@ -493,6 +497,15 @@ void GraphicElement::render(Window *window) {
}
}
+Common::SharedPtr<Structural> GraphicElement::shallowClone() const {
+ return Common::SharedPtr<Structural>(new GraphicElement(*this));
+}
+
+void GraphicElement::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ VisualElement::visitInternalReferences(visitor);
+
+}
+
MovieResizeFilter::~MovieResizeFilter() {
}
@@ -939,6 +952,17 @@ void MovieElement::setResizeFilter(const Common::SharedPtr<MovieResizeFilter> &f
_resizeFilter = filter;
}
+Common::SharedPtr<Structural> MovieElement::shallowClone() const {
+ error("Cloning movie elements is not currently supported");
+ return nullptr;
+}
+
+void MovieElement::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ VisualElement::visitInternalReferences(visitor);
+
+ error("Cloning movie elements is not currently supported");
+}
+
#ifdef MTROPOLIS_DEBUG_ENABLE
void MovieElement::debugSkipMovies() {
if (_videoDecoder && !_videoDecoder->endOfVideo()) {
@@ -1273,6 +1297,14 @@ void ImageElement::render(Window *window) {
}
}
+Common::SharedPtr<Structural> ImageElement::shallowClone() const {
+ return Common::SharedPtr<Structural>(new ImageElement(*this));
+}
+
+void ImageElement::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ VisualElement::visitInternalReferences(visitor);
+}
+
#ifdef MTROPOLIS_DEBUG_ENABLE
void ImageElement::debugInspect(IDebugInspectionReport *report) const {
VisualElement::debugInspect(report);
@@ -1292,6 +1324,14 @@ MToonElement::MToonElement()
_isPlaying(false), _isStopped(false), _renderedFrame(0), _playRange(IntRange(1, 1)), _cel(1), _hasIssuedRenderWarning(false) {
}
+MToonElement::MToonElement(const MToonElement &other)
+ : VisualElement(other), _cacheBitmap(other._cacheBitmap), _maintainRate(other._maintainRate), _assetID(other._assetID)
+ , _rateTimes100000(other._rateTimes100000), _flushPriority(other._flushPriority), _celStartTimeMSec(other._celStartTimeMSec)
+ , _isPlaying(other._isPlaying), _isStopped(other._isStopped), _renderSurface(nullptr), _renderedFrame(0), _metadata(other._metadata)
+ , _cachedMToon(other._cachedMToon), _playMediaSignaller(nullptr), _playRange(other._playRange), _cel(other._cel), _hasIssuedRenderWarning(false) {
+ _playMediaSignaller = other.getRuntime()->getProject()->notifyOnPlayMedia(this);
+}
+
MToonElement::~MToonElement() {
if (_playMediaSignaller)
_playMediaSignaller->removeReceiver(this);
@@ -1568,6 +1608,15 @@ Common::Rect MToonElement::getRelativeCollisionRect() const {
colRect.translate(_rect.left, _rect.top);
return colRect;
}
+
+Common::SharedPtr<Structural> MToonElement::shallowClone() const {
+ return Common::SharedPtr<Structural>(new MToonElement(*this));
+}
+
+void MToonElement::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ VisualElement::visitInternalReferences(visitor);
+}
+
#ifdef MTROPOLIS_DEBUG_ENABLE
void MToonElement::debugInspect(IDebugInspectionReport *report) const {
VisualElement::debugInspect(report);
@@ -1896,10 +1945,20 @@ MiniscriptInstructionOutcome MToonElement::scriptSetRate(MiniscriptThread *threa
TextLabelElement::TextLabelElement()
- : _cacheBitmap(false), _needsRender(false), /*_isBitmap(false), */_assetID(0),
+ : _cacheBitmap(false), _needsRender(false), _isBitmap(true), _assetID(0),
_macFontID(0), _size(12), _alignment(kTextAlignmentLeft) {
}
+TextLabelElement::TextLabelElement(const TextLabelElement &other)
+ : VisualElement(other), _cacheBitmap(other._cacheBitmap), _needsRender(other._needsRender), _isBitmap(other._isBitmap)
+ , _assetID(other._assetID), _text(other._text), _macFontID(other._macFontID), _fontFamilyName(other._fontFamilyName)
+ , _size(other._size), _alignment(other._alignment), _styleFlags(other._styleFlags), _macFormattingSpans(other._macFormattingSpans)
+ , _renderedText(nullptr) {
+
+ if (other._isBitmap)
+ _renderedText = other._renderedText;
+}
+
TextLabelElement::~TextLabelElement() {
}
@@ -1993,8 +2052,10 @@ void TextLabelElement::activate() {
if (textAsset->isBitmap()) {
_renderedText = textAsset->getBitmapSurface();
_needsRender = false;
+ _isBitmap = true;
} else {
_needsRender = true;
+ _isBitmap = false;
_text = textAsset->getString();
_macFormattingSpans = textAsset->getMacFormattingSpans();
}
@@ -2016,6 +2077,7 @@ void TextLabelElement::render(Window *window) {
if (_needsRender) {
_needsRender = false;
+ _isBitmap = false;
_renderedText.reset();
_renderedText.reset(new Graphics::ManagedSurface());
@@ -2165,6 +2227,7 @@ void TextLabelElement::render(Window *window) {
void TextLabelElement::setTextStyle(uint16 macFontID, const Common::String &fontFamilyName, uint size, TextAlignment alignment, const TextStyleFlags &styleFlags) {
if (!_text.empty()) {
_needsRender = true;
+ _isBitmap = false;
_contentsDirty = true;
}
@@ -2202,6 +2265,14 @@ Graphics::FontManager::FontUsage TextLabelElement::getDefaultUsageForNamedFont(c
return Graphics::FontManager::kGUIFont;
}
+Common::SharedPtr<Structural> TextLabelElement::shallowClone() const {
+ return Common::SharedPtr<Structural>(new TextLabelElement(*this));
+}
+
+void TextLabelElement::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ VisualElement::visitInternalReferences(visitor);
+}
+
MiniscriptInstructionOutcome TextLabelElement::scriptSetText(MiniscriptThread *thread, const DynamicValue &value) {
if (value.getType() != DynamicValueTypes::kString) {
thread->error("Tried to set a text label element's text to something that wasn't a string");
@@ -2210,6 +2281,7 @@ MiniscriptInstructionOutcome TextLabelElement::scriptSetText(MiniscriptThread *t
_text = value.getString();
_needsRender = true;
+ _isBitmap = false;
_contentsDirty = true;
_macFormattingSpans.clear();
@@ -2239,6 +2311,7 @@ MiniscriptInstructionOutcome TextLabelElement::scriptSetLine(MiniscriptThread *t
}
_needsRender = true;
+ _isBitmap = false;
_contentsDirty = true;
_macFormattingSpans.clear();
@@ -2295,6 +2368,18 @@ SoundElement::SoundElement()
_startTimestamp(0), _shouldPlayIfNotPaused(true), _needsReset(true) {
}
+SoundElement::SoundElement(const SoundElement &other)
+ : NonVisualElement(other), _leftVolume(other._leftVolume), _rightVolume(other._rightVolume), _balance(other._balance)
+ , _assetID(other._assetID), _cachedAudio(other._cachedAudio), _metadata(other._metadata), _player(nullptr)
+ , _startTime(other._startTime), _finishTime(other._finishTime), _startTimestamp(other._startTimestamp)
+ , _cueCheckTime(other._cueCheckTime), _shouldPlayIfNotPaused(other._shouldPlayIfNotPaused), _needsReset(true)
+ , _playMediaSignaller(nullptr), _subtitlePlayer(nullptr) {
+
+ _playMediaSignaller = other.getRuntime()->getProject()->notifyOnPlayMedia(this);
+
+ initSubtitles();
+}
+
SoundElement::~SoundElement() {
if (_playMediaSignaller)
_playMediaSignaller->removeReceiver(this);
@@ -2361,6 +2446,23 @@ VThreadState SoundElement::consumeCommand(Runtime *runtime, const Common::Shared
return NonVisualElement::consumeCommand(runtime, msg);
}
+void SoundElement::initSubtitles() {
+ Project *project = getRuntime()->getProject();
+
+ const SubtitleTables &subTables = project->getSubtitles();
+ if (subTables.assetMapping) {
+ const Common::String *subtitleSetIDPtr = subTables.assetMapping->findSubtitleSetForAssetID(_assetID);
+ if (!subtitleSetIDPtr) {
+ Common::String assetName = project->getAssetNameByID(_assetID);
+ if (assetName.size() > 0)
+ subtitleSetIDPtr = subTables.assetMapping->findSubtitleSetForAssetName(assetName);
+ }
+
+ if (subtitleSetIDPtr)
+ _subtitlePlayer.reset(new SubtitlePlayer(getRuntime(), *subtitleSetIDPtr, subTables));
+ }
+}
+
void SoundElement::activate() {
Project *project = getRuntime()->getProject();
Common::SharedPtr<Asset> asset = project->getAssetByID(_assetID).lock();
@@ -2383,18 +2485,7 @@ void SoundElement::activate() {
if (_name.empty())
_name = project->getAssetNameByID(_assetID);
- const SubtitleTables &subTables = project->getSubtitles();
- if (subTables.assetMapping) {
- const Common::String *subtitleSetIDPtr = subTables.assetMapping->findSubtitleSetForAssetID(_assetID);
- if (!subtitleSetIDPtr) {
- Common::String assetName = project->getAssetNameByID(_assetID);
- if (assetName.size() > 0)
- subtitleSetIDPtr = subTables.assetMapping->findSubtitleSetForAssetName(assetName);
- }
-
- if (subtitleSetIDPtr)
- _subtitlePlayer.reset(new SubtitlePlayer(getRuntime(), *subtitleSetIDPtr, subTables));
- }
+ initSubtitles();
}
@@ -2490,6 +2581,14 @@ bool SoundElement::resolveMediaMarkerLabel(const Label &label, int32 &outResolut
return false;
}
+Common::SharedPtr<Structural> SoundElement::shallowClone() const {
+ return Common::SharedPtr<Structural>(new SoundElement(*this));
+}
+
+void SoundElement::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ NonVisualElement::visitInternalReferences(visitor);
+}
+
void SoundElement::stopPlayer() {
_player.reset();
if (_subtitlePlayer)
diff --git a/engines/mtropolis/elements.h b/engines/mtropolis/elements.h
index 2ba0e30975e..1bcb9e44d42 100644
--- a/engines/mtropolis/elements.h
+++ b/engines/mtropolis/elements.h
@@ -64,12 +64,17 @@ public:
void render(Window *window) override;
+ Common::SharedPtr<Structural> shallowClone() const override;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "Graphic Element"; }
SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
#endif
private:
+ GraphicElement(const GraphicElement &other);
+
bool _cacheBitmap;
Common::SharedPtr<Graphics::ManagedSurface> _mask;
@@ -105,6 +110,9 @@ public:
void setResizeFilter(const Common::SharedPtr<MovieResizeFilter> &filter);
+ Common::SharedPtr<Structural> shallowClone() const override;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "Movie Element"; }
SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
@@ -195,6 +203,9 @@ public:
void render(Window *window) override;
+ Common::SharedPtr<Structural> shallowClone() const override;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "Image Element"; }
SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
@@ -235,6 +246,9 @@ public:
Common::Rect getRelativeCollisionRect() const override;
+ Common::SharedPtr<Structural> shallowClone() const override;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "mToon Element"; }
SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
@@ -242,6 +256,8 @@ public:
#endif
private:
+ MToonElement(const MToonElement &other);
+
struct StartPlayingTaskData {
StartPlayingTaskData() : runtime(nullptr) {}
@@ -336,12 +352,17 @@ public:
Graphics::FontManager::FontUsage getDefaultUsageForMacFont(uint16 macFontID, uint size);
Graphics::FontManager::FontUsage getDefaultUsageForNamedFont(const Common::String &fontFamilyName, uint size);
+ Common::SharedPtr<Structural> shallowClone() const override;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "Text Label Element"; }
SupportStatus debugGetSupportStatus() const override { return kSupportStatusPartial; }
#endif
private:
+ TextLabelElement(const TextLabelElement &other);
+
struct TextLabelLineWriteInterface {
static MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset);
static MiniscriptInstructionOutcome refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib);
@@ -357,7 +378,7 @@ private:
bool _cacheBitmap;
bool _needsRender;
- //bool _isBitmap;
+ bool _isBitmap;
uint32 _assetID;
Common::String _text;
@@ -396,6 +417,9 @@ public:
bool resolveMediaMarkerLabel(const Label &label, int32 &outResolution) const override;
+ Common::SharedPtr<Structural> shallowClone() const override;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "Sound Element"; }
SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
@@ -403,6 +427,9 @@ public:
#endif
private:
+ SoundElement(const SoundElement &other);
+
+ void initSubtitles();
void stopPlayer();
MiniscriptInstructionOutcome scriptSetLoop(MiniscriptThread *thread, const DynamicValue &value);
diff --git a/engines/mtropolis/hacks.cpp b/engines/mtropolis/hacks.cpp
index 96164220414..3b52204a134 100644
--- a/engines/mtropolis/hacks.cpp
+++ b/engines/mtropolis/hacks.cpp
@@ -45,6 +45,7 @@ Hacks::Hacks() {
mtiVariableReferencesHack = false;
mtiSceneReturnHack = false;
mtiHispaniolaDamagedStringHack = false;
+ ignoreSceneUnloads = false;
}
Hacks::~Hacks() {
@@ -1172,6 +1173,7 @@ void SPQRSoundPreloadHooks::onProjectStarted(Runtime *runtime) {
void addSPQRQuirks(const MTropolisGameDescription &desc, Hacks &hacks) {
hacks.addSceneTransitionHooks(Common::SharedPtr<SceneTransitionHooks>(new SPQRSoundPreloadHooks()));
+ hacks.ignoreSceneUnloads = true;
}
} // End of namespace HackSuites
diff --git a/engines/mtropolis/hacks.h b/engines/mtropolis/hacks.h
index c4acd69ac58..96a05c73683 100644
--- a/engines/mtropolis/hacks.h
+++ b/engines/mtropolis/hacks.h
@@ -56,6 +56,8 @@ struct Hacks {
bool mtiSceneReturnHack;
bool mtiHispaniolaDamagedStringHack;
+ bool ignoreSceneUnloads;
+
uint midiVolumeScale; // 256 = 1.0
uint32 minTransitionDuration;
diff --git a/engines/mtropolis/modifiers.cpp b/engines/mtropolis/modifiers.cpp
index 055839b80eb..be0470eaf79 100644
--- a/engines/mtropolis/modifiers.cpp
+++ b/engines/mtropolis/modifiers.cpp
@@ -103,6 +103,15 @@ void BehaviorModifier::appendModifier(const Common::SharedPtr<Modifier> &modifie
modifier->setParent(getSelfReference());
}
+void BehaviorModifier::removeModifier(const Modifier *modifier) {
+ for (Common::Array<Common::SharedPtr<Modifier> >::iterator it = _children.begin(), itEnd = _children.end(); it != itEnd; ++it) {
+ if (it->get() == modifier) {
+ _children.erase(it);
+ return;
+ }
+ }
+}
+
IModifierContainer *BehaviorModifier::getMessagePropagationContainer() {
if (_isEnabled)
return this;
@@ -2818,6 +2827,15 @@ void CompoundVariableModifier::appendModifier(const Common::SharedPtr<Modifier>
modifier->setParent(getSelfReference());
}
+void CompoundVariableModifier::removeModifier(const Modifier *modifier) {
+ for (Common::Array<Common::SharedPtr<Modifier> >::iterator it = _children.begin(), itEnd = _children.end(); it != itEnd; ++it) {
+ if (it->get() == modifier) {
+ _children.erase(it);
+ return;
+ }
+ }
+}
+
void CompoundVariableModifier::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
for (Common::Array<Common::SharedPtr<Modifier> >::iterator it = _children.begin(), itEnd = _children.end(); it != itEnd; ++it) {
visitor->visitChildModifierRef(*it);
diff --git a/engines/mtropolis/modifiers.h b/engines/mtropolis/modifiers.h
index 1545c6a2ca3..ce3590b1c27 100644
--- a/engines/mtropolis/modifiers.h
+++ b/engines/mtropolis/modifiers.h
@@ -48,6 +48,7 @@ public:
const Common::Array<Common::SharedPtr<Modifier> > &getModifiers() const override;
void appendModifier(const Common::SharedPtr<Modifier> &modifier) override;
+ void removeModifier(const Modifier *modifier) override;
IModifierContainer *getMessagePropagationContainer() override;
IModifierContainer *getChildContainer() override;
@@ -1152,6 +1153,7 @@ private:
const Common::Array<Common::SharedPtr<Modifier> > &getModifiers() const override;
void appendModifier(const Common::SharedPtr<Modifier> &modifier) override;
+ void removeModifier(const Modifier *modifier) override;
void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) override;
diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp
index a6b69025d96..2f993e2d36d 100644
--- a/engines/mtropolis/runtime.cpp
+++ b/engines/mtropolis/runtime.cpp
@@ -21,6 +21,7 @@
#include "common/debug.h"
#include "common/file.h"
+#include "common/hash-ptr.h"
#include "common/macresman.h"
#include "common/random.h"
#include "common/substream.h"
@@ -253,6 +254,130 @@ void ModifierChildCloner::visitWeakModifierRef(Common::WeakPtr<Modifier> &modifi
// Do nothing
}
+class ObjectCloner : public IStructuralReferenceVisitor {
+public:
+ ObjectCloner(Runtime *runtime, const Common::WeakPtr<RuntimeObject> &relinkParent, Common::HashMap<RuntimeObject *, RuntimeObject *> *objectRemaps);
+
+ void visitChildStructuralRef(Common::SharedPtr<Structural> &structural) override;
+ void visitChildModifierRef(Common::SharedPtr<Modifier> &modifier) override;
+ void visitWeakStructuralRef(Common::WeakPtr<Structural> &structural) override;
+ void visitWeakModifierRef(Common::WeakPtr<Modifier> &modifier) override;
+
+private:
+ Runtime *_runtime;
+ Common::WeakPtr<RuntimeObject> _relinkParent;
+
+ Common::HashMap<RuntimeObject *, RuntimeObject *> *_objectRemaps;
+};
+
+ObjectCloner::ObjectCloner(Runtime *runtime, const Common::WeakPtr<RuntimeObject> &relinkParent, Common::HashMap<RuntimeObject *, RuntimeObject *> *objectRemaps)
+ : _runtime(runtime), _relinkParent(relinkParent), _objectRemaps(objectRemaps) {
+}
+
+void ObjectCloner::visitChildStructuralRef(Common::SharedPtr<Structural> &structuralRef) {
+ uint32 oldGUID = structuralRef->getStaticGUID();
+ Common::SharedPtr<Structural> cloned = structuralRef->shallowClone();
+ assert(cloned->getStaticGUID() == oldGUID);
+
+ (void)oldGUID;
+
+ if (_objectRemaps)
+ (*_objectRemaps)[structuralRef.get()] = cloned.get();
+
+ assert(!_relinkParent.expired() && _relinkParent.lock()->isStructural());
+
+ cloned->setSelfReference(cloned);
+ cloned->setRuntimeGUID(_runtime->allocateRuntimeGUID());
+ cloned->setParent(static_cast<Structural *>(_relinkParent.lock().get()));
+
+ ObjectCloner recursiveCloner(_runtime, cloned, _objectRemaps);
+ cloned->visitInternalReferences(&recursiveCloner);
+
+ structuralRef = cloned;
+}
+
+void ObjectCloner::visitChildModifierRef(Common::SharedPtr<Modifier> &modifierRef) {
+ uint32 oldGUID = modifierRef->getStaticGUID();
+ Common::SharedPtr<Modifier> cloned = modifierRef->shallowClone();
+ assert(cloned->getStaticGUID() == oldGUID);
+
+ (void)oldGUID;
+
+ if (_objectRemaps)
+ (*_objectRemaps)[modifierRef.get()] = cloned.get();
+
+ cloned->setSelfReference(cloned);
+ cloned->setRuntimeGUID(_runtime->allocateRuntimeGUID());
+ cloned->setParent(_relinkParent);
+
+ ObjectCloner recursiveCloner(_runtime, cloned, _objectRemaps);
+ cloned->visitInternalReferences(&recursiveCloner);
+
+ modifierRef = cloned;
+}
+
+void ObjectCloner::visitWeakStructuralRef(Common::WeakPtr<Structural> &structural) {
+ // Do nothing
+}
+
+void ObjectCloner::visitWeakModifierRef(Common::WeakPtr<Modifier> &modifier) {
+ // Do nothing
+}
+
+
+
+class ObjectRefRemapper : public IStructuralReferenceVisitor {
+public:
+ explicit ObjectRefRemapper(const Common::HashMap<RuntimeObject *, RuntimeObject *> &objectRemaps);
+
+ void visitChildStructuralRef(Common::SharedPtr<Structural> &structural) override;
+ void visitChildModifierRef(Common::SharedPtr<Modifier> &modifier) override;
+ void visitWeakStructuralRef(Common::WeakPtr<Structural> &structural) override;
+ void visitWeakModifierRef(Common::WeakPtr<Modifier> &modifier) override;
+
+private:
+ const Common::HashMap<RuntimeObject *, RuntimeObject *> &_objectRemaps;
+};
+
+ObjectRefRemapper::ObjectRefRemapper(const Common::HashMap<RuntimeObject *, RuntimeObject *> &objectRemaps) : _objectRemaps(objectRemaps) {
+}
+
+void ObjectRefRemapper::visitChildStructuralRef(Common::SharedPtr<Structural> &structural) {
+ RuntimeObject *obj = structural.get();
+ if (obj) {
+ Common::HashMap<RuntimeObject *, RuntimeObject *> ::const_iterator it = _objectRemaps.find(obj);
+ if (it != _objectRemaps.end())
+ structural = it->_value->getSelfReference().lock().staticCast<Structural>();
+ }
+}
+
+void ObjectRefRemapper::visitChildModifierRef(Common::SharedPtr<Modifier> &modifier) {
+ RuntimeObject *obj = modifier.get();
+ if (obj) {
+ Common::HashMap<RuntimeObject *, RuntimeObject *>::const_iterator it = _objectRemaps.find(obj);
+ if (it != _objectRemaps.end())
+ modifier = it->_value->getSelfReference().lock().staticCast<Modifier>();
+ }
+}
+
+void ObjectRefRemapper::visitWeakStructuralRef(Common::WeakPtr<Structural> &structural) {
+ RuntimeObject *obj = structural.lock().get();
+ if (obj) {
+ Common::HashMap<RuntimeObject *, RuntimeObject *>::const_iterator it = _objectRemaps.find(obj);
+ if (it != _objectRemaps.end())
+ structural = it->_value->getSelfReference().staticCast<Structural>();
+ }
+}
+
+void ObjectRefRemapper::visitWeakModifierRef(Common::WeakPtr<Modifier> &modifier) {
+ RuntimeObject *obj = modifier.lock().get();
+ if (obj) {
+ Common::HashMap<RuntimeObject *, RuntimeObject *>::const_iterator it = _objectRemaps.find(obj);
+ if (it != _objectRemaps.end())
+ modifier = it->_value->getSelfReference().staticCast<Modifier>();
+ }
+}
+
char invariantToLower(char c) {
if (c >= 'A' && c <= 'Z')
return static_cast<char>(c - 'A' + 'a');
@@ -2645,6 +2770,15 @@ void SimpleModifierContainer::appendModifier(const Common::SharedPtr<Modifier> &
modifier->setParent(nullptr);
}
+void SimpleModifierContainer::removeModifier(const Modifier *modifier) {
+ for (Common::Array<Common::SharedPtr<Modifier> >::iterator it = _modifiers.begin(), itEnd = _modifiers.end(); it != itEnd; ++it) {
+ if (it->get() == modifier) {
+ _modifiers.erase(it);
+ return;
+ }
+ }
+}
+
void SimpleModifierContainer::clear() {
_modifiers.clear();
}
@@ -2729,6 +2863,23 @@ MiniscriptInstructionOutcome RuntimeObject::writeRefAttribute(MiniscriptThread *
}
}
+ if (attrib == "clone") {
+ DynamicValueWriteFuncHelper<RuntimeObject, &RuntimeObject::scriptSetClone, false>::create(this, writeProxy);
+ return kMiniscriptInstructionOutcomeContinue;
+ }
+
+ if (attrib == "kill") {
+ DynamicValueWriteFuncHelper<RuntimeObject, &RuntimeObject::scriptSetKill, false>::create(this, writeProxy);
+ return kMiniscriptInstructionOutcomeContinue;
+ }
+
+ if (attrib == "parent") {
+ writeProxy.pod.ifc = DynamicValueWriteInterfaceGlue<ParentWriteProxyInterface>::getInstance();
+ writeProxy.pod.objectRef = this;
+ writeProxy.pod.ptrOrOffset = 0;
+ return kMiniscriptInstructionOutcomeContinue;
+ }
+
return kMiniscriptInstructionOutcomeFailed;
}
@@ -2736,6 +2887,46 @@ MiniscriptInstructionOutcome RuntimeObject::writeRefAttributeIndexed(MiniscriptT
return kMiniscriptInstructionOutcomeFailed;
}
+MiniscriptInstructionOutcome RuntimeObject::scriptSetClone(MiniscriptThread *thread, const DynamicValue &value) {
+ thread->getRuntime()->queueCloneObject(this->getSelfReference());
+ return kMiniscriptInstructionOutcomeContinue;
+}
+
+MiniscriptInstructionOutcome RuntimeObject::scriptSetKill(MiniscriptThread *thread, const DynamicValue &value) {
+ thread->getRuntime()->queueKillObject(this->getSelfReference());
+ return kMiniscriptInstructionOutcomeContinue;
+}
+
+MiniscriptInstructionOutcome RuntimeObject::scriptSetParent(MiniscriptThread *thread, const DynamicValue &value) {
+ if (value.getType() != DynamicValueTypes::kObject) {
+ thread->error("Object couldn't be re-parented to a non-object");
+ return kMiniscriptInstructionOutcomeFailed;
+ }
+
+ thread->getRuntime()->queueChangeObjectParent(this->getSelfReference(), value.getObject().object);
+
+ return kMiniscriptInstructionOutcomeContinue;
+}
+
+// Need special handling of "parent" property, assigns indirect the value but writes re-parent the object
+MiniscriptInstructionOutcome RuntimeObject::ParentWriteProxyInterface::write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset) {
+ return static_cast<RuntimeObject *>(objectRef)->scriptSetParent(thread, dest);
+}
+
+MiniscriptInstructionOutcome RuntimeObject::ParentWriteProxyInterface::refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib) {
+ DynamicValueWriteProxy tempProxy;
+ DynamicValueWriteObjectHelper::create(static_cast<RuntimeObject *>(objectRef), tempProxy);
+
+ return tempProxy.pod.ifc->refAttrib(thread, proxy, tempProxy.pod.objectRef, tempProxy.pod.ptrOrOffset, attrib);
+}
+
+MiniscriptInstructionOutcome RuntimeObject::ParentWriteProxyInterface::refAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index) {
+ DynamicValueWriteProxy tempProxy;
+ DynamicValueWriteObjectHelper::create(static_cast<RuntimeObject *>(objectRef), tempProxy);
+
+ return tempProxy.pod.ifc->refAttribIndexed(thread, proxy, tempProxy.pod.objectRef, tempProxy.pod.ptrOrOffset, attrib, index);
+}
+
MessageProperties::MessageProperties(const Event &evt, const DynamicValue &value, const Common::WeakPtr<RuntimeObject> &source)
: _evt(evt), _value(value), _source(source) {
}
@@ -3089,6 +3280,11 @@ Structural::Structural() : Structural(nullptr) {
Structural::Structural(Runtime *runtime) : _parent(nullptr), _paused(false), _loop(false), _flushPriority(0), _runtime(runtime) {
}
+Structural::Structural(const Structural &other)
+ : RuntimeObject(other), _parent(other._parent), _children(other._children), _modifiers(other._modifiers), _name(other._name), _assets(other._assets)
+ , _paused(other._paused), _loop(other._loop), _flushPriority(other._flushPriority)/*, _hooks(other._hooks)*/, _runtime(other._runtime) {
+}
+
Structural::~Structural() {
}
@@ -3100,6 +3296,14 @@ const Common::SharedPtr<StructuralHooks> &Structural::getHooks() const {
return _hooks;
}
+void Structural::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ for (Common::SharedPtr<Structural> &child : _children)
+ visitor->visitChildStructuralRef(child);
+
+ for (Common::SharedPtr<Modifier> &child : _modifiers)
+ visitor->visitChildModifierRef(child);
+}
+
bool Structural::isStructural() const {
return true;
}
@@ -3298,15 +3502,6 @@ MiniscriptInstructionOutcome Structural::writeRefAttribute(MiniscriptThread *thr
} else if (attrib == "system") {
DynamicValueWriteObjectHelper::create(thread->getRuntime()->getSystemInterface(), result);
return kMiniscriptInstructionOutcomeContinue;
- } else if (attrib == "parent") {
- // NOTE: Re-parenting objects is allowed by mTropolis but we don't currently support that.
- Structural *parent = getParent();
- if (parent) {
- DynamicValueWriteObjectHelper::create(getParent(), result);
- return kMiniscriptInstructionOutcomeContinue;
- } else {
- return kMiniscriptInstructionOutcomeFailed;
- }
} else if (attrib == "next") {
Structural *sibling = findNextSibling();
if (sibling) {
@@ -3488,6 +3683,15 @@ void Structural::appendModifier(const Common::SharedPtr<Modifier> &modifier) {
modifier->setParent(getSelfReference());
}
+void Structural::removeModifier(const Modifier *modifier) {
+ for (Common::Array<Common::SharedPtr<Modifier> >::iterator it = _modifiers.begin(), itEnd = _modifiers.end(); it != itEnd; ++it) {
+ if (it->get() == modifier) {
+ _modifiers.erase(it);
+ return;
+ }
+ }
+}
+
bool Structural::respondsToEvent(const Event &evt) const {
return false;
}
@@ -3862,6 +4066,10 @@ HighLevelSceneTransition::HighLevelSceneTransition(const Common::SharedPtr<Struc
: scene(hlst_scene), type(hlst_type), addToDestinationScene(hlst_addToDestinationScene), addToReturnList(hlst_addToReturnList) {
}
+ObjectParentChange::ObjectParentChange(const Common::WeakPtr<RuntimeObject> &object, const Common::WeakPtr<RuntimeObject> &newParent)
+ : _object(object), _newParent(newParent) {
+}
+
SceneTransitionEffect::SceneTransitionEffect()
: _duration(100000), _steps(64), _transitionType(SceneTransitionTypes::kNone), _transitionDirection(SceneTransitionDirections::kUp) {
}
@@ -4635,6 +4843,57 @@ bool Runtime::runFrame() {
continue;
}
+ // The order of operations for dynamic object behaviors is:
+ // - Parent changes
+ // - Parent Enabled -> Clone for each cloned object
+ // - Show for each cloned object that is visible
+ // - Hide -> Kill -> Parent Disabled for each killed object
+ if (_pendingParentChanges.size() > 0) {
+ ObjectParentChange parentChange = _pendingParentChanges.remove_at(0);
+
+ RuntimeObject *obj = parentChange._object.lock().get();
+ RuntimeObject *newParent = parentChange._newParent.lock().get();
+
+ if (obj) {
+ if (newParent)
+ executeChangeObjectParent(obj, newParent);
+ else
+ warning("Object re-parenting failed, the new parent was invalid!");
+ }
+
+ continue;
+ }
+
+ if (_pendingClones.size() > 0) {
+ RuntimeObject *objectToClone = _pendingClones.remove_at(0).lock().get();
+
+ if (objectToClone)
+ executeCloneObject(objectToClone);
+
+ continue;
+ }
+
+ if (_pendingShowClonedObject.size() > 0) {
+ Structural *objectToShow = _pendingShowClonedObject.remove_at(0).lock().get();
+
+ if (objectToShow && objectToShow->isElement() && static_cast<Element *>(objectToShow)->isVisual() && static_cast<VisualElement *>(objectToShow)->isVisible()) {
+ Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event(EventIDs::kElementShow, 0), DynamicValue(), objectToShow->getSelfReference()));
+ Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, objectToShow, false, true, false));
+ sendMessageOnVThread(dispatch);
+ }
+
+ continue;
+ }
+
+ if (_pendingKills.size() > 0) {
+ RuntimeObject *objectToKill = _pendingKills.remove_at(0).lock().get();
+
+ if (objectToKill)
+ executeKillObject(objectToKill);
+
+ continue;
+ }
+
// Teardowns must only occur during idle conditions where there are no VThread tasks
if (_pendingTeardowns.size() > 0) {
for (Common::Array<Teardown>::const_iterator it = _pendingTeardowns.begin(), itEnd = _pendingTeardowns.end(); it != itEnd; ++it) {
@@ -4945,29 +5204,42 @@ Common::SharedPtr<Structural> Runtime::findDefaultSharedSceneForScene(Structural
}
void Runtime::executeTeardown(const Teardown &teardown) {
- Common::SharedPtr<Structural> structural = teardown.structural.lock();
- if (!structural)
- return; // Already gone
+ if (Common::SharedPtr<Structural> structural = teardown.structural.lock()) {
+ recursiveDeactivateStructural(structural.get());
- recursiveDeactivateStructural(structural.get());
+ if (teardown.onlyRemoveChildren) {
+ structural->removeAllChildren();
+ structural->removeAllModifiers();
+ structural->removeAllAssets();
+ } else {
+ Structural *parent = structural->getParent();
- if (teardown.onlyRemoveChildren) {
- structural->removeAllChildren();
- structural->removeAllModifiers();
- structural->removeAllAssets();
- } else {
- Structural *parent = structural->getParent();
+ // Nothing should be holding strong references to structural objects after they're removed from the project
+ assert(parent != nullptr);
- // Nothing should be holding strong references to structural objects after they're removed from the project
- assert(parent != nullptr);
+ if (!parent) {
+ return; // Already unlinked but still alive somehow
+ }
- if (!parent) {
- return; // Already unlinked but still alive somehow
+ parent->removeChild(structural.get());
+
+ structural->setParent(nullptr);
}
+ }
+
+ if (Common::SharedPtr<Modifier> modifier = teardown.modifier.lock()) {
+ IModifierContainer *container = nullptr;
+ RuntimeObject *parent = modifier->getParent().lock().get();
- parent->removeChild(structural.get());
+ if (parent) {
+ if (parent->isStructural())
+ container = static_cast<Structural *>(parent);
+ else if (parent->isModifier())
+ container = static_cast<Modifier *>(parent)->getChildContainer();
+ }
- structural->setParent(nullptr);
+ if (container)
+ container->removeModifier(modifier.get());
}
}
@@ -5020,6 +5292,190 @@ void Runtime::executeSceneChangeRecursiveVisibilityChange(Structural *structural
}
}
+void Runtime::executeChangeObjectParent(RuntimeObject *object, RuntimeObject *newParent) {
+ // TODO: Should do circularity checks
+
+ if (object->isModifier()) {
+ Common::SharedPtr<Modifier> modifier = object->getSelfReference().lock().staticCast<Modifier>();
+
+ IModifierContainer *oldParentContainer = nullptr;
+
+ RuntimeObject *oldParent = modifier->getParent().lock().get();
+
+ if (oldParent == newParent)
+ return;
+
+ if (oldParent->isStructural())
+ oldParentContainer = static_cast<Structural *>(oldParent);
+ else if (oldParent->isModifier())
+ oldParentContainer = static_cast<Modifier *>(oldParent)->getChildContainer();
+
+ IModifierContainer *newParentContainer = nullptr;
+ if (newParent->isStructural())
+ newParentContainer = static_cast<Structural *>(newParent);
+ else if (newParent->isModifier())
+ newParentContainer = static_cast<Modifier *>(newParent)->getChildContainer();
+
+ if (!newParentContainer) {
+ warning("Object re-parent failed, the new parent isn't a modifier container");
+ return;
+ }
+
+ oldParentContainer->removeModifier(modifier.get());
+ newParentContainer->appendModifier(modifier);
+
+ modifier->setParent(newParent->getSelfReference());
+
+ {
+ Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event(EventIDs::kParentChanged, 0), DynamicValue(), modifier->getSelfReference()));
+ Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, modifier.get(), false, false, false));
+ sendMessageOnVThread(dispatch);
+ }
+ }
+
+ if (object->isStructural()) {
+ Common::SharedPtr<Structural> structural = object->getSelfReference().lock().staticCast<Structural>();
+
+ Structural *oldParent = structural->getParent();
+
+ if (oldParent == newParent)
+ return;
+
+ if (!newParent->isStructural()) {
+ warning("Object re-parent failed, the new parent isn't structural");
+ return;
+ }
+
+ Structural *newParentStructural = static_cast<Structural *>(newParent);
+
+ oldParent->removeChild(structural.get());
+ newParentStructural->addChild(structural);
+
+ {
+ Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event(EventIDs::kParentChanged, 0), DynamicValue(), structural->getSelfReference()));
+ Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, structural.get(), false, true, false));
+ sendMessageOnVThread(dispatch);
+ }
+ }
+}
+
+void Runtime::executeCloneObject(RuntimeObject *object) {
+ Common::HashMap<RuntimeObject *, RuntimeObject *> objectRemaps;
+
+ if (object->isModifier()) {
+ Common::SharedPtr<Modifier> modifierRef = object->getSelfReference().lock().staticCast<Modifier>();
+ Common::WeakPtr<RuntimeObject> relinkParent = modifierRef->getParent();
+
+ ObjectCloner cloner(this, relinkParent, &objectRemaps);
+ cloner.visitChildModifierRef(modifierRef);
+
+ ObjectRefRemapper remapper(objectRemaps);
+ remapper.visitChildModifierRef(modifierRef);
+
+ IModifierContainer *container = nullptr;
+ Common::SharedPtr<RuntimeObject> parent = relinkParent.lock();
+ if (parent) {
+ if (parent->isStructural())
+ container = static_cast<Structural *>(parent.get());
+ else if (parent->isModifier())
+ container = static_cast<Modifier *>(parent.get())->getChildContainer();
+ }
+
+ if (container)
+ container->appendModifier(modifierRef);
+ else
+ error("Internal error: Cloned a modifier, but the parent isn't a modifeir container");
+
+ {
+ Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event(EventIDs::kClone, 0), DynamicValue(), modifierRef));
+ Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, modifierRef.get(), true, true, false));
+ sendMessageOnVThread(dispatch);
+ }
+
+ {
+ Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event(EventIDs::kParentEnabled, 0), DynamicValue(), modifierRef));
+ Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, modifierRef.get(), true, true, false));
+ sendMessageOnVThread(dispatch);
+ }
+ } else if (object->isStructural()) {
+ Common::SharedPtr<Structural> structuralRef = object->getSelfReference().lock().staticCast<Structural>();
+ Common::WeakPtr<RuntimeObject> relinkParent = structuralRef->getParent()->getSelfReference();
+
+ ObjectCloner cloner(this, relinkParent, &objectRemaps);
+ cloner.visitChildStructuralRef(structuralRef);
+
+ ObjectRefRemapper remapper(objectRemaps);
+ remapper.visitChildStructuralRef(structuralRef);
+
+ structuralRef->getParent()->addChild(structuralRef);
+
+ _pendingPostCloneShowChecks.push_back(structuralRef);
+
+ {
+ Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event(EventIDs::kClone, 0), DynamicValue(), structuralRef));
+ Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, structuralRef.get(), true, true, false));
+ sendMessageOnVThread(dispatch);
+ }
+
+ {
+ Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event(EventIDs::kParentEnabled, 0), DynamicValue(), structuralRef));
+ Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, structuralRef.get(), true, true, false));
+ sendMessageOnVThread(dispatch);
+ }
+ } else
+ error("Internal error: Cloned something unusual");
+}
+
+void Runtime::executeKillObject(RuntimeObject *object) {
+ // TODO: Should do circularity checks
+
+ if (object->isModifier()) {
+ Modifier *modifier = static_cast<Modifier *>(object);
+
+ {
+ Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event(EventIDs::kParentDisabled, 0), DynamicValue(), modifier->getSelfReference()));
+ Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, modifier, true, true, false));
+ sendMessageOnVThread(dispatch);
+ }
+
+ {
+ Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event(EventIDs::kKill, 0), DynamicValue(), modifier->getSelfReference()));
+ Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, modifier, true, true, false));
+ sendMessageOnVThread(dispatch);
+ }
+
+ Teardown teardown;
+ teardown.modifier = modifier->getSelfReference().lock().staticCast<Modifier>();
+
+ _pendingTeardowns.push_back(teardown);
+ }
+
+ if (object->isStructural()) {
+ Structural *structural = static_cast<Structural *>(object);
+
+ // Task order is LIFO, so order is Hide -> Kill -> Parent Disabled
+ {
+ Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event(EventIDs::kParentDisabled, 0), DynamicValue(), structural->getSelfReference()));
+ Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, structural, true, true, false));
+ sendMessageOnVThread(dispatch);
+ }
+
+ {
+ Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event(EventIDs::kKill, 0), DynamicValue(), structural->getSelfReference()));
+ Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, structural, true, true, false));
+ sendMessageOnVThread(dispatch);
+ }
+
+ if (structural->isElement() && static_cast<Element *>(structural)->isVisual())
+ static_cast<VisualElement *>(structural)->pushVisibilityChangeTask(this, false);
+
+ Teardown teardown;
+ teardown.structural = structural->getSelfReference().lock().staticCast<Structural>();
+
+ _pendingTeardowns.push_back(teardown);
+ }
+}
+
void Runtime::executeCompleteTransitionToScene(const Common::SharedPtr<Structural> &targetScene) {
// NOTE: Transitioning to the same scene is allowed, Obsidian relies on this to avoid getting stuck
// after going up the wrong side in the Bureau chapter final area (i.e. after reaching the sky face).
@@ -6665,6 +7121,21 @@ bool Runtime::isIdle() const {
if (_pendingLowLevelTransitions.size() > 0)
return false;
+ if (_pendingClones.size() > 0)
+ return false;
+
+ if (_pendingPostCloneShowChecks.size() > 0)
+ return false;
+
+ if (_pendingShowClonedObject.size() > 0)
+ return false;
+
+ if (_pendingParentChanges.size() > 0)
+ return false;
+
+ if (_pendingKills.size() > 0)
+ return false;
+
if (_messageQueue.size() > 0)
return false;
@@ -6681,6 +7152,31 @@ const Common::SharedPtr<SubtitleRenderer> &Runtime::getSubtitleRenderer() const
return _subtitleRenderer;
}
+void Runtime::queueCloneObject(const Common::WeakPtr<RuntimeObject> &obj) {
+ Common::SharedPtr<RuntimeObject> ptr = obj.lock();
+
+ // Cloning the same object multiple times doesn't work
+ for (const Common::WeakPtr<RuntimeObject> &candidate : _pendingClones)
+ if (candidate.lock() == ptr)
+ return;
+
+ _pendingClones.push_back(obj);
+}
+
+void Runtime::queueKillObject(const Common::WeakPtr<RuntimeObject> &obj) {
+ Common::SharedPtr<RuntimeObject> ptr = obj.lock();
+
+ for (const Common::WeakPtr<RuntimeObject> &candidate : _pendingKills)
+ if (candidate.lock() == ptr)
+ return;
+
+ _pendingKills.push_back(obj);
+}
+
+void Runtime::queueChangeObjectParent(const Common::WeakPtr<RuntimeObject> &obj, const Common::WeakPtr<RuntimeObject> &newParent) {
+ _pendingParentChanges.push_back(ObjectParentChange(obj, newParent));
+}
+
void Runtime::ensureMainWindowExists() {
// Maybe there's a better spot for this
if (_mainWindow.expired() && _project) {
@@ -7709,6 +8205,15 @@ ProjectPlatform Project::getPlatform() const {
return _platform;
}
+void Project::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ error("Cloning a project is not supported");
+}
+
+Common::SharedPtr<Structural> Project::shallowClone() const {
+ error("Cloning a project is not supported");
+ return nullptr;
+}
+
void Project::loadPresentationSettings(const Data::PresentationSettings &presentationSettings) {
_presentationSettings.bitsPerPixel = presentationSettings.bitsPerPixel;
if (_presentationSettings.bitsPerPixel != 8 && _presentationSettings.bitsPerPixel != 16) {
@@ -8143,6 +8648,15 @@ bool Section::isSection() const {
return true;
}
+Common::SharedPtr<Structural> Section::shallowClone() const {
+ error("Cloning sections is not supported");
+ return nullptr;
+}
+
+void Section::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ error("Cloning sections is not supported");
+}
+
ObjectLinkingScope *Section::getPersistentStructuralScope() {
return &_structuralScope;
}
@@ -8166,6 +8680,15 @@ bool Subsection::isSubsection() const {
return true;
}
+Common::SharedPtr<Structural> Subsection::shallowClone() const {
+ error("Cloning subsections is not supported");
+ return nullptr;
+}
+
+void Subsection::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ error("Cloning subsections is not supported");
+}
+
ObjectLinkingScope *Subsection::getSceneLoadMaterializeScope() {
return getPersistentStructuralScope();
}
@@ -8181,6 +8704,12 @@ ObjectLinkingScope *Subsection::getPersistentModifierScope() {
Element::Element() : _streamLocator(0), _sectionID(0), _haveCheckedAutoPlay(false) {
}
+Element::Element(const Element &other)
+ : Structural(other), _streamLocator(other._streamLocator), _sectionID(other._sectionID)
+ // Don't copy checked autoplay or mediacues lists
+ , _haveCheckedAutoPlay(false), _mediaCues() {
+}
+
bool Element::canAutoPlay() const {
return false;
}
@@ -8227,6 +8756,10 @@ bool Element::resolveMediaMarkerLabel(const Label& label, int32 &outResolution)
return false;
}
+void Element::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ Structural::visitInternalReferences(visitor);
+}
+
VisualElementTransitionProperties::VisualElementTransitionProperties() : _isDirty(true), _alpha(255) {
}
@@ -8363,6 +8896,14 @@ VisualElement::VisualElement()
_topLeftBevelShading(0), _bottomRightBevelShading(0), _interiorShading(0), _bevelSize(0) {
}
+VisualElement::VisualElement(const VisualElement &other)
+ : Element(other), _directToScreen(other._directToScreen), _visible(other._visible), _visibleByDefault(other._visibleByDefault)
+ , _rect(other._rect), _cachedAbsoluteOrigin(other._cachedAbsoluteOrigin), _layer(other._layer), _topLeftBevelShading(other._topLeftBevelShading)
+ , _bottomRightBevelShading(other._bottomRightBevelShading), _interiorShading(other._interiorShading), _bevelSize(other._bevelSize)
+ , _dragProps(nullptr), _renderProps(other._renderProps), _primaryGraphicModifier(nullptr), _transitionProps(other._transitionProps)
+ , _palette(other._palette), _prevRect(other._prevRect), _contentsDirty(true) {
+}
+
bool VisualElement::isVisual() const {
return true;
}
@@ -8655,6 +9196,12 @@ MiniscriptInstructionOutcome VisualElement::writeRefAttribute(MiniscriptThread *
// Not sure what this does, MTI uses it frequently
DynamicValueWriteDiscardHelper::create(writeProxy);
return kMiniscriptInstructionOutcomeContinue;
+ } else if (attrib == "unload") {
+ if (getRuntime()->getHacks().ignoreSceneUnloads) {
+ DynamicValueWriteDiscardHelper::create(writeProxy);
+ return kMiniscriptInstructionOutcomeContinue;
+ } else
+ return kMiniscriptInstructionOutcomeFailed;
}
return Element::writeRefAttribute(thread, writeProxy, attrib);
@@ -8811,6 +9358,17 @@ const Common::SharedPtr<Palette> &VisualElement::getPalette() const {
return _palette;
}
+void VisualElement::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ Element::visitInternalReferences(visitor);
+}
+
+
+void VisualElement::pushVisibilityChangeTask(Runtime * runtime, bool desiredVisibility) {
+ ChangeFlagTaskData *changeVisibilityTask = runtime->getVThread().pushTask("VisualElement::changeVisibilityTask", this, &VisualElement::changeVisibilityTask);
+ changeVisibilityTask->desiredFlag = true;
+ changeVisibilityTask->runtime = runtime;
+}
+
#ifdef MTROPOLIS_DEBUG_ENABLE
void VisualElement::debugInspect(IDebugInspectionReport *report) const {
report->declareDynamic("layer", Common::String::format("%i", static_cast<int>(_layer)));
diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h
index ac6f2d1e8da..65e1730f5dc 100644
--- a/engines/mtropolis/runtime.h
+++ b/engines/mtropolis/runtime.h
@@ -1354,6 +1354,13 @@ private:
Common::SharedPtr<MessageDispatch> _msg;
};
+struct ObjectParentChange {
+ explicit ObjectParentChange(const Common::WeakPtr<RuntimeObject> &object, const Common::WeakPtr<RuntimeObject> &newParent);
+
+ Common::WeakPtr<RuntimeObject> _object;
+ Common::WeakPtr<RuntimeObject> _newParent;
+};
+
struct HighLevelSceneTransition {
enum Type {
kTypeReturn,
@@ -1727,6 +1734,10 @@ public:
const Common::SharedPtr<SubtitleRenderer> &getSubtitleRenderer() const;
+ void queueCloneObject(const Common::WeakPtr<RuntimeObject> &obj);
+ void queueKillObject(const Common::WeakPtr<RuntimeObject> &obj);
+ void queueChangeObjectParent(const Common::WeakPtr<RuntimeObject> &obj, const Common::WeakPtr<RuntimeObject> &newParent);
+
#ifdef MTROPOLIS_DEBUG_ENABLE
void debugSetEnabled(bool enabled);
void debugBreak();
@@ -1752,6 +1763,7 @@ private:
Teardown();
Common::WeakPtr<Structural> structural;
+ Common::WeakPtr<Modifier> modifier;
bool onlyRemoveChildren;
};
@@ -1842,6 +1854,9 @@ private:
void executeCompleteTransitionToScene(const Common::SharedPtr<Structural> &scene);
void executeSharedScenePostSceneChangeActions();
void executeSceneChangeRecursiveVisibilityChange(Structural *structural, bool showing);
+ void executeChangeObjectParent(RuntimeObject *object, RuntimeObject *newParent);
+ void executeCloneObject(RuntimeObject *object);
+ void executeKillObject(RuntimeObject *object);
void recursiveAutoPlayMedia(Structural *structural);
void recursiveDeactivateStructural(Structural *structural);
@@ -1884,6 +1899,11 @@ private:
Common::Array<Teardown> _pendingTeardowns;
Common::Array<LowLevelSceneStateTransitionAction> _pendingLowLevelTransitions;
+ Common::Array<Common::WeakPtr<RuntimeObject> > _pendingKills;
+ Common::Array<Common::WeakPtr<RuntimeObject> > _pendingClones;
+ Common::Array<Common::WeakPtr<Structural> > _pendingPostCloneShowChecks;
+ Common::Array<Common::WeakPtr<Structural> > _pendingShowClonedObject;
+ Common::Array<ObjectParentChange> _pendingParentChanges;
Common::Array<HighLevelSceneTransition> _pendingSceneTransitions;
Common::Array<SceneStackEntry> _sceneStack;
Common::SharedPtr<Structural> _activeMainScene;
@@ -2005,12 +2025,14 @@ private:
struct IModifierContainer : public IInterfaceBase {
virtual const Common::Array<Common::SharedPtr<Modifier> > &getModifiers() const = 0;
virtual void appendModifier(const Common::SharedPtr<Modifier> &modifier) = 0;
+ virtual void removeModifier(const Modifier *modifier) = 0;
};
class SimpleModifierContainer : public IModifierContainer {
public:
const Common::Array<Common::SharedPtr<Modifier> > &getModifiers() const override;
void appendModifier(const Common::SharedPtr<Modifier> &modifier) override;
+ void removeModifier(const Modifier *modifier) override;
void clear();
@@ -2047,6 +2069,10 @@ public:
virtual MiniscriptInstructionOutcome writeRefAttributeIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib, const DynamicValue &index);
protected:
+ MiniscriptInstructionOutcome scriptSetClone(MiniscriptThread *thread, const DynamicValue &value);
+ MiniscriptInstructionOutcome scriptSetKill(MiniscriptThread *thread, const DynamicValue &value);
+ MiniscriptInstructionOutcome scriptSetParent(MiniscriptThread *thread, const DynamicValue &value);
+
// This is the static GUID stored in the data, it is not guaranteed
// to be globally unique at runtime. In particular, cloning an object
// and using aliased modifiers will cause multiple objects with the same
@@ -2054,6 +2080,12 @@ protected:
uint32 _guid;
uint32 _runtimeGUID;
Common::WeakPtr<RuntimeObject> _selfReference;
+
+ struct ParentWriteProxyInterface {
+ static MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset);
+ static MiniscriptInstructionOutcome refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib);
+ static MiniscriptInstructionOutcome refAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index);
+ };
};
struct MessageProperties {
@@ -2181,6 +2213,7 @@ public:
const Common::Array<Common::SharedPtr<Modifier> > &getModifiers() const override;
void appendModifier(const Common::SharedPtr<Modifier> &modifier) override;
+ void removeModifier(const Modifier *modifier) override;
bool respondsToEvent(const Event &evt) const override;
VThreadState consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
@@ -2198,6 +2231,11 @@ public:
void setHooks(const Common::SharedPtr<StructuralHooks> &hooks);
const Common::SharedPtr<StructuralHooks> &getHooks() const;
+ // Shallow clones only need to copy the object. Descendent copies are done using visitInternalReferences.
+ virtual Common::SharedPtr<Structural> shallowClone() const = 0;
+
+ virtual void visitInternalReferences(IStructuralReferenceVisitor *visitor);
+
#ifdef MTROPOLIS_DEBUG_ENABLE
SupportStatus debugGetSupportStatus() const override;
const Common::String &debugGetName() const override;
@@ -2207,6 +2245,8 @@ public:
#endif
protected:
+ explicit Structural(const Structural &other);
+
virtual ObjectLinkingScope *getPersistentStructuralScope();
virtual ObjectLinkingScope *getPersistentModifierScope();
@@ -2450,6 +2490,9 @@ public:
RuntimeVersion getRuntimeVersion() const;
ProjectPlatform getPlatform() const;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+ Common::SharedPtr<Structural> shallowClone() const override;
+
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "Project"; }
#endif
@@ -2580,6 +2623,9 @@ public:
bool isSection() const override;
+ Common::SharedPtr<Structural> shallowClone() const override;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "Section"; }
#endif
@@ -2602,6 +2648,9 @@ public:
bool isSubsection() const override;
+ Common::SharedPtr<Structural> shallowClone() const override;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "Subsection"; }
#endif
@@ -2633,7 +2682,11 @@ public:
virtual bool resolveMediaMarkerLabel(const Label &label, int32 &outResolution) const;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
protected:
+ Element(const Element &other);
+
uint32 _streamLocator;
uint16 _sectionID;
@@ -2813,11 +2866,17 @@ public:
void setPalette(const Common::SharedPtr<Palette> &palette);
const Common::SharedPtr<Palette> &getPalette() const;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
+ void pushVisibilityChangeTask(Runtime *runtime, bool desiredVisibility);
+
#ifdef MTROPOLIS_DEBUG_ENABLE
void debugInspect(IDebugInspectionReport *report) const override;
#endif
protected:
+ VisualElement(const VisualElement &other);
+
bool loadCommon(const Common::String &name, uint32 guid, const Data::Rect &rect, uint32 elementFlags, uint16 layer, uint32 streamLocator, uint16 sectionID);
MiniscriptInstructionOutcome scriptSetDirect(MiniscriptThread *thread, const DynamicValue &dest);
More information about the Scummvm-git-logs
mailing list