[Scummvm-git-logs] scummvm master -> 52e54cc0901dfa4d9d8a15e329e7157ecf7275eb

elasota noreply at scummvm.org
Sun Nov 27 22:02:14 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:
52e54cc090 MTROPOLIS: Change how variable storage works so variable modifiers can be cloned into the scene tree.  Add hack for MTI 


Commit: 52e54cc0901dfa4d9d8a15e329e7157ecf7275eb
    https://github.com/scummvm/scummvm/commit/52e54cc0901dfa4d9d8a15e329e7157ecf7275eb
Author: elasota (ejlasota at gmail.com)
Date: 2022-11-27T16:54:24-05:00

Commit Message:
MTROPOLIS: Change how variable storage works so variable modifiers can be cloned into the scene tree.  Add hack for MTI variable init.

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


diff --git a/engines/mtropolis/hacks.cpp b/engines/mtropolis/hacks.cpp
index 9338e84f9f4..3c29b4532ad 100644
--- a/engines/mtropolis/hacks.cpp
+++ b/engines/mtropolis/hacks.cpp
@@ -1028,6 +1028,21 @@ void addMTIQuirks(const MTropolisGameDescription &desc, Hacks &hacks) {
 	// Anyway, there are two possible solutions to this: Lock the clock to 60Hz, or ignore the flag.
 	// Given that the flag should not be set, we ignore the flag.
 	hacks.ignoreMToonMaintainRateFlag = true;
+
+	// MTI initializes variables in a way that doesn't seem to match mTropolis behavior in any explicable way:
+	//
+	// For example, 0010cb0e "Scene Started => init Benbow" looks like this internally, decompiled:
+	// set local:a.billystate to 0
+	//
+	// In this case "a" is a compound variable and "billyState" is a NON-ALIASED integer variable contained in
+	// the compound.  Later, 0009fc9a "Scene Started => play intro" checks local 00007f83 00 'billyState'
+	// to determine if the Benbow intro needs to be played.  Since the GUID doesn't match (?) we check by name,
+	// which resolves to the GUID-less (?) alias in the Benbow subsection, which references 00097cf4, a different
+	// variable also named "billyState"
+	//
+	// Haven't figured out anything that would explain why it would reference the variables in the compound
+	// modifier.  Probably some quirk of early-version mTropolis.
+	hacks.mtiVariableReferencesHack = true;
 }
 
 } // End of namespace HackSuites
diff --git a/engines/mtropolis/hacks.h b/engines/mtropolis/hacks.h
index 371cc976066..cf8a77bd125 100644
--- a/engines/mtropolis/hacks.h
+++ b/engines/mtropolis/hacks.h
@@ -51,6 +51,8 @@ struct Hacks {
 	bool ignoreMismatchedProjectNameInObjectLookups;
 	bool removeQuickTimeEdits;
 	bool ignoreMToonMaintainRateFlag;
+	bool mtiVariableReferencesHack;
+
 	uint midiVolumeScale;	// 256 = 1.0
 
 	uint32 minTransitionDuration;
diff --git a/engines/mtropolis/modifiers.cpp b/engines/mtropolis/modifiers.cpp
index 1a0c1d5b315..4db195f4544 100644
--- a/engines/mtropolis/modifiers.cpp
+++ b/engines/mtropolis/modifiers.cpp
@@ -2319,7 +2319,7 @@ bool CompoundVariableModifier::load(ModifierLoaderContext &context, const Data::
 }
 
 void CompoundVariableModifier::disable(Runtime *runtime) {
-	// Do nothing I guess, no variables can be disdabled
+	// Do nothing I guess, no variables can be disabled
 }
 
 Common::SharedPtr<ModifierSaveLoad> CompoundVariableModifier::getSaveLoad() {
@@ -2346,7 +2346,7 @@ void CompoundVariableModifier::visitInternalReferences(IStructuralReferenceVisit
 }
 
 bool CompoundVariableModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
-	Modifier *var = findChildByName(attrib);
+	Modifier *var = findChildByName(thread->getRuntime(), attrib);
 	if (var) {
 		// Shouldn't dereference the value here, some scripts (e.g. "<go dest> on MUI" in Obsidian) depend on it not being dereferenced
 		result.setObject(var->getSelfReference());
@@ -2356,7 +2356,7 @@ bool CompoundVariableModifier::readAttribute(MiniscriptThread *thread, DynamicVa
 }
 
 bool CompoundVariableModifier::readAttributeIndexed(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib, const DynamicValue &index) {
-	Modifier *var = findChildByName(attrib);
+	Modifier *var = findChildByName(thread->getRuntime(), attrib);
 	if (!var || !var->isVariable())
 		return false;
 
@@ -2364,7 +2364,7 @@ bool CompoundVariableModifier::readAttributeIndexed(MiniscriptThread *thread, Dy
 }
 
 MiniscriptInstructionOutcome CompoundVariableModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
-	Modifier *var = findChildByName(attrib);
+	Modifier *var = findChildByName(thread->getRuntime(), attrib);
 	if (!var)
 		return kMiniscriptInstructionOutcomeFailed;
 
@@ -2379,14 +2379,26 @@ MiniscriptInstructionOutcome CompoundVariableModifier::writeRefAttribute(Miniscr
 }
 
 MiniscriptInstructionOutcome CompoundVariableModifier::writeRefAttributeIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib, const DynamicValue &index) {
-	Modifier *var = findChildByName(attrib);
+	Modifier *var = findChildByName(thread->getRuntime(), attrib);
 	if (!var || !var->isModifier())
 		return kMiniscriptInstructionOutcomeFailed;
 
 	return var->writeRefAttributeIndexed(thread, writeProxy, "value", index);
 }
 
-Modifier *CompoundVariableModifier::findChildByName(const Common::String &name) const {
+Modifier *CompoundVariableModifier::findChildByName(Runtime *runtime, const Common::String &name) const {
+	if (runtime->getHacks().mtiVariableReferencesHack) {
+		const Common::String &myName = getName();
+
+		if (myName.size() == 1 && myName == "a" || myName == "b" || myName == "c" || myName == "d") {
+			Project *project = runtime->getProject();
+			Modifier *modifier = project->findGlobalVarWithName(MTropolis::toCaseInsensitive(name)).get();
+
+			if (modifier)
+				return modifier;
+		}
+	}
+
 	for (Common::Array<Common::SharedPtr<Modifier> >::const_iterator it = _children.begin(), itEnd = _children.end(); it != itEnd; ++it) {
 		Modifier *modifier = it->get();
 		if (caseInsensitiveEqual(name, modifier->getName()))
@@ -2446,41 +2458,37 @@ const char *CompoundVariableModifier::getDefaultName() const {
 	return "Compound Variable";
 }
 
-BooleanVariableModifier::BooleanVariableModifier() : _value(false) {
+BooleanVariableModifier::BooleanVariableModifier() : VariableModifier(Common::SharedPtr<VariableStorage>(new BooleanVariableStorage())) {
 }
 
 bool BooleanVariableModifier::load(ModifierLoaderContext &context, const Data::BooleanVariableModifier &data) {
 	if (!loadTypicalHeader(data.modHeader))
 		return false;
 
-	_value = (data.value != 0);
+	static_cast<BooleanVariableStorage *>(_storage.get())->_value = (data.value != 0);
 
 	return true;
 }
 
-Common::SharedPtr<ModifierSaveLoad> BooleanVariableModifier::getSaveLoad() {
-	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
-}
-
 bool BooleanVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
 	DynamicValue boolValue;
 	if (!value.convertToType(DynamicValueTypes::kBoolean, boolValue))
 		return false;
 
-	_value = boolValue.getBool();
+	static_cast<BooleanVariableStorage *>(_storage.get())->_value = boolValue.getBool();
 
 	return true;
 }
 
 void BooleanVariableModifier::varGetValue(DynamicValue &dest) const {
-	dest.setBool(_value);
+	dest.setBool(static_cast<BooleanVariableStorage *>(_storage.get())->_value);
 }
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 void BooleanVariableModifier::debugInspect(IDebugInspectionReport *report) const {
 	VariableModifier::debugInspect(report);
 
-	report->declareDynamic("value", _value ? "true" : "false");
+	report->declareDynamic("value", static_cast<BooleanVariableStorage *>(_storage.get())->_value ? "true" : "false");
 }
 #endif
 
@@ -2492,19 +2500,30 @@ const char *BooleanVariableModifier::getDefaultName() const {
 	return "Boolean Variable";
 }
 
-BooleanVariableModifier::SaveLoad::SaveLoad(BooleanVariableModifier *modifier) : _modifier(modifier) {
-	_value = _modifier->_value;
+BooleanVariableStorage::BooleanVariableStorage() : _value(false) {
+}
+
+Common::SharedPtr<ModifierSaveLoad> BooleanVariableStorage::getSaveLoad() {
+	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
+}
+
+Common::SharedPtr<VariableStorage> BooleanVariableStorage::clone() const {
+	return Common::SharedPtr<VariableStorage>(new BooleanVariableStorage(*this));
+}
+
+BooleanVariableStorage::SaveLoad::SaveLoad(BooleanVariableStorage *storage) : _storage(storage) {
+	_value = _storage->_value;
 }
 
-void BooleanVariableModifier::SaveLoad::commitLoad() const {
-	_modifier->_value = _value;
+void BooleanVariableStorage::SaveLoad::commitLoad() const {
+	_storage->_value = _value;
 }
 
-void BooleanVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) const {
+void BooleanVariableStorage::SaveLoad::saveInternal(Common::WriteStream *stream) const {
 	stream->writeByte(_value ? 1 : 0);
 }
 
-bool BooleanVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
+bool BooleanVariableStorage::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
 	byte b = stream->readByte();
 	if (stream->err())
 		return false;
@@ -2513,41 +2532,48 @@ bool BooleanVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream,
 	return true;
 }
 
-IntegerVariableModifier::IntegerVariableModifier() : _value(0) {
+IntegerVariableModifier::IntegerVariableModifier() : VariableModifier(Common::SharedPtr<VariableStorage>(new IntegerVariableStorage())) {
 }
 
 bool IntegerVariableModifier::load(ModifierLoaderContext& context, const Data::IntegerVariableModifier& data) {
 	if (!loadTypicalHeader(data.modHeader))
 		return false;
 
-	_value = data.value;
+	static_cast<IntegerVariableStorage *>(_storage.get())->_value = data.value;
 
 	return true;
 }
 
-Common::SharedPtr<ModifierSaveLoad> IntegerVariableModifier::getSaveLoad() {
+IntegerVariableStorage::IntegerVariableStorage() : _value(0) {
+}
+
+Common::SharedPtr<ModifierSaveLoad> IntegerVariableStorage::getSaveLoad() {
 	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
 }
 
+Common::SharedPtr<VariableStorage> IntegerVariableStorage::clone() const {
+	return Common::SharedPtr<VariableStorage>(new IntegerVariableStorage(*this));
+}
+
 bool IntegerVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
 	DynamicValue intValue;
 	if (!value.convertToType(DynamicValueTypes::kInteger, intValue))
 		return false;
 
-	_value = intValue.getInt();
+	static_cast<IntegerVariableStorage *>(_storage.get())->_value = intValue.getInt();
 
 	return true;
 }
 
 void IntegerVariableModifier::varGetValue(DynamicValue &dest) const {
-	dest.setInt(_value);
+	dest.setInt(static_cast<IntegerVariableStorage *>(_storage.get())->_value);
 }
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 void IntegerVariableModifier::debugInspect(IDebugInspectionReport *report) const {
 	VariableModifier::debugInspect(report);
 
-	report->declareDynamic("value", Common::String::format("%i", _value));
+	report->declareDynamic("value", Common::String::format("%i", static_cast<IntegerVariableStorage *>(_storage.get())->_value));
 }
 #endif
 
@@ -2559,19 +2585,19 @@ const char *IntegerVariableModifier::getDefaultName() const {
 	return "Integer Variable";
 }
 
-IntegerVariableModifier::SaveLoad::SaveLoad(IntegerVariableModifier *modifier) : _modifier(modifier) {
-	_value = _modifier->_value;
+IntegerVariableStorage::SaveLoad::SaveLoad(IntegerVariableStorage *storage) : _storage(storage) {
+	_value = _storage->_value;
 }
 
-void IntegerVariableModifier::SaveLoad::commitLoad() const {
-	_modifier->_value = _value;
+void IntegerVariableStorage::SaveLoad::commitLoad() const {
+	_storage->_value = _value;
 }
 
-void IntegerVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) const {
+void IntegerVariableStorage::SaveLoad::saveInternal(Common::WriteStream *stream) const {
 	stream->writeSint32BE(_value);
 }
 
-bool IntegerVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
+bool IntegerVariableStorage::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
 	_value = stream->readSint32BE();
 
 	if (stream->err())
@@ -2580,53 +2606,57 @@ bool IntegerVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream,
 	return true;
 }
 
+
+IntegerRangeVariableModifier::IntegerRangeVariableModifier() : VariableModifier(Common::SharedPtr<VariableStorage>(new IntegerRangeVariableStorage())) {
+}
+
 bool IntegerRangeVariableModifier::load(ModifierLoaderContext& context, const Data::IntegerRangeVariableModifier& data) {
 	if (!loadTypicalHeader(data.modHeader))
 		return false;
 
-	if (!_range.load(data.range))
+	if (!static_cast<IntegerRangeVariableStorage *>(_storage.get())->_range.load(data.range))
 		return false;
 
 	return true;
 }
 
-Common::SharedPtr<ModifierSaveLoad> IntegerRangeVariableModifier::getSaveLoad() {
-	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
-}
-
 bool IntegerRangeVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
 	DynamicValue intRangeValue;
 	if (!value.convertToType(DynamicValueTypes::kIntegerRange, intRangeValue))
 		return false;
 
-	_range = intRangeValue.getIntRange();
+	static_cast<IntegerRangeVariableStorage *>(_storage.get())->_range = intRangeValue.getIntRange();
 
 	return true;
 }
 
 void IntegerRangeVariableModifier::varGetValue(DynamicValue &dest) const {
-	dest.setIntRange(_range);
+	dest.setIntRange(static_cast<IntegerRangeVariableStorage *>(_storage.get())->_range);
 }
 
 bool IntegerRangeVariableModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
+	IntegerRangeVariableStorage *storage = static_cast<IntegerRangeVariableStorage *>(_storage.get());
+
 	if (attrib == "start") {
-		result.setInt(_range.min);
+		result.setInt(storage->_range.min);
 		return true;
 	}
 	if (attrib == "end") {
-		result.setInt(_range.max);
+		result.setInt(storage->_range.max);
 		return true;
 	}
 	return Modifier::readAttribute(thread, result, attrib);
 }
 
 MiniscriptInstructionOutcome IntegerRangeVariableModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
+	IntegerRangeVariableStorage *storage = static_cast<IntegerRangeVariableStorage *>(_storage.get());
+
 	if (attrib == "start") {
-		DynamicValueWriteIntegerHelper<int32>::create(&_range.min, result);
+		DynamicValueWriteIntegerHelper<int32>::create(&storage->_range.min, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "end") {
-		DynamicValueWriteIntegerHelper<int32>::create(&_range.max, result);
+		DynamicValueWriteIntegerHelper<int32>::create(&storage->_range.max, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	return Modifier::writeRefAttribute(thread, result, attrib);
@@ -2634,9 +2664,11 @@ MiniscriptInstructionOutcome IntegerRangeVariableModifier::writeRefAttribute(Min
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 void IntegerRangeVariableModifier::debugInspect(IDebugInspectionReport *report) const {
+	IntegerRangeVariableStorage *storage = static_cast<IntegerRangeVariableStorage *>(_storage.get());
+
 	VariableModifier::debugInspect(report);
 
-	report->declareDynamic("value", _range.toString());
+	report->declareDynamic("value", storage->_range.toString());
 }
 #endif
 
@@ -2648,22 +2680,33 @@ const char *IntegerRangeVariableModifier::getDefaultName() const {
 	return "Integer Range Variable";
 }
 
-IntegerRangeVariableModifier::SaveLoad::SaveLoad(IntegerRangeVariableModifier *modifier) : _modifier(modifier) {
-	_range = _modifier->_range;
+IntegerRangeVariableStorage::IntegerRangeVariableStorage() {
+}
+
+Common::SharedPtr<ModifierSaveLoad> IntegerRangeVariableStorage::getSaveLoad() {
+	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
+}
+
+Common::SharedPtr<VariableStorage> IntegerRangeVariableStorage::clone() const {
+	return Common::SharedPtr<VariableStorage>(new IntegerRangeVariableStorage(*this));
 }
 
-void IntegerRangeVariableModifier::SaveLoad::commitLoad() const {
-	_modifier->_range = _range;
+IntegerRangeVariableStorage::SaveLoad::SaveLoad(IntegerRangeVariableStorage *storage) : _storage(storage) {
+	_range = _storage->_range;
 }
 
-void IntegerRangeVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) const {
-	stream->writeSint32BE(_range.min);
-	stream->writeSint32BE(_range.max);
+void IntegerRangeVariableStorage::SaveLoad::commitLoad() const {
+	_storage->_range = _range;
 }
 
-bool IntegerRangeVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
-	_range.min = stream->readSint32BE();
-	_range.max = stream->readSint32BE();
+void IntegerRangeVariableStorage::SaveLoad::saveInternal(Common::WriteStream *stream) const {
+	stream->writeSint32BE(_storage->_range.min);
+	stream->writeSint32BE(_storage->_range.max);
+}
+
+bool IntegerRangeVariableStorage::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
+	_storage->_range.min = stream->readSint32BE();
+	_storage->_range.max = stream->readSint32BE();
 
 	if (stream->err())
 		return false;
@@ -2671,18 +2714,19 @@ bool IntegerRangeVariableModifier::SaveLoad::loadInternal(Common::ReadStream *st
 	return true;
 }
 
+VectorVariableModifier::VectorVariableModifier() : VariableModifier(Common::SharedPtr<VariableStorage>(new VectorVariableStorage())) {
+}
+
 bool VectorVariableModifier::load(ModifierLoaderContext &context, const Data::VectorVariableModifier &data) {
 	if (!loadTypicalHeader(data.modHeader))
 		return false;
 
-	_vector.angleDegrees = data.vector.angleRadians.toDouble() * (180 / M_PI);
-	_vector.magnitude = data.vector.magnitude.toDouble();
+	VectorVariableStorage *storage = static_cast<VectorVariableStorage *>(_storage.get());
 
-	return true;
-}
+	storage->_vector.angleDegrees = data.vector.angleRadians.toDouble() * (180 / M_PI);
+	storage->_vector.magnitude = data.vector.magnitude.toDouble();
 
-Common::SharedPtr<ModifierSaveLoad> VectorVariableModifier::getSaveLoad() {
-	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
+	return true;
 }
 
 bool VectorVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
@@ -2690,21 +2734,27 @@ bool VectorVariableModifier::varSetValue(MiniscriptThread *thread, const Dynamic
 	if (!value.convertToType(DynamicValueTypes::kVector, vectorValue))
 		return false;
 
-	_vector = vectorValue.getVector();
+	VectorVariableStorage *storage = static_cast<VectorVariableStorage *>(_storage.get());
+
+	storage->_vector = vectorValue.getVector();
 
 	return true;
 }
 
 void VectorVariableModifier::varGetValue(DynamicValue &dest) const {
-	dest.setVector(_vector);
+	VectorVariableStorage *storage = static_cast<VectorVariableStorage *>(_storage.get());
+
+	dest.setVector(storage->_vector);
 }
 
 bool VectorVariableModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
+	VectorVariableStorage *storage = static_cast<VectorVariableStorage *>(_storage.get());
+
 	if (attrib == "magnitude") {
-		result.setFloat(_vector.magnitude);
+		result.setFloat(storage->_vector.magnitude);
 		return true;
 	} else if (attrib == "angle") {
-		result.setFloat(_vector.angleDegrees);
+		result.setFloat(storage->_vector.angleDegrees);
 		return true;
 	}
 
@@ -2712,11 +2762,13 @@ bool VectorVariableModifier::readAttribute(MiniscriptThread *thread, DynamicValu
 }
 
 MiniscriptInstructionOutcome VectorVariableModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
+	VectorVariableStorage *storage = static_cast<VectorVariableStorage *>(_storage.get());
+
 	if (attrib == "magnitude") {
-		DynamicValueWriteFloatHelper<double>::create(&_vector.magnitude, result);
+		DynamicValueWriteFloatHelper<double>::create(&storage->_vector.magnitude, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "angle") {
-		DynamicValueWriteFloatHelper<double>::create(&_vector.angleDegrees, result);
+		DynamicValueWriteFloatHelper<double>::create(&storage->_vector.angleDegrees, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -2727,7 +2779,9 @@ MiniscriptInstructionOutcome VectorVariableModifier::writeRefAttribute(Miniscrip
 void VectorVariableModifier::debugInspect(IDebugInspectionReport *report) const {
 	VariableModifier::debugInspect(report);
 
-	report->declareDynamic("value", _vector.toString());
+	VectorVariableStorage *storage = static_cast<VectorVariableStorage *>(_storage.get());
+
+	report->declareDynamic("value", storage->_vector.toString());
 }
 #endif
 
@@ -2739,20 +2793,31 @@ const char *VectorVariableModifier::getDefaultName() const {
 	return "Vector Variable";
 }
 
-VectorVariableModifier::SaveLoad::SaveLoad(VectorVariableModifier *modifier) : _modifier(modifier) {
-	_vector = _modifier->_vector;
+VectorVariableStorage::VectorVariableStorage() {
+}
+
+Common::SharedPtr<ModifierSaveLoad> VectorVariableStorage::getSaveLoad() {
+	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
+}
+
+Common::SharedPtr<VariableStorage> VectorVariableStorage::clone() const {
+	return Common::SharedPtr<VariableStorage>(new VectorVariableStorage(*this));
 }
 
-void VectorVariableModifier::SaveLoad::commitLoad() const {
-	_modifier->_vector = _vector;
+VectorVariableStorage::SaveLoad::SaveLoad(VectorVariableStorage *storage) : _storage(storage) {
+	_vector = _storage->_vector;
 }
 
-void VectorVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) const {
+void VectorVariableStorage::SaveLoad::commitLoad() const {
+	_storage->_vector = _vector;
+}
+
+void VectorVariableStorage::SaveLoad::saveInternal(Common::WriteStream *stream) const {
 	stream->writeDoubleBE(_vector.angleDegrees);
 	stream->writeDoubleBE(_vector.magnitude);
 }
 
-bool VectorVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
+bool VectorVariableStorage::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
 	_vector.angleDegrees = stream->readDoubleBE();
 	_vector.magnitude = stream->readDoubleBE();
 
@@ -2762,18 +2827,19 @@ bool VectorVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream,
 	return true;
 }
 
+PointVariableModifier::PointVariableModifier() : VariableModifier(Common::SharedPtr<VariableStorage>(new PointVariableStorage())) {
+}
+
 bool PointVariableModifier::load(ModifierLoaderContext &context, const Data::PointVariableModifier &data) {
 	if (!loadTypicalHeader(data.modHeader))
 		return false;
 
-	_value.x = data.value.x;
-	_value.y = data.value.y;
+	PointVariableStorage *storage = static_cast<PointVariableStorage *>(_storage.get());
 
-	return true;
-}
+	if (!data.value.toScummVMPoint(storage->_value))
+		return false;
 
-Common::SharedPtr<ModifierSaveLoad> PointVariableModifier::getSaveLoad() {
-	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
+	return true;
 }
 
 bool PointVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
@@ -2781,22 +2847,28 @@ bool PointVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicV
 	if (!value.convertToType(DynamicValueTypes::kPoint, pointValue))
 		return false;
 
-	_value = pointValue.getPoint();
+	PointVariableStorage *storage = static_cast<PointVariableStorage *>(_storage.get());
+
+	storage->_value = pointValue.getPoint();
 
 	return true;
 }
 
 void PointVariableModifier::varGetValue(DynamicValue &dest) const {
-	dest.setPoint(_value);
+	PointVariableStorage *storage = static_cast<PointVariableStorage *>(_storage.get());
+
+	dest.setPoint(storage->_value);
 }
 
 bool PointVariableModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
+	PointVariableStorage *storage = static_cast<PointVariableStorage *>(_storage.get());
+
 	if (attrib == "x") {
-		result.setInt(_value.x);
+		result.setInt(storage->_value.x);
 		return true;
 	}
 	if (attrib == "y") {
-		result.setInt(_value.y);
+		result.setInt(storage->_value.y);
 		return true;
 	}
 
@@ -2804,12 +2876,14 @@ bool PointVariableModifier::readAttribute(MiniscriptThread *thread, DynamicValue
 }
 
 MiniscriptInstructionOutcome PointVariableModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
+	PointVariableStorage *storage = static_cast<PointVariableStorage *>(_storage.get());
+
 	if (attrib == "x") {
-		DynamicValueWriteIntegerHelper<int16>::create(&_value.x, writeProxy);
+		DynamicValueWriteIntegerHelper<int16>::create(&storage->_value.x, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "y") {
-		DynamicValueWriteIntegerHelper<int16>::create(&_value.y, writeProxy);
+		DynamicValueWriteIntegerHelper<int16>::create(&storage->_value.y, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -2820,7 +2894,9 @@ MiniscriptInstructionOutcome PointVariableModifier::writeRefAttribute(Miniscript
 void PointVariableModifier::debugInspect(IDebugInspectionReport *report) const {
 	VariableModifier::debugInspect(report);
 
-	report->declareDynamic("value", pointToString(_value));
+	PointVariableStorage *storage = static_cast<PointVariableStorage *>(_storage.get());
+
+	report->declareDynamic("value", pointToString(storage->_value));
 }
 #endif
 
@@ -2832,20 +2908,31 @@ const char *PointVariableModifier::getDefaultName() const {
 	return "Point Variable";
 }
 
-PointVariableModifier::SaveLoad::SaveLoad(PointVariableModifier *modifier) : _modifier(modifier) {
-	_value = _modifier->_value;
+PointVariableStorage::PointVariableStorage() {
 }
 
-void PointVariableModifier::SaveLoad::commitLoad() const {
-	_modifier->_value = _value;
+Common::SharedPtr<ModifierSaveLoad> PointVariableStorage::getSaveLoad() {
+	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
 }
 
-void PointVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) const {
+Common::SharedPtr<VariableStorage> PointVariableStorage::clone() const {
+	return Common::SharedPtr<VariableStorage>(new PointVariableStorage(*this));
+}
+
+PointVariableStorage::SaveLoad::SaveLoad(PointVariableStorage *storage) : _storage(storage) {
+	_value = storage->_value;
+}
+
+void PointVariableStorage::SaveLoad::commitLoad() const {
+	_storage->_value = _value;
+}
+
+void PointVariableStorage::SaveLoad::saveInternal(Common::WriteStream *stream) const {
 	stream->writeSint16BE(_value.x);
 	stream->writeSint16BE(_value.y);
 }
 
-bool PointVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
+bool PointVariableStorage::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
 	_value.x = stream->readSint16BE();
 	_value.y = stream->readSint16BE();
 
@@ -2855,20 +2942,18 @@ bool PointVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, u
 	return true;
 }
 
-FloatingPointVariableModifier::FloatingPointVariableModifier() : _value(0.0) {
+FloatingPointVariableModifier::FloatingPointVariableModifier() : VariableModifier(Common::SharedPtr<FloatingPointVariableStorage>(new FloatingPointVariableStorage())) {
 }
 
 bool FloatingPointVariableModifier::load(ModifierLoaderContext &context, const Data::FloatingPointVariableModifier &data) {
 	if (!loadTypicalHeader(data.modHeader))
 		return false;
 
-	_value = data.value.toDouble();
+	FloatingPointVariableStorage *storage = static_cast<FloatingPointVariableStorage *>(_storage.get());
 
-	return true;
-}
+	storage->_value = data.value.toDouble();
 
-Common::SharedPtr<ModifierSaveLoad> FloatingPointVariableModifier::getSaveLoad() {
-	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
+	return true;
 }
 
 bool FloatingPointVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
@@ -2876,20 +2961,26 @@ bool FloatingPointVariableModifier::varSetValue(MiniscriptThread *thread, const
 	if (!value.convertToType(DynamicValueTypes::kFloat, floatValue))
 		return false;
 
-	_value = floatValue.getFloat();
+	FloatingPointVariableStorage *storage = static_cast<FloatingPointVariableStorage *>(_storage.get());
+
+	storage->_value = floatValue.getFloat();
 
 	return true;
 }
 
 void FloatingPointVariableModifier::varGetValue(DynamicValue &dest) const {
-	dest.setFloat(_value);
+	FloatingPointVariableStorage *storage = static_cast<FloatingPointVariableStorage *>(_storage.get());
+
+	dest.setFloat(storage->_value);
 }
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 void FloatingPointVariableModifier::debugInspect(IDebugInspectionReport *report) const {
 	VariableModifier::debugInspect(report);
 
-	report->declareDynamic("value", Common::String::format("%g", _value));
+	FloatingPointVariableStorage *storage = static_cast<FloatingPointVariableStorage *>(_storage.get());
+
+	report->declareDynamic("value", Common::String::format("%g", storage->_value));
 }
 #endif
 
@@ -2901,20 +2992,31 @@ const char *FloatingPointVariableModifier::getDefaultName() const {
 	return "Floating Point Variable";
 }
 
-FloatingPointVariableModifier::SaveLoad::SaveLoad(FloatingPointVariableModifier *modifier) : _modifier(modifier) {
-	_value = _modifier->_value;
+FloatingPointVariableStorage::FloatingPointVariableStorage() : _value(0.0) {
+}
+
+Common::SharedPtr<ModifierSaveLoad> FloatingPointVariableStorage::getSaveLoad() {
+	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
 }
 
-void FloatingPointVariableModifier::SaveLoad::commitLoad() const {
-	_modifier->_value = _value;
+Common::SharedPtr<VariableStorage> FloatingPointVariableStorage::clone() const {
+	return Common::SharedPtr<VariableStorage>(new FloatingPointVariableStorage(*this));
 }
 
-void FloatingPointVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) const {
-	stream->writeDoubleBE(_value);
+FloatingPointVariableStorage::SaveLoad::SaveLoad(FloatingPointVariableStorage *storage) : _storage(storage) {
+	_value = _storage->_value;
 }
 
-bool FloatingPointVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
-	_value = stream->readDoubleBE();
+void FloatingPointVariableStorage::SaveLoad::commitLoad() const {
+	_storage->_value = _value;
+}
+
+void FloatingPointVariableStorage::SaveLoad::saveInternal(Common::WriteStream *stream) const {
+	stream->writeDoubleBE(_storage->_value);
+}
+
+bool FloatingPointVariableStorage::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
+	_storage->_value = stream->readDoubleBE();
 
 	if (stream->err())
 		return false;
@@ -2922,17 +3024,18 @@ bool FloatingPointVariableModifier::SaveLoad::loadInternal(Common::ReadStream *s
 	return true;
 }
 
+StringVariableModifier::StringVariableModifier() : VariableModifier(Common::SharedPtr<VariableStorage>(new StringVariableStorage())) {
+}
+
 bool StringVariableModifier::load(ModifierLoaderContext &context, const Data::StringVariableModifier &data) {
 	if (!loadTypicalHeader(data.modHeader))
 		return false;
 
-	_value = data.value;
+	StringVariableStorage *storage = static_cast<StringVariableStorage *>(_storage.get());
 
-	return true;
-}
+	storage->_value = data.value;
 
-Common::SharedPtr<ModifierSaveLoad> StringVariableModifier::getSaveLoad() {
-	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
+	return true;
 }
 
 bool StringVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
@@ -2940,20 +3043,26 @@ bool StringVariableModifier::varSetValue(MiniscriptThread *thread, const Dynamic
 	if (!value.convertToType(DynamicValueTypes::kString, stringValue))
 		return false;
 
-	_value = stringValue.getString();
+	StringVariableStorage *storage = static_cast<StringVariableStorage *>(_storage.get());
+
+	storage->_value = stringValue.getString();
 
 	return true;
 }
 
 void StringVariableModifier::varGetValue(DynamicValue &dest) const {
-	dest.setString(_value);
+	StringVariableStorage *storage = static_cast<StringVariableStorage *>(_storage.get());
+
+	dest.setString(storage->_value);
 }
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 void StringVariableModifier::debugInspect(IDebugInspectionReport *report) const {
 	VariableModifier::debugInspect(report);
 
-	report->declareDynamic("value", _value);
+	StringVariableStorage *storage = static_cast<StringVariableStorage *>(_storage.get());
+
+	report->declareDynamic("value", storage->_value);
 }
 #endif
 
@@ -2965,20 +3074,31 @@ const char *StringVariableModifier::getDefaultName() const {
 	return "String Variable";
 }
 
-StringVariableModifier::SaveLoad::SaveLoad(StringVariableModifier *modifier) : _modifier(modifier) {
-	_value = _modifier->_value;
+StringVariableStorage::StringVariableStorage() {
+}
+
+Common::SharedPtr<ModifierSaveLoad> StringVariableStorage::getSaveLoad() {
+	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
+}
+
+Common::SharedPtr<VariableStorage> StringVariableStorage::clone() const {
+	return Common::SharedPtr<VariableStorage>(new StringVariableStorage(*this));
+}
+
+StringVariableStorage::SaveLoad::SaveLoad(StringVariableStorage *storage) : _storage(storage) {
+	_value = _storage->_value;
 }
 
-void StringVariableModifier::SaveLoad::commitLoad() const {
-	_modifier->_value = _value;
+void StringVariableStorage::SaveLoad::commitLoad() const {
+	_storage->_value = _value;
 }
 
-void StringVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) const {
+void StringVariableStorage::SaveLoad::saveInternal(Common::WriteStream *stream) const {
 	stream->writeUint32BE(_value.size());
 	stream->writeString(_value);
 }
 
-bool StringVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
+bool StringVariableStorage::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
 	uint32 size = stream->readUint32BE();
 
 	if (stream->err())
@@ -2999,7 +3119,8 @@ bool StringVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream,
 	return true;
 }
 
-
+ObjectReferenceVariableModifierV1::ObjectReferenceVariableModifierV1() : VariableModifier(Common::SharedPtr<VariableStorage>(new ObjectReferenceVariableV1Storage())) {
+}
 
 bool ObjectReferenceVariableModifierV1::load(ModifierLoaderContext &context, const Data::ObjectReferenceVariableModifierV1 &data) {
 	if (!loadTypicalHeader(data.modHeader))
@@ -3022,17 +3143,15 @@ VThreadState ObjectReferenceVariableModifierV1::consumeMessage(Runtime *runtime,
 	return kVThreadError;
 }
 
-Common::SharedPtr<ModifierSaveLoad> ObjectReferenceVariableModifierV1::getSaveLoad() {
-	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
-}
-
 bool ObjectReferenceVariableModifierV1::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
+	ObjectReferenceVariableV1Storage *storage = static_cast<ObjectReferenceVariableV1Storage *>(_storage.get());
+
 	// Somewhat tricky aspect: If this is set to another object reference variable modifier, then this will reference
 	// the other object variable modifier, it will NOT copy it.
 	if (value.getType() == DynamicValueTypes::kNull)
-		_value.reset();
+		storage->_value.reset();
 	else if (value.getType() == DynamicValueTypes::kObject)
-		_value = value.getObject().object;
+		storage->_value = value.getObject().object;
 	else
 		return false;
 
@@ -3051,18 +3170,29 @@ const char *ObjectReferenceVariableModifierV1::getDefaultName() const {
 	return "Object Reference Variable";
 }
 
-ObjectReferenceVariableModifierV1::SaveLoad::SaveLoad(ObjectReferenceVariableModifierV1 *modifier) : _modifier(modifier) {
+ObjectReferenceVariableV1Storage::ObjectReferenceVariableV1Storage() {
+}
+
+Common::SharedPtr<ModifierSaveLoad> ObjectReferenceVariableV1Storage::getSaveLoad() {
+	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
+}
+
+Common::SharedPtr<VariableStorage> ObjectReferenceVariableV1Storage::clone() const {
+	return Common::SharedPtr<VariableStorage>(new ObjectReferenceVariableV1Storage(*this));
+}
+
+ObjectReferenceVariableV1Storage::SaveLoad::SaveLoad(ObjectReferenceVariableV1Storage *storage) : _storage(storage) {
 }
 
-void ObjectReferenceVariableModifierV1::SaveLoad::commitLoad() const {
-	_modifier->_value = _value;
+void ObjectReferenceVariableV1Storage::SaveLoad::commitLoad() const {
+	_storage->_value = _value;
 }
 
-void ObjectReferenceVariableModifierV1::SaveLoad::saveInternal(Common::WriteStream *stream) const {
+void ObjectReferenceVariableV1Storage::SaveLoad::saveInternal(Common::WriteStream *stream) const {
 	error("Saving version 1 object reference variables is not currently supported");
 }
 
-bool ObjectReferenceVariableModifierV1::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
+bool ObjectReferenceVariableV1Storage::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
 	return true;
 }
 
diff --git a/engines/mtropolis/modifiers.h b/engines/mtropolis/modifiers.h
index af76be475a9..f04b84e6d66 100644
--- a/engines/mtropolis/modifiers.h
+++ b/engines/mtropolis/modifiers.h
@@ -1000,7 +1000,7 @@ private:
 	MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) override;
 	MiniscriptInstructionOutcome writeRefAttributeIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib, const DynamicValue &index) override;
 
-	Modifier *findChildByName(const Common::String &name) const;
+	Modifier *findChildByName(Runtime *runtime, const Common::String &name) const;
 
 	Common::Array<Common::SharedPtr<Modifier> > _children;
 };
@@ -1011,8 +1011,6 @@ public:
 
 	bool load(ModifierLoaderContext &context, const Data::BooleanVariableModifier &data);
 
-	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
-
 	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
 	void varGetValue(DynamicValue &dest) const override;
 
@@ -1022,23 +1020,35 @@ public:
 	void debugInspect(IDebugInspectionReport *report) const override;
 #endif
 
+private:
+	Common::SharedPtr<Modifier> shallowClone() const override;
+	const char *getDefaultName() const override;
+};
+
+class BooleanVariableStorage : public VariableStorage {
+public:
+	friend class BooleanVariableModifier;
+
+	BooleanVariableStorage();
+
+	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+
+	Common::SharedPtr<VariableStorage> clone() const override;
+
 private:
 	class SaveLoad : public ModifierSaveLoad {
 	public:
-		explicit SaveLoad(BooleanVariableModifier *modifier);
+		explicit SaveLoad(BooleanVariableStorage *modifier);
 
 	private:
 		void commitLoad() const override;
 		void saveInternal(Common::WriteStream *stream) const override;
 		bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override;
 
-		BooleanVariableModifier *_modifier;
+		BooleanVariableStorage *_storage;
 		bool _value;
 	};
 
-	Common::SharedPtr<Modifier> shallowClone() const override;
-	const char *getDefaultName() const override;
-
 	bool _value;
 };
 
@@ -1048,8 +1058,6 @@ public:
 
 	bool load(ModifierLoaderContext &context, const Data::IntegerVariableModifier &data);
 
-	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
-
 	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
 	void varGetValue(DynamicValue &dest) const override;
 
@@ -1059,31 +1067,43 @@ public:
 	void debugInspect(IDebugInspectionReport *report) const override;
 #endif
 
+private:
+	Common::SharedPtr<Modifier> shallowClone() const override;
+	const char *getDefaultName() const override;
+};
+
+class IntegerVariableStorage : public VariableStorage {
+public:
+	friend class IntegerVariableModifier;
+
+	IntegerVariableStorage();
+
+	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+
+	Common::SharedPtr<VariableStorage> clone() const override;
+
 private:
 	class SaveLoad : public ModifierSaveLoad {
 	public:
-		explicit SaveLoad(IntegerVariableModifier *modifier);
+		explicit SaveLoad(IntegerVariableStorage *storage);
 
 	private:
 		void commitLoad() const override;
 		void saveInternal(Common::WriteStream *stream) const override;
 		bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override;
 
-		IntegerVariableModifier *_modifier;
+		IntegerVariableStorage *_storage;
 		int32 _value;
 	};
 
-	Common::SharedPtr<Modifier> shallowClone() const override;
-	const char *getDefaultName() const override;
-
 	int32 _value;
 };
 
 class IntegerRangeVariableModifier : public VariableModifier {
 public:
-	bool load(ModifierLoaderContext &context, const Data::IntegerRangeVariableModifier &data);
+	IntegerRangeVariableModifier();
 
-	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+	bool load(ModifierLoaderContext &context, const Data::IntegerRangeVariableModifier &data);
 
 	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
 	void varGetValue(DynamicValue &dest) const override;
@@ -1097,31 +1117,43 @@ public:
 	void debugInspect(IDebugInspectionReport *report) const override;
 #endif
 
+private:
+	Common::SharedPtr<Modifier> shallowClone() const override;
+	const char *getDefaultName() const override;
+};
+
+class IntegerRangeVariableStorage : public VariableStorage {
+public:
+	friend class IntegerRangeVariableModifier;
+
+	IntegerRangeVariableStorage();
+
+	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+
+	Common::SharedPtr<VariableStorage> clone() const override;
+
 private:
 	class SaveLoad : public ModifierSaveLoad {
 	public:
-		explicit SaveLoad(IntegerRangeVariableModifier *modifier);
+		explicit SaveLoad(IntegerRangeVariableStorage *storage);
 
 	private:
 		void commitLoad() const override;
 		void saveInternal(Common::WriteStream *stream) const override;
 		bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override;
 
-		IntegerRangeVariableModifier *_modifier;
+		IntegerRangeVariableStorage *_storage;
 		IntRange _range;
 	};
 
-	Common::SharedPtr<Modifier> shallowClone() const override;
-	const char *getDefaultName() const override;
-
 	IntRange _range;
 };
 
 class VectorVariableModifier : public VariableModifier {
 public:
-	bool load(ModifierLoaderContext &context, const Data::VectorVariableModifier &data);
+	VectorVariableModifier();
 
-	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+	bool load(ModifierLoaderContext &context, const Data::VectorVariableModifier &data);
 
 	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
 	void varGetValue(DynamicValue &dest) const override;
@@ -1135,31 +1167,42 @@ public:
 	void debugInspect(IDebugInspectionReport *report) const override;
 #endif
 
+private:
+	Common::SharedPtr<Modifier> shallowClone() const override;
+	const char *getDefaultName() const override;
+};
+
+class VectorVariableStorage : public VariableStorage {
+	friend class VectorVariableModifier;
+
+	VectorVariableStorage();
+
+	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+
+	Common::SharedPtr<VariableStorage> clone() const override;
+
 private:
 	class SaveLoad : public ModifierSaveLoad {
 	public:
-		explicit SaveLoad(VectorVariableModifier *modifier);
+		explicit SaveLoad(VectorVariableStorage *storage);
 
 	private:
 		void commitLoad() const override;
 		void saveInternal(Common::WriteStream *stream) const override;
 		bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override;
 
-		VectorVariableModifier *_modifier;
+		VectorVariableStorage *_storage;
 		AngleMagVector _vector;
 	};
 
-	Common::SharedPtr<Modifier> shallowClone() const override;
-	const char *getDefaultName() const override;
-
 	AngleMagVector _vector;
 };
 
 class PointVariableModifier : public VariableModifier {
 public:
-	bool load(ModifierLoaderContext &context, const Data::PointVariableModifier &data);
+	PointVariableModifier();
 
-	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+	bool load(ModifierLoaderContext &context, const Data::PointVariableModifier &data);
 
 	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
 	void varGetValue(DynamicValue &dest) const override;
@@ -1173,23 +1216,35 @@ public:
 	void debugInspect(IDebugInspectionReport *report) const override;
 #endif
 
+private:
+	Common::SharedPtr<Modifier> shallowClone() const override;
+	const char *getDefaultName() const override;
+};
+
+class PointVariableStorage : public VariableStorage {
+public:
+	friend class PointVariableModifier;
+
+	PointVariableStorage();
+
+	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+
+	Common::SharedPtr<VariableStorage> clone() const override;
+
 private:
 	class SaveLoad : public ModifierSaveLoad {
 	public:
-		explicit SaveLoad(PointVariableModifier *modifier);
+		explicit SaveLoad(PointVariableStorage *storage);
 
 	private:
 		void commitLoad() const override;
 		void saveInternal(Common::WriteStream *stream) const override;
 		bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override;
 
-		PointVariableModifier *_modifier;
+		PointVariableStorage *_storage;
 		Common::Point _value;
 	};
 
-	Common::SharedPtr<Modifier> shallowClone() const override;
-	const char *getDefaultName() const override;
-
 	Common::Point _value;
 };
 
@@ -1199,8 +1254,6 @@ public:
 
 	bool load(ModifierLoaderContext &context, const Data::FloatingPointVariableModifier &data);
 
-	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
-
 	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
 	void varGetValue(DynamicValue &dest) const override;
 
@@ -1210,31 +1263,43 @@ public:
 	void debugInspect(IDebugInspectionReport *report) const override;
 #endif
 
+private:
+	Common::SharedPtr<Modifier> shallowClone() const override;
+	const char *getDefaultName() const override;
+};
+
+class FloatingPointVariableStorage : public VariableStorage {
+public:
+	friend class FloatingPointVariableModifier;
+
+	FloatingPointVariableStorage();
+
+	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+
+	Common::SharedPtr<VariableStorage> clone() const override;
+
 private:
 	class SaveLoad : public ModifierSaveLoad {
 	public:
-		explicit SaveLoad(FloatingPointVariableModifier *modifier);
+		explicit SaveLoad(FloatingPointVariableStorage *storage);
 
 	private:
 		void commitLoad() const override;
 		void saveInternal(Common::WriteStream *stream) const override;
 		bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override;
 
-		FloatingPointVariableModifier *_modifier;
+		FloatingPointVariableStorage *_storage;
 		double _value;
 	};
 
-	Common::SharedPtr<Modifier> shallowClone() const override;
-	const char *getDefaultName() const override;
-
 	double _value;
 };
 
 class StringVariableModifier : public VariableModifier {
 public:
-	bool load(ModifierLoaderContext &context, const Data::StringVariableModifier &data);
+	StringVariableModifier();
 
-	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+	bool load(ModifierLoaderContext &context, const Data::StringVariableModifier &data);
 
 	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
 	void varGetValue(DynamicValue &dest) const override;
@@ -1245,62 +1310,88 @@ public:
 	void debugInspect(IDebugInspectionReport *report) const override;
 #endif
 
+private:
+	Common::SharedPtr<Modifier> shallowClone() const override;
+	const char *getDefaultName() const override;
+};
+
+class StringVariableStorage : public VariableStorage {
+public:
+	friend class StringVariableModifier;
+
+	StringVariableStorage();
+
+	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+
+	Common::SharedPtr<VariableStorage> clone() const override;
+
 private:
 	class SaveLoad : public ModifierSaveLoad {
 	public:
-		explicit SaveLoad(StringVariableModifier *modifier);
+		explicit SaveLoad(StringVariableStorage *storage);
 
 	private:
 		void commitLoad() const override;
 		void saveInternal(Common::WriteStream *stream) const override;
 		bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override;
 
-		StringVariableModifier *_modifier;
+		StringVariableStorage *_storage;
 		Common::String _value;
 	};
 
-	Common::SharedPtr<Modifier> shallowClone() const override;
-	const char *getDefaultName() const override;
-
 	Common::String _value;
 };
 
 class ObjectReferenceVariableModifierV1 : public VariableModifier {
 public:
+	ObjectReferenceVariableModifierV1();
+
 	bool load(ModifierLoaderContext &context, const Data::ObjectReferenceVariableModifierV1 &data);
 
+	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
+	void varGetValue(DynamicValue &dest) const override;
+
 	bool respondsToEvent(const Event &evt) const override;
 	VThreadState consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
 
-	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
-
-	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
-	void varGetValue(DynamicValue &dest) const override;
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Object Reference Variable Modifier V1"; }
 	SupportStatus debugGetSupportStatus() const override { return kSupportStatusNone; }
 #endif
 
+private:
+	Common::SharedPtr<Modifier> shallowClone() const override;
+	const char *getDefaultName() const override;
+
+	Event _setToSourcesParentWhen;
+};
+
+class ObjectReferenceVariableV1Storage : public VariableStorage {
+public:
+	friend class ObjectReferenceVariableModifierV1;
+
+	ObjectReferenceVariableV1Storage();
+
+	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+
+	Common::SharedPtr<VariableStorage> clone() const override;
+
 private:
 	class SaveLoad : public ModifierSaveLoad {
 	public:
-		explicit SaveLoad(ObjectReferenceVariableModifierV1 *modifier);
+		explicit SaveLoad(ObjectReferenceVariableV1Storage *storage);
 
 	private:
 		void commitLoad() const override;
 		void saveInternal(Common::WriteStream *stream) const override;
 		bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override;
 
-		ObjectReferenceVariableModifierV1 *_modifier;
+		ObjectReferenceVariableV1Storage *_storage;
 		Common::WeakPtr<RuntimeObject> _value;
 	};
 
-	Common::SharedPtr<Modifier> shallowClone() const override;
-	const char *getDefaultName() const override;
-
 	Common::WeakPtr<RuntimeObject> _value;
-	Event _setToSourcesParentWhen;
 };
 
 }	// End of namespace MTropolis
diff --git a/engines/mtropolis/plugin/standard.cpp b/engines/mtropolis/plugin/standard.cpp
index 3c2ab192a8c..db64a446938 100644
--- a/engines/mtropolis/plugin/standard.cpp
+++ b/engines/mtropolis/plugin/standard.cpp
@@ -1960,7 +1960,7 @@ void MediaCueMessengerModifier::visitInternalReferences(IStructuralReferenceVisi
 	_mediaCue.send.visitInternalReferences(visitor);
 }
 
-ObjectReferenceVariableModifier::ObjectReferenceVariableModifier() {
+ObjectReferenceVariableModifier::ObjectReferenceVariableModifier() : VariableModifier(Common::SharedPtr<VariableStorage>(new ObjectReferenceVariableStorage())) {
 }
 
 bool ObjectReferenceVariableModifier::load(const PlugInModifierLoaderContext &context, const Data::Standard::ObjectReferenceVariableModifier &data) {
@@ -1970,36 +1970,36 @@ bool ObjectReferenceVariableModifier::load(const PlugInModifierLoaderContext &co
 	if (!_setToSourceParentWhen.load(data.setToSourceParentWhen.value.asEvent))
 		return false;
 
+	ObjectReferenceVariableStorage *storage = static_cast<ObjectReferenceVariableStorage *>(_storage.get());
+
 	if (data.objectPath.type == Data::PlugInTypeTaggedValue::kString)
-		_objectPath = data.objectPath.value.asString;
+		storage->_objectPath = data.objectPath.value.asString;
 	else if (data.objectPath.type != Data::PlugInTypeTaggedValue::kNull)
 		return false;
 
-	_object.reset();
+	storage->_object.reset();
 
 	return true;
 }
 
-Common::SharedPtr<ModifierSaveLoad> ObjectReferenceVariableModifier::getSaveLoad() {
-	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
-}
-
 // Object reference variables are somewhat unusual in that they don't store a simple value,
 // they instead have "object" and "path" attributes AND as a value, they resolve to the
 // modifier itself.
 bool ObjectReferenceVariableModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
+	ObjectReferenceVariableStorage *storage = static_cast<ObjectReferenceVariableStorage *>(_storage.get());
+
 	if (attrib == "path") {
-		result.setString(_objectPath);
+		result.setString(storage->_objectPath);
 		return true;
 	}
 	if (attrib == "object") {
-		if (_object.object.expired())
+		if (storage->_object.object.expired())
 			resolve(thread->getRuntime());
 
-		if (_object.object.expired())
+		if (storage->_object.object.expired())
 			result.clear();
 		else
-			result.setObject(_object);
+			result.setObject(storage->_object);
 		return true;
 	}
 
@@ -2041,8 +2041,10 @@ void ObjectReferenceVariableModifier::varGetValue(DynamicValue &dest) const {
 void ObjectReferenceVariableModifier::debugInspect(IDebugInspectionReport *report) const {
 	VariableModifier::debugInspect(report);
 
-	report->declareDynamic("path", _objectPath);
-	report->declareDynamic("fullPath", _fullPath);
+	ObjectReferenceVariableStorage *storage = static_cast<ObjectReferenceVariableStorage *>(_storage.get());
+
+	report->declareDynamic("path", storage->_objectPath);
+	report->declareDynamic("fullPath", storage->_fullPath);
 }
 #endif
 
@@ -2058,17 +2060,21 @@ MiniscriptInstructionOutcome ObjectReferenceVariableModifier::scriptSetPath(Mini
 	if (value.getType() != DynamicValueTypes::kString)
 		return kMiniscriptInstructionOutcomeFailed;
 
-	_objectPath = value.getString();
-	_object.reset();
+	ObjectReferenceVariableStorage *storage = static_cast<ObjectReferenceVariableStorage *>(_storage.get());
+
+	storage->_objectPath = value.getString();
+	storage->_object.reset();
 
 	return kMiniscriptInstructionOutcomeContinue;
 }
 
 MiniscriptInstructionOutcome ObjectReferenceVariableModifier::scriptSetObject(MiniscriptThread *thread, const DynamicValue &value) {
+	ObjectReferenceVariableStorage *storage = static_cast<ObjectReferenceVariableStorage *>(_storage.get());
+
 	if (value.getType() == DynamicValueTypes::kNull) {
-		_object.reset();
-		_objectPath.clear();
-		_fullPath.clear();
+		storage->_object.reset();
+		storage->_objectPath.clear();
+		storage->_fullPath.clear();
 
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (value.getType() == DynamicValueTypes::kObject) {
@@ -2076,11 +2082,11 @@ MiniscriptInstructionOutcome ObjectReferenceVariableModifier::scriptSetObject(Mi
 		if (!obj)
 			return scriptSetObject(thread, DynamicValue());
 
-		if (!computeObjectPath(obj.get(), _fullPath))
+		if (!computeObjectPath(obj.get(), storage->_fullPath))
 			return scriptSetObject(thread, DynamicValue());
 
-		_objectPath = _fullPath;
-		_object.object = obj;
+		storage->_objectPath = storage->_fullPath;
+		storage->_object.object = obj;
 
 		return kMiniscriptInstructionOutcomeContinue;
 	} else
@@ -2088,52 +2094,60 @@ MiniscriptInstructionOutcome ObjectReferenceVariableModifier::scriptSetObject(Mi
 }
 
 MiniscriptInstructionOutcome ObjectReferenceVariableModifier::scriptObjectRefAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib) {
+	ObjectReferenceVariableStorage *storage = static_cast<ObjectReferenceVariableStorage *>(_storage.get());
+
 	resolve(thread->getRuntime());
 
-	if (_object.object.expired()) {
+	if (storage->_object.object.expired()) {
 		thread->error("Attempted to reference an attribute of an object variable object, but the reference is dead");
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	return _object.object.lock()->writeRefAttribute(thread, proxy, attrib);
+	return storage->_object.object.lock()->writeRefAttribute(thread, proxy, attrib);
 }
 
 MiniscriptInstructionOutcome ObjectReferenceVariableModifier::scriptObjectRefAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib, const DynamicValue &index) {
+	ObjectReferenceVariableStorage *storage = static_cast<ObjectReferenceVariableStorage *>(_storage.get());
+
 	resolve(thread->getRuntime());
 
-	if (_object.object.expired()) {
+	if (storage->_object.object.expired()) {
 		thread->error("Attempted to reference an attribute of an object variable object, but the reference is dead");
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	return _object.object.lock()->writeRefAttributeIndexed(thread, proxy, attrib, index);
+	return storage->_object.object.lock()->writeRefAttributeIndexed(thread, proxy, attrib, index);
 }
 
 void ObjectReferenceVariableModifier::resolve(Runtime *runtime) {
-	if (!_object.object.expired())
+	ObjectReferenceVariableStorage *storage = static_cast<ObjectReferenceVariableStorage *>(_storage.get());
+
+	if (!storage->_object.object.expired())
 		return;
 
-	_fullPath.clear();
-	_object.reset();
+	storage->_fullPath.clear();
+	storage->_object.reset();
 
-	if (_objectPath.size() == 0)
+	if (storage->_objectPath.size() == 0)
 		return;
 
-	if (_objectPath[0] == '/')
+	if (storage->_objectPath[0] == '/')
 		resolveAbsolutePath(runtime);
-	else if (_objectPath[0] == '.')
-		resolveRelativePath(this, _objectPath, 0);
+	else if (storage->_objectPath[0] == '.')
+		resolveRelativePath(this, storage->_objectPath, 0);
 	else
 		warning("Object reference variable had an unknown path format");
 
-	if (!_object.object.expired()) {
-		if (!computeObjectPath(_object.object.lock().get(), _fullPath)) {
-			_object.reset();
+	if (!storage->_object.object.expired()) {
+		if (!computeObjectPath(storage->_object.object.lock().get(), storage->_fullPath)) {
+			storage->_object.reset();
 		}
 	}
 }
 
 void ObjectReferenceVariableModifier::resolveRelativePath(RuntimeObject *obj, const Common::String &path, size_t startPos) {
+	ObjectReferenceVariableStorage *storage = static_cast<ObjectReferenceVariableStorage *>(_storage.get());
+
 	bool haveNextLevel = true;
 	size_t nextLevelPos = startPos;
 
@@ -2197,11 +2211,13 @@ void ObjectReferenceVariableModifier::resolveRelativePath(RuntimeObject *obj, co
 			return;
 	}
 
-	_object.object = obj->getSelfReference();
+	storage->_object.object = obj->getSelfReference();
 }
 
 void ObjectReferenceVariableModifier::resolveAbsolutePath(Runtime *runtime) {
-	assert(_objectPath[0] == '/');
+	ObjectReferenceVariableStorage *storage = static_cast<ObjectReferenceVariableStorage *>(_storage.get());
+
+	assert(storage->_objectPath[0] == '/');
 
 	RuntimeObject *project = this;
 	for (;;) {
@@ -2219,7 +2235,7 @@ void ObjectReferenceVariableModifier::resolveAbsolutePath(Runtime *runtime) {
 	bool foundPrefix = false;
 
 	if (runtime->getHacks().ignoreMismatchedProjectNameInObjectLookups) {
-		size_t slashOffset = _objectPath.findFirstOf('/', 1);
+		size_t slashOffset = storage->_objectPath.findFirstOf('/', 1);
 		if (slashOffset != Common::String::npos) {
 			prefixEnd = slashOffset;
 			foundPrefix = true;
@@ -2230,7 +2246,7 @@ void ObjectReferenceVariableModifier::resolveAbsolutePath(Runtime *runtime) {
 			"/<project>"};
 
 		for (const Common::String &prefix : projectPrefixes) {
-			if (_objectPath.size() >= prefix.size() && caseInsensitiveEqual(_objectPath.substr(0, prefix.size()), prefix)) {
+			if (storage->_objectPath.size() >= prefix.size() && caseInsensitiveEqual(storage->_objectPath.substr(0, prefix.size()), prefix)) {
 				prefixEnd = prefix.size();
 				foundPrefix = true;
 				break;
@@ -2242,15 +2258,15 @@ void ObjectReferenceVariableModifier::resolveAbsolutePath(Runtime *runtime) {
 		return;
 
 	// If the object path is longer, then there must be a slash separator, otherwise this doesn't match the project
-	if (prefixEnd == _objectPath.size()) {
-		_object = ObjectReference(project->getSelfReference());
+	if (prefixEnd == storage->_objectPath.size()) {
+		storage->_object = ObjectReference(project->getSelfReference());
 		return;
 	}
 
-	if (_objectPath[prefixEnd] != '/')
+	if (storage->_objectPath[prefixEnd] != '/')
 		return;
 
-	return resolveRelativePath(project, _objectPath, prefixEnd + 1);
+	return resolveRelativePath(project, storage->_objectPath, prefixEnd + 1);
 }
 
 bool ObjectReferenceVariableModifier::computeObjectPath(RuntimeObject *obj, Common::String &outPath) {
@@ -2301,22 +2317,36 @@ MiniscriptInstructionOutcome ObjectReferenceVariableModifier::ObjectWriteInterfa
 	return static_cast<ObjectReferenceVariableModifier *>(objectRef)->scriptObjectRefAttribIndexed(thread, proxy, attrib, index);
 }
 
-ObjectReferenceVariableModifier::SaveLoad::SaveLoad(ObjectReferenceVariableModifier *modifier) : _modifier(modifier) {
-	_objectPath = _modifier->_objectPath;
+ObjectReferenceVariableStorage::SaveLoad::SaveLoad(ObjectReferenceVariableStorage *storage) : _storage(storage) {
+	_objectPath = _storage->_objectPath;
+}
+
+
+
+
+ObjectReferenceVariableStorage::ObjectReferenceVariableStorage() {
+}
+
+Common::SharedPtr<ModifierSaveLoad> ObjectReferenceVariableStorage::getSaveLoad() {
+	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
+}
+
+Common::SharedPtr<VariableStorage> ObjectReferenceVariableStorage::clone() const {
+	return Common::SharedPtr<VariableStorage>(new ObjectReferenceVariableStorage(*this));
 }
 
-void ObjectReferenceVariableModifier::SaveLoad::commitLoad() const {
-	_modifier->_object.reset();
-	_modifier->_fullPath.clear();
-	_modifier->_objectPath = _objectPath;
+void ObjectReferenceVariableStorage::SaveLoad::commitLoad() const {
+	_storage->_object.reset();
+	_storage->_fullPath.clear();
+	_storage->_objectPath = _objectPath;
 }
 
-void ObjectReferenceVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) const {
+void ObjectReferenceVariableStorage::SaveLoad::saveInternal(Common::WriteStream *stream) const {
 	stream->writeUint32BE(_objectPath.size());
 	stream->writeString(_objectPath);
 }
 
-bool ObjectReferenceVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
+bool ObjectReferenceVariableStorage::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
 	uint32 stringLen = stream->readUint32BE();
 	if (stream->err())
 		return false;
@@ -2721,29 +2751,31 @@ MiniscriptInstructionOutcome MidiModifier::MuteTrackProxyInterface::refAttribInd
 	return kMiniscriptInstructionOutcomeFailed;
 }
 
-ListVariableModifier::ListVariableModifier() : _list(new DynamicList()), _preferredContentType(DynamicValueTypes::kInteger) {
+ListVariableModifier::ListVariableModifier() : VariableModifier(Common::SharedPtr<VariableStorage>(new ListVariableStorage())) {
 }
 
 bool ListVariableModifier::load(const PlugInModifierLoaderContext &context, const Data::Standard::ListVariableModifier &data) {
-	_preferredContentType = DynamicValueTypes::kInvalid;
+	ListVariableStorage *storage = static_cast<ListVariableStorage *>(_storage.get());
+
+	storage->_preferredContentType = DynamicValueTypes::kInvalid;
 	switch (data.contentsType) {
 	case Data::Standard::ListVariableModifier::kContentsTypeInteger:
-		_preferredContentType = DynamicValueTypes::kInteger;
+		storage->_preferredContentType = DynamicValueTypes::kInteger;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypePoint:
-		_preferredContentType = DynamicValueTypes::kPoint;
+		storage->_preferredContentType = DynamicValueTypes::kPoint;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeRange:
-		_preferredContentType = DynamicValueTypes::kIntegerRange;
+		storage->_preferredContentType = DynamicValueTypes::kIntegerRange;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeFloat:
-		_preferredContentType = DynamicValueTypes::kFloat;
+		storage->_preferredContentType = DynamicValueTypes::kFloat;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeString:
-		_preferredContentType = DynamicValueTypes::kString;
+		storage->_preferredContentType = DynamicValueTypes::kString;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeObject:
-		_preferredContentType = DynamicValueTypes::kObject;
+		storage->_preferredContentType = DynamicValueTypes::kObject;
 		if (data.persistentValuesGarbled) {
 			// Ignore and let the game fix it
 			return true;
@@ -2753,10 +2785,10 @@ bool ListVariableModifier::load(const PlugInModifierLoaderContext &context, cons
 		}
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeVector:
-		_preferredContentType = DynamicValueTypes::kVector;
+		storage->_preferredContentType = DynamicValueTypes::kVector;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeBoolean:
-		_preferredContentType = DynamicValueTypes::kBoolean;
+		storage->_preferredContentType = DynamicValueTypes::kBoolean;
 		break;
 	default:
 		warning("Unknown list data type");
@@ -2771,12 +2803,12 @@ bool ListVariableModifier::load(const PlugInModifierLoaderContext &context, cons
 		if (!dynValue.loadConstant(data.values[i]))
 			return false;
 
-		if (dynValue.getType() != _preferredContentType) {
+		if (dynValue.getType() != storage->_preferredContentType) {
 			warning("List mod initialization element had the wrong type");
 			return false;
 		}
 
-		if (!_list->setAtIndex(i, dynValue)) {
+		if (!storage->_list->setAtIndex(i, dynValue)) {
 			warning("Failed to initialize list modifier, value was rejected");
 			return false;
 		}
@@ -2789,11 +2821,9 @@ bool ListVariableModifier::isListVariable() const {
 	return true;
 }
 
-Common::SharedPtr<ModifierSaveLoad> ListVariableModifier::getSaveLoad() {
-	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
-}
-
 bool ListVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
+	ListVariableStorage *storage = static_cast<ListVariableStorage *>(_storage.get());
+
 	if (value.getType() == DynamicValueTypes::kList) {
 		// Source value is a list.  In this case, it must be convertible, or an error occurs.
 		Common::SharedPtr<DynamicList> sourceList = value.getList();
@@ -2805,7 +2835,7 @@ bool ListVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicVa
 
 			DynamicValue convertedElement;
 
-			if (!sourceElement.convertToType(_preferredContentType, convertedElement)) {
+			if (!sourceElement.convertToType(storage->_preferredContentType, convertedElement)) {
 				thread->error("Failed to convert list when assigning to a list variable");
 				return false;
 			}
@@ -2813,12 +2843,12 @@ bool ListVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicVa
 			newList->setAtIndex(i, convertedElement);
 		}
 
-		_list = newList;
+		storage->_list = newList;
 	} else if (value.getType() == DynamicValueTypes::kObject) {
 		// Source value is an object.  In this case, it must be another list, otherwise this fails without an error.
 		RuntimeObject *obj = value.getObject().object.lock().get();
 		if (obj && obj->isModifier() && static_cast<Modifier *>(obj)->isVariable() && static_cast<VariableModifier *>(obj)->isListVariable()) {
-			Common::SharedPtr<DynamicList> sourceList = static_cast<ListVariableModifier *>(obj)->_list;
+			Common::SharedPtr<DynamicList> sourceList = static_cast<ListVariableStorage *>(static_cast<ListVariableModifier *>(obj)->_storage.get())->_list;
 			Common::SharedPtr<DynamicList> newList(new DynamicList());
 
 			bool failed = false;
@@ -2828,7 +2858,7 @@ bool ListVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicVa
 
 				DynamicValue convertedElement;
 
-				if (!sourceElement.convertToType(_preferredContentType, convertedElement)) {
+				if (!sourceElement.convertToType(storage->_preferredContentType, convertedElement)) {
 					warning("Failed to convert list when assigning to a list variable.  (Non-fatal since it was directly assigned.)");
 					failed = true;
 					break;
@@ -2838,16 +2868,16 @@ bool ListVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicVa
 			}
 
 			if (!failed)
-				_list = newList;
+				storage->_list = newList;
 		}
 	} else {
 		// Source value is a non-list.  In this case, it must be exactly the correct type, except for numbers.
 
 		DynamicValue convertedValue;
-		if (value.convertToType(_preferredContentType, convertedValue)) {
+		if (value.convertToType(storage->_preferredContentType, convertedValue)) {
 			Common::SharedPtr<DynamicList> newList(new DynamicList());
 			newList->setAtIndex(0, convertedValue);
-			_list = newList;
+			storage->_list = newList;
 		} else {
 			thread->error("Can't assign incompatible value type to a list variable");
 			return false;
@@ -2862,32 +2892,34 @@ void ListVariableModifier::varGetValue(DynamicValue &dest) const {
 }
 
 bool ListVariableModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
+	ListVariableStorage *storage = static_cast<ListVariableStorage *>(_storage.get());
+
 	if (attrib == "count") {
-		result.setInt(_list->getSize());
+		result.setInt(storage->_list->getSize());
 		return true;
 	} else if (attrib == "random") {
-		if (_list->getSize() == 0)
+		if (storage->_list->getSize() == 0)
 			return false;
 
-		size_t index = thread->getRuntime()->getRandom()->getRandomNumber(_list->getSize() - 1);
-		return _list->getAtIndex(index, result);
+		size_t index = thread->getRuntime()->getRandom()->getRandomNumber(storage->_list->getSize() - 1);
+		return storage->_list->getAtIndex(index, result);
 	} else if (attrib == "shuffle") {
-		_list = _list->clone();
+		storage->_list = storage->_list->clone();
 
 		Common::RandomSource *rng = thread->getRuntime()->getRandom();
 
-		size_t listSize = _list->getSize();
+		size_t listSize = storage->_list->getSize();
 		for (size_t i = 1; i < listSize; i++) {
 			size_t sourceIndex = i;
-			size_t destIndex = rng->getRandomNumber(static_cast<uint>(listSize - 1 - i));
+			size_t destIndex = sourceIndex + rng->getRandomNumber(static_cast<uint>(listSize - 1 - i));
 			if (sourceIndex != destIndex) {
 				DynamicValue srcValue;
 				DynamicValue destValue;
-				(void)_list->getAtIndex(sourceIndex, srcValue);
-				(void)_list->getAtIndex(destIndex, destValue);
+				(void)storage->_list->getAtIndex(sourceIndex, srcValue);
+				(void)storage->_list->getAtIndex(destIndex, destValue);
 
-				(void)_list->setAtIndex(destIndex, srcValue);
-				(void)_list->setAtIndex(sourceIndex, destValue);
+				(void)storage->_list->setAtIndex(destIndex, srcValue);
+				(void)storage->_list->setAtIndex(sourceIndex, destValue);
 			}
 		}
 
@@ -2899,9 +2931,11 @@ bool ListVariableModifier::readAttribute(MiniscriptThread *thread, DynamicValue
 }
 
 bool ListVariableModifier::readAttributeIndexed(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib, const DynamicValue &index) {
+	ListVariableStorage *storage = static_cast<ListVariableStorage *>(_storage.get());
+
 	if (attrib == "value") {
 		size_t realIndex = 0;
-		return _list->dynamicValueToIndex(realIndex, index) && _list->getAtIndex(realIndex, result);
+		return storage->_list->dynamicValueToIndex(realIndex, index) && storage->_list->getAtIndex(realIndex, result);
 	}
 	return Modifier::readAttributeIndexed(thread, result, attrib, index);
 }
@@ -2916,13 +2950,15 @@ MiniscriptInstructionOutcome ListVariableModifier::writeRefAttribute(MiniscriptT
 }
 
 MiniscriptInstructionOutcome ListVariableModifier::writeRefAttributeIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib, const DynamicValue &index) {
+	ListVariableStorage *storage = static_cast<ListVariableStorage *>(_storage.get());
+
 	if (attrib == "value") {
 		size_t realIndex = 0;
-		if (!_list->dynamicValueToIndex(realIndex, index))
+		if (!storage->_list->dynamicValueToIndex(realIndex, index))
 			return kMiniscriptInstructionOutcomeFailed;
 
-		_list->createWriteProxyForIndex(realIndex, writeProxy);
-		writeProxy.containerList = _list;
+		storage->_list->createWriteProxyForIndex(realIndex, writeProxy);
+		writeProxy.containerList = storage->_list;
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	return kMiniscriptInstructionOutcomeFailed;
@@ -2932,28 +2968,30 @@ MiniscriptInstructionOutcome ListVariableModifier::writeRefAttributeIndexed(Mini
 void ListVariableModifier::debugInspect(IDebugInspectionReport *report) const {
 	VariableModifier::debugInspect(report);
 
-	size_t listSize = _list->getSize();
+	ListVariableStorage *storage = static_cast<ListVariableStorage *>(_storage.get());
+
+	size_t listSize = storage->_list->getSize();
 
 	for (size_t i = 0; i < listSize; i++) {
 		int cardinal = i + 1;
-		switch (_list->getType()) {
+		switch (storage->_list->getType()) {
 		case DynamicValueTypes::kInteger:
-			report->declareLoose(Common::String::format("[%i] = %i", cardinal, _list->getInt()[i]));
+			report->declareLoose(Common::String::format("[%i] = %i", cardinal, storage->_list->getInt()[i]));
 			break;
 		case DynamicValueTypes::kFloat:
-			report->declareLoose(Common::String::format("[%i] = %g", cardinal, _list->getFloat()[i]));
+			report->declareLoose(Common::String::format("[%i] = %g", cardinal, storage->_list->getFloat()[i]));
 			break;
 		case DynamicValueTypes::kPoint:
-			report->declareLoose(Common::String::format("[%i] = ", cardinal) + pointToString(_list->getPoint()[i]));
+			report->declareLoose(Common::String::format("[%i] = ", cardinal) + pointToString(storage->_list->getPoint()[i]));
 			break;
 		case DynamicValueTypes::kIntegerRange:
-			report->declareLoose(Common::String::format("[%i] = ", cardinal) + _list->getIntRange()[i].toString());
+			report->declareLoose(Common::String::format("[%i] = ", cardinal) + storage->_list->getIntRange()[i].toString());
 			break;
 		case DynamicValueTypes::kBoolean:
-			report->declareLoose(Common::String::format("[%i] = %s", cardinal, _list->getBool()[i] ? "true" : "false"));
+			report->declareLoose(Common::String::format("[%i] = %s", cardinal, storage->_list->getBool()[i] ? "true" : "false"));
 			break;
 		case DynamicValueTypes::kVector:
-			report->declareLoose(Common::String::format("[%i] = ", cardinal) + _list->getVector()[i].toString());
+			report->declareLoose(Common::String::format("[%i] = ", cardinal) + storage->_list->getVector()[i].toString());
 			break;
 		case DynamicValueTypes::kLabel:
 			report->declareLoose(Common::String::format("[%i] = Label?", cardinal));
@@ -2962,7 +3000,7 @@ void ListVariableModifier::debugInspect(IDebugInspectionReport *report) const {
 			report->declareLoose(Common::String::format("[%i] = Event?", cardinal));
 			break;
 		case DynamicValueTypes::kString:
-			report->declareLoose(Common::String::format("[%i] = ", cardinal) + _list->getString()[i]);
+			report->declareLoose(Common::String::format("[%i] = ", cardinal) + storage->_list->getString()[i]);
 			break;
 		case DynamicValueTypes::kList:
 			report->declareLoose(Common::String::format("[%i] = List", cardinal));
@@ -2978,12 +3016,9 @@ void ListVariableModifier::debugInspect(IDebugInspectionReport *report) const {
 }
 #endif
 
-ListVariableModifier::ListVariableModifier(const ListVariableModifier &other) : VariableModifier(other), _preferredContentType(other._preferredContentType) {
-	if (other._list)
-		_list = other._list->clone();
-}
-
 MiniscriptInstructionOutcome ListVariableModifier::scriptSetCount(MiniscriptThread *thread, const DynamicValue &value) {
+	ListVariableStorage *storage = static_cast<ListVariableStorage *>(_storage.get());
+
 	int32 asInteger = 0;
 	if (!value.roundToInt(asInteger)) {
 		thread->error("Tried to set a list variable count to something other than an integer");
@@ -2996,15 +3031,15 @@ MiniscriptInstructionOutcome ListVariableModifier::scriptSetCount(MiniscriptThre
 	}
 
 	size_t newSize = asInteger;
-	if (newSize > _list->getSize()) {
-		if (_list->getSize() == 0) {
+	if (newSize > storage->_list->getSize()) {
+		if (storage->_list->getSize() == 0) {
 			thread->error("Restoring an empty list by setting its count isn't implemented");
 			return kMiniscriptInstructionOutcomeFailed;
 		}
 
-		_list->expandToMinimumSize(newSize);
-	} else if (newSize < _list->getSize()) {
-		_list->truncateToSize(newSize);
+		storage->_list->expandToMinimumSize(newSize);
+	} else if (newSize < storage->_list->getSize()) {
+		storage->_list->truncateToSize(newSize);
 	}
 
 	return kMiniscriptInstructionOutcomeContinue;
@@ -3018,21 +3053,35 @@ const char *ListVariableModifier::getDefaultName() const {
 	return "List Variable";
 }
 
-ListVariableModifier::SaveLoad::SaveLoad(ListVariableModifier *modifier) : _modifier(modifier), _list(_modifier->_list) {
+ListVariableStorage::ListVariableStorage() : _preferredContentType(DynamicValueTypes::kInteger), _list(new DynamicList()) {
+}
+
+Common::SharedPtr<ModifierSaveLoad> ListVariableStorage::getSaveLoad() {
+	return Common::SharedPtr<ModifierSaveLoad>(new SaveLoad(this));
+}
+
+Common::SharedPtr<VariableStorage> ListVariableStorage::clone() const {
+	ListVariableStorage *newInstance = new ListVariableStorage();
+	newInstance->_list = Common::SharedPtr<DynamicList>(new DynamicList(*_list));
+	newInstance->_preferredContentType = _preferredContentType;
+	return Common::SharedPtr<VariableStorage>(newInstance);
+}
+
+ListVariableStorage::SaveLoad::SaveLoad(ListVariableStorage *storage) : _storage(storage), _list(storage->_list) {
 }
 
-void ListVariableModifier::SaveLoad::commitLoad() const {
+void ListVariableStorage::SaveLoad::commitLoad() const {
 	// We don't support deserializing object references (yet?), so just leave the existing values.
 	// In Obsidian at least, this doesn't matter.
 	if (_list->getType() != DynamicValueTypes::kObject)
-		_modifier->_list = _list;
+		_storage->_list = _list;
 }
 
-void ListVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) const {
+void ListVariableStorage::SaveLoad::saveInternal(Common::WriteStream *stream) const {
 	recursiveWriteList(_list.get(), stream);
 }
 
-bool ListVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
+bool ListVariableStorage::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) {
 	Common::SharedPtr<DynamicList> list = recursiveReadList(stream);
 	if (list) {
 		_list = list;
@@ -3042,7 +3091,7 @@ bool ListVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, ui
 	}
 }
 
-void ListVariableModifier::SaveLoad::recursiveWriteList(DynamicList *list, Common::WriteStream *stream) {
+void ListVariableStorage::SaveLoad::recursiveWriteList(DynamicList *list, Common::WriteStream *stream) {
 	stream->writeUint32BE(list->getType());
 	stream->writeUint32BE(list->getSize());
 
@@ -3088,7 +3137,7 @@ void ListVariableModifier::SaveLoad::recursiveWriteList(DynamicList *list, Commo
 	}
 }
 
-Common::SharedPtr<DynamicList> ListVariableModifier::SaveLoad::recursiveReadList(Common::ReadStream *stream) {
+Common::SharedPtr<DynamicList> ListVariableStorage::SaveLoad::recursiveReadList(Common::ReadStream *stream) {
 	Common::SharedPtr<DynamicList> list;
 	list.reset(new DynamicList());
 
diff --git a/engines/mtropolis/plugin/standard.h b/engines/mtropolis/plugin/standard.h
index dd12aa39927..31b2c9be5f3 100644
--- a/engines/mtropolis/plugin/standard.h
+++ b/engines/mtropolis/plugin/standard.h
@@ -175,14 +175,12 @@ public:
 
 	bool load(const PlugInModifierLoaderContext &context, const Data::Standard::ObjectReferenceVariableModifier &data);
 
-	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
+	void varGetValue(DynamicValue &dest) const override;
 
 	bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) override;
 	MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) override;
 
-	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
-	void varGetValue(DynamicValue &dest) const override;
-
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Object Reference Variable Modifier"; }
 	SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
@@ -196,19 +194,6 @@ private:
 		static MiniscriptInstructionOutcome refAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index);
 	};
 
-	class SaveLoad : public ModifierSaveLoad {
-	public:
-		explicit SaveLoad(ObjectReferenceVariableModifier *modifier);
-
-	private:
-		void commitLoad() const override;
-		void saveInternal(Common::WriteStream *stream) const override;
-		bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override;
-
-		ObjectReferenceVariableModifier *_modifier;
-		Common::String _objectPath;
-	};
-
 	Common::SharedPtr<Modifier> shallowClone() const override;
 	const char *getDefaultName() const override;
 
@@ -225,10 +210,35 @@ private:
 	static RuntimeObject *getObjectParent(RuntimeObject *obj);
 
 	Event _setToSourceParentWhen;
+};
+
+class ObjectReferenceVariableStorage : public VariableStorage {
+public:
+	friend class ObjectReferenceVariableModifier;
+
+	ObjectReferenceVariableStorage();
+
+	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
+
+	Common::SharedPtr<VariableStorage> clone() const override;
+
+private:
+	class SaveLoad : public ModifierSaveLoad {
+	public:
+		explicit SaveLoad(ObjectReferenceVariableStorage *storage);
+
+	private:
+		void commitLoad() const override;
+		void saveInternal(Common::WriteStream *stream) const override;
+		bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override;
+
+		ObjectReferenceVariableStorage *_storage;
+		Common::String _objectPath;
+	};
 
-	mutable ObjectReference _object;
-	Common::String _objectPath;
 	Common::String _fullPath;
+	Common::String _objectPath;
+	mutable ObjectReference _object;
 };
 
 class MidiModifier : public Modifier {
@@ -319,34 +329,20 @@ private:
 	MidiNotePlayer *_notePlayer;
 };
 
-class ListVariableModifier : public VariableModifier {
+class ListVariableStorage : public VariableStorage {
 public:
-	ListVariableModifier();
+	friend class ListVariableModifier;
 
-	bool load(const PlugInModifierLoaderContext &context, const Data::Standard::ListVariableModifier &data);
-
-	bool isListVariable() const override;
+	ListVariableStorage();
 
 	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
 
-	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
-	void varGetValue(DynamicValue &dest) const override;
-
-	bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) override;
-	bool readAttributeIndexed(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib, const DynamicValue &index) override;
-	MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) override;
-	MiniscriptInstructionOutcome writeRefAttributeIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib, const DynamicValue &index) override;
-
-#ifdef MTROPOLIS_DEBUG_ENABLE
-	const char *debugGetTypeName() const override { return "List Variable Modifier"; }
-	SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
-	void debugInspect(IDebugInspectionReport *report) const override;
-#endif
+	Common::SharedPtr<VariableStorage> clone() const override;
 
 private:
 	class SaveLoad : public ModifierSaveLoad {
 	public:
-		explicit SaveLoad(ListVariableModifier *modifier);
+		explicit SaveLoad(ListVariableStorage *storage);
 
 	private:
 		void commitLoad() const override;
@@ -356,20 +352,41 @@ private:
 		static void recursiveWriteList(DynamicList *list, Common::WriteStream *stream);
 		static Common::SharedPtr<DynamicList> recursiveReadList(Common::ReadStream *stream);
 
-		ListVariableModifier *_modifier;
+		ListVariableStorage *_storage;
 		Common::SharedPtr<DynamicList> _list;
 	};
 
-	ListVariableModifier(const ListVariableModifier &other);
-	ListVariableModifier &operator=(const ListVariableModifier &other);
+	Common::SharedPtr<DynamicList> _list;
+	DynamicValueTypes::DynamicValueType _preferredContentType;
+};
 
+class ListVariableModifier : public VariableModifier {
+public:
+	ListVariableModifier();
+
+	bool load(const PlugInModifierLoaderContext &context, const Data::Standard::ListVariableModifier &data);
+
+	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
+	void varGetValue(DynamicValue &dest) const override;
+
+	bool isListVariable() const override;
+
+	bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) override;
+	bool readAttributeIndexed(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib, const DynamicValue &index) override;
+	MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) override;
+	MiniscriptInstructionOutcome writeRefAttributeIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib, const DynamicValue &index) override;
+
+#ifdef MTROPOLIS_DEBUG_ENABLE
+	const char *debugGetTypeName() const override { return "List Variable Modifier"; }
+	SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
+	void debugInspect(IDebugInspectionReport *report) const override;
+#endif
+
+private:
 	MiniscriptInstructionOutcome scriptSetCount(MiniscriptThread *thread, const DynamicValue &value);
 
 	Common::SharedPtr<Modifier> shallowClone() const override;
 	const char *getDefaultName() const override;
-
-	Common::SharedPtr<DynamicList> _list;
-	DynamicValueTypes::DynamicValueType _preferredContentType;
 };
 
 class SysInfoModifier : public Modifier {
diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp
index fa35a2428f2..2c2e73a8b6a 100644
--- a/engines/mtropolis/runtime.cpp
+++ b/engines/mtropolis/runtime.cpp
@@ -2677,6 +2677,9 @@ bool WorldManagerInterface::readAttribute(MiniscriptThread *thread, DynamicValue
 	} else if (attrib == "postponeredraws") {
 		result.setBool(_postponeRedraws);
 		return true;
+	} else if (attrib == "clickcount") {
+		result.setInt(thread->getRuntime()->getMultiClickCount());
+		return true;
 	}
 
 	return RuntimeObject::readAttribute(thread, result, attrib);
@@ -4169,7 +4172,8 @@ Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, ISaveUIProvider *saveProv
 	  _cachedMousePosition(Common::Point(0, 0)), _realMousePosition(Common::Point(0, 0)), _trackedMouseOutside(false),
 	  _forceCursorRefreshOnce(true), _autoResetCursor(false), _haveModifierOverrideCursor(false), _sceneGraphChanged(false), _isQuitting(false),
 	  _collisionCheckTime(0), _defaultVolumeState(true), _activeSceneTransitionEffect(nullptr), _sceneTransitionStartTime(0), _sceneTransitionEndTime(0),
-	  _sharedSceneWasSetExplicitly(false), _modifierOverrideCursorID(0), _subtitleRenderer(subRenderer) {
+	  _sharedSceneWasSetExplicitly(false), _modifierOverrideCursorID(0), _subtitleRenderer(subRenderer), _multiClickStartTime(0), _multiClickInterval(500), _multiClickCount(0)
+{
 	_random.reset(new Common::RandomSource("mtropolis"));
 
 	_vthread.reset(new VThread());
@@ -4273,6 +4277,14 @@ bool Runtime::runFrame() {
 			case kOSEventTypeMouseMove: {
 					MouseInputEvent *mouseEvt = static_cast<MouseInputEvent *>(evt.get());
 
+					if (evtType == kOSEventTypeMouseDown) {
+						if (_multiClickStartTime + _multiClickInterval >= _playTime) {
+							_multiClickStartTime = _playTime;
+							_multiClickCount = 1;
+						} else
+							_multiClickCount++;
+					}
+
 					// Maybe shouldn't post the update mouse button task if non-left buttons are pressed?
 					if ((evtType == kOSEventTypeMouseDown || evtType == kOSEventTypeMouseUp) && mouseEvt->getButton() == Actions::kMouseButtonLeft) {
 						UpdateMouseStateTaskData *taskData = _vthread->pushTask("Runtime::updateMouseStateTask", this, &Runtime::updateMouseStateTask);
@@ -5688,18 +5700,22 @@ void Runtime::instantiateIfAlias(Common::SharedPtr<Modifier> &modifier, const Co
 			error("Failed to resolve alias");
 		}
 
-		if (!modifier->isVariable()) {
-			Common::SharedPtr<Modifier> clonedModifier = templateModifier->shallowClone();
-			clonedModifier->setSelfReference(clonedModifier);
-			clonedModifier->setRuntimeGUID(allocateRuntimeGUID());
+		Common::SharedPtr<Modifier> clonedModifier = templateModifier->shallowClone();
+		clonedModifier->setSelfReference(clonedModifier);
+		clonedModifier->setRuntimeGUID(allocateRuntimeGUID());
 
-			clonedModifier->setName(modifier->getName());
+		clonedModifier->setName(modifier->getName());
 
-			modifier = clonedModifier;
-			clonedModifier->setParent(relinkParent);
+		modifier = clonedModifier;
+		clonedModifier->setParent(relinkParent);
 
-			ModifierChildCloner cloner(this, clonedModifier);
-			clonedModifier->visitInternalReferences(&cloner);
+		ModifierChildCloner cloner(this, clonedModifier);
+		clonedModifier->visitInternalReferences(&cloner);
+
+		// Aliased variables use the same variable storage, but are treated as distinct objects.
+		if (clonedModifier->isVariable()) {
+ 			assert(templateModifier->isVariable());
+			static_cast<VariableModifier *>(clonedModifier.get())->setStorage(static_cast<const VariableModifier *>(templateModifier.get())->getStorage());
 		}
 	}
 }
@@ -5830,6 +5846,10 @@ void Runtime::setAutoResetCursor(bool enabled) {
 	_autoResetCursor = enabled;
 }
 
+uint Runtime::getMultiClickCount() const {
+	return _multiClickCount;
+}
+
 bool Runtime::isAwaitingSceneTransition() const {
 	return _sceneTransitionState != kSceneTransitionStateNotTransitioning;
 }
@@ -6899,6 +6919,15 @@ Common::SharedPtr<Modifier> Project::resolveAlias(uint32 aliasID) const {
 	return _globalModifiers.getModifiers()[aliasID - 1];
 }
 
+Common::SharedPtr<Modifier> Project::findGlobalVarWithName(const Common::String &name) const {
+	for (const Common::SharedPtr<Modifier> &modifier : _globalModifiers.getModifiers()) {
+		if (modifier && modifier->isVariable() && MTropolis::caseInsensitiveEqual(name, modifier->getName()))
+			return modifier;
+	}
+
+	return nullptr;
+}
+
 void Project::materializeGlobalVariables(Runtime *runtime, ObjectLinkingScope *outerScope) {
 	for (Common::Array<Common::SharedPtr<Modifier> >::const_iterator it = _globalModifiers.getModifiers().begin(), itEnd = _globalModifiers.getModifiers().end(); it != itEnd; ++it) {
 		Modifier *modifier = it->get();
@@ -8660,6 +8689,15 @@ void Modifier::debugInspect(IDebugInspectionReport *report) const {
 
 #endif /* MTROPOLIS_DEBUG_ENABLE */
 
+VariableStorage::~VariableStorage() {
+}
+
+VariableModifier::VariableModifier(const Common::SharedPtr<VariableStorage> &storage) : _storage(storage) {
+}
+
+VariableModifier::VariableModifier(const VariableModifier &other) : Modifier(other), _storage(other._storage->clone()) {
+}
+
 bool VariableModifier::isVariable() const {
 	return true;
 }
@@ -8668,6 +8706,19 @@ bool VariableModifier::isListVariable() const {
 	return false;
 }
 
+
+Common::SharedPtr<ModifierSaveLoad> VariableModifier::getSaveLoad() {
+	return _storage->getSaveLoad();
+}
+
+const Common::SharedPtr<VariableStorage> &VariableModifier::getStorage() const {
+	return _storage;
+}
+
+void VariableModifier::setStorage(const Common::SharedPtr<VariableStorage> &storage) {
+	_storage = storage;
+}
+
 bool VariableModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
 	if (attrib == "value") {
 		varGetValue(result);
diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h
index 355a4526090..72f61db8ae5 100644
--- a/engines/mtropolis/runtime.h
+++ b/engines/mtropolis/runtime.h
@@ -1648,6 +1648,8 @@ public:
 	void forceCursorRefreshOnce();
 	void setAutoResetCursor(bool enabled);
 
+	uint getMultiClickCount() const;
+
 	bool isAwaitingSceneTransition() const;
 
 	Common::RandomSource *getRandom() const;
@@ -1922,6 +1924,10 @@ private:
 	uint32 _modifierOverrideCursorID;
 	bool _haveModifierOverrideCursor;
 
+	uint32 _multiClickStartTime;
+	uint32 _multiClickInterval;
+	uint _multiClickCount;
+
 	bool _defaultVolumeState;
 
 	// True if any elements were added to the scene, removed from the scene, or reparented since last draw
@@ -2348,6 +2354,7 @@ public:
 	void loadSceneFromStream(const Common::SharedPtr<Structural> &structural, uint32 streamID, const Hacks &hacks);
 
 	Common::SharedPtr<Modifier> resolveAlias(uint32 aliasID) const;
+	Common::SharedPtr<Modifier> findGlobalVarWithName(const Common::String &name) const;
 	void materializeGlobalVariables(Runtime *runtime, ObjectLinkingScope *scope);
 
 	const ProjectPresentationSettings &getPresentationSettings() const;
@@ -2895,14 +2902,29 @@ protected:
 	Common::SharedPtr<ModifierHooks> _hooks;
 };
 
+class VariableStorage {
+public:
+	virtual ~VariableStorage();
+	virtual Common::SharedPtr<ModifierSaveLoad> getSaveLoad() = 0;
+
+	virtual Common::SharedPtr<VariableStorage> clone() const = 0;
+};
+
 class VariableModifier : public Modifier {
 public:
+	explicit VariableModifier(const Common::SharedPtr<VariableStorage> &storage);
+	VariableModifier(const VariableModifier &other);
+
 	virtual bool isVariable() const override;
 	virtual bool isListVariable() const;
 
+	virtual Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override final;
+
+	const Common::SharedPtr<VariableStorage> &getStorage() const;
+	void setStorage(const Common::SharedPtr<VariableStorage> &storage);
+
 	virtual bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) = 0;
 	virtual void varGetValue(DynamicValue &dest) const = 0;
-	virtual Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override = 0;
 
 	bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) override;
 
@@ -2911,11 +2933,16 @@ public:
 	virtual DynamicValueWriteProxy createWriteProxy();
 
 private:
+	VariableModifier() = delete;
+
 	struct WriteProxyInterface {
 		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);
 	};
+
+protected:
+	Common::SharedPtr<VariableStorage> _storage;
 };
 
 enum AssetType {




More information about the Scummvm-git-logs mailing list