[Scummvm-git-logs] scummvm master -> 6e4245b962e973f86dd5715444d0a73e24e03f82

elasota noreply at scummvm.org
Tue Nov 8 04:19:36 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:
6e4245b962 MTROPOLIS: More accurate variable dereferencing and list behavior.  Fix some Obsidian regressions.


Commit: 6e4245b962e973f86dd5715444d0a73e24e03f82
    https://github.com/scummvm/scummvm/commit/6e4245b962e973f86dd5715444d0a73e24e03f82
Author: elasota (ejlasota at gmail.com)
Date: 2022-11-07T23:18:03-05:00

Commit Message:
MTROPOLIS: More accurate variable dereferencing and list behavior.  Fix some Obsidian regressions.

Changed paths:
    engines/mtropolis/assets.cpp
    engines/mtropolis/assets.h
    engines/mtropolis/elements.cpp
    engines/mtropolis/miniscript.cpp
    engines/mtropolis/miniscript.h
    engines/mtropolis/modifiers.cpp
    engines/mtropolis/mtropolis.cpp
    engines/mtropolis/mtropolis.h
    engines/mtropolis/plugin/obsidian.cpp
    engines/mtropolis/plugin/standard.cpp
    engines/mtropolis/plugin/standard.h
    engines/mtropolis/runtime.cpp
    engines/mtropolis/runtime.h
    engines/mtropolis/saveload.cpp


diff --git a/engines/mtropolis/assets.cpp b/engines/mtropolis/assets.cpp
index 3dba8f9f0c2..af7ef3b68fe 100644
--- a/engines/mtropolis/assets.cpp
+++ b/engines/mtropolis/assets.cpp
@@ -798,6 +798,10 @@ void CachedImage::resetSurface(ColorDepthMode colorDepth, const Common::SharedPt
 	_surface = surface;
 }
 
+ColorDepthMode CachedImage::getOriginalColorDepth() const {
+	return _colorDepth;
+}
+
 const Common::SharedPtr<Graphics::ManagedSurface> &CachedImage::optimize(Runtime *runtime) {
 	ColorDepthMode renderDepth = runtime->getRealColorDepth();
 	const Graphics::PixelFormat &renderFmt = runtime->getRenderPixelFormat();
diff --git a/engines/mtropolis/assets.h b/engines/mtropolis/assets.h
index 4b9326e7aa4..fa2ccc16890 100644
--- a/engines/mtropolis/assets.h
+++ b/engines/mtropolis/assets.h
@@ -236,6 +236,8 @@ public:
 
 	void resetSurface(ColorDepthMode colorDepth, const Common::SharedPtr<Graphics::ManagedSurface> &surface);
 
+	ColorDepthMode getOriginalColorDepth() const;
+
 private:
 	Common::SharedPtr<Graphics::ManagedSurface> _surface;
 	Common::SharedPtr<Graphics::ManagedSurface> _optimizedSurface;
diff --git a/engines/mtropolis/elements.cpp b/engines/mtropolis/elements.cpp
index f8f8abfd7ea..5a479fc9213 100644
--- a/engines/mtropolis/elements.cpp
+++ b/engines/mtropolis/elements.cpp
@@ -474,11 +474,11 @@ MiniscriptInstructionOutcome MovieElement::writeRefAttribute(MiniscriptThread *t
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "volume") {
-		DynamicValueWriteFuncHelper<MovieElement, &MovieElement::scriptSetVolume>::create(this, result);
+		DynamicValueWriteFuncHelper<MovieElement, &MovieElement::scriptSetVolume, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "timevalue") {
-		DynamicValueWriteFuncHelper<MovieElement, &MovieElement::scriptSetTimestamp>::create(this, result);
+		DynamicValueWriteFuncHelper<MovieElement, &MovieElement::scriptSetTimestamp, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -918,11 +918,11 @@ MiniscriptInstructionOutcome MovieElement::scriptSetRangeEnd(MiniscriptThread *t
 
 MiniscriptInstructionOutcome MovieElement::scriptRangeWriteRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "start") {
-		DynamicValueWriteFuncHelper<MovieElement, &MovieElement::scriptSetRangeStart>::create(this, result);
+		DynamicValueWriteFuncHelper<MovieElement, &MovieElement::scriptSetRangeStart, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 	if (attrib == "end") {
-		DynamicValueWriteFuncHelper<MovieElement, &MovieElement::scriptSetRangeStart>::create(this, result);
+		DynamicValueWriteFuncHelper<MovieElement, &MovieElement::scriptSetRangeStart, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
@@ -1039,7 +1039,7 @@ MiniscriptInstructionOutcome ImageElement::writeRefAttribute(MiniscriptThread *t
 		DynamicValueWriteStringHelper::create(&_text, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "flushpriority") {
-		DynamicValueWriteFuncHelper<ImageElement, &ImageElement::scriptSetFlushPriority>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<ImageElement, &ImageElement::scriptSetFlushPriority, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -1082,12 +1082,21 @@ void ImageElement::render(Window *window) {
 		Common::Rect destRect(_cachedAbsoluteOrigin.x, _cachedAbsoluteOrigin.y, _cachedAbsoluteOrigin.x + _rect.width(), _cachedAbsoluteOrigin.y + _rect.height());
 
 		if (optimized->format.bytesPerPixel == 1) {
-			const Palette *palette = getPalette().get();
-			if (!palette)
-				palette = &_runtime->getGlobalPalette();
-
 			// FIXME: Pass palette to blit functions instead
-			optimized->setPalette(palette->getPalette(), 0, 256);
+			if (_cachedImage->getOriginalColorDepth() == kColorDepthMode1Bit) {
+				const Graphics::PixelFormat &fmt = window->getPixelFormat();
+				uint32 blackColor = fmt.RGBToColor(0, 0, 0);
+				uint32 whiteColor = fmt.RGBToColor(255, 255, 255);
+
+				const uint32 bwPalette[2] = {whiteColor, blackColor};
+				optimized->setPalette(bwPalette, 0, 2);
+			} else {
+				const Palette *palette = getPalette().get();
+				if (!palette)
+					palette = &_runtime->getGlobalPalette();
+
+				optimized->setPalette(palette->getPalette(), 0, 256);
+			}
 		}
 
 		uint8 alpha = _transitionProps.getAlpha();
@@ -1177,7 +1186,7 @@ bool MToonElement::readAttribute(MiniscriptThread *thread, DynamicValue &result,
 
 MiniscriptInstructionOutcome MToonElement::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "cel") {
-		DynamicValueWriteFuncHelper<MToonElement, &MToonElement::scriptSetCel>::create(this, result);
+		DynamicValueWriteFuncHelper<MToonElement, &MToonElement::scriptSetCel, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "flushpriority") {
 		DynamicValueWriteIntegerHelper<int32>::create(&_flushPriority, result);
@@ -1186,7 +1195,7 @@ MiniscriptInstructionOutcome MToonElement::writeRefAttribute(MiniscriptThread *t
 		DynamicValueWriteBoolHelper::create(&_maintainRate, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "rate") {
-		DynamicValueWriteFuncHelper<MToonElement, &MToonElement::scriptSetRate>::create(this, result);
+		DynamicValueWriteFuncHelper<MToonElement, &MToonElement::scriptSetRate, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "range") {
 		DynamicValueWriteOrRefAttribFuncHelper<MToonElement, &MToonElement::scriptSetRange, &MToonElement::scriptRangeWriteRefAttribute>::create(this, result);
@@ -1568,10 +1577,10 @@ MiniscriptInstructionOutcome MToonElement::scriptSetRangeEnd(MiniscriptThread *t
 
 MiniscriptInstructionOutcome MToonElement::scriptRangeWriteRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "start") {
-		DynamicValueWriteFuncHelper<MToonElement, &MToonElement::scriptSetRangeStart>::create(this, result);
+		DynamicValueWriteFuncHelper<MToonElement, &MToonElement::scriptSetRangeStart, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "end") {
-		DynamicValueWriteFuncHelper<MToonElement, &MToonElement::scriptSetRangeEnd>::create(this, result);
+		DynamicValueWriteFuncHelper<MToonElement, &MToonElement::scriptSetRangeEnd, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -1695,7 +1704,7 @@ bool TextLabelElement::readAttributeIndexed(MiniscriptThread *thread, DynamicVal
 
 MiniscriptInstructionOutcome TextLabelElement::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
 	if (attrib == "text") {
-		DynamicValueWriteFuncHelper<TextLabelElement, &TextLabelElement::scriptSetText>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<TextLabelElement, &TextLabelElement::scriptSetText, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -1961,7 +1970,9 @@ MiniscriptInstructionOutcome TextLabelElement::scriptSetText(MiniscriptThread *t
 
 
 MiniscriptInstructionOutcome TextLabelElement::scriptSetLine(MiniscriptThread *thread, size_t lineIndex, const DynamicValue &value) {
-	if (value.getType() != DynamicValueTypes::kString) {
+	DynamicValue derefValue = value.dereference();
+
+	if (derefValue.getType() != DynamicValueTypes::kString) {
 		thread->error("Tried to set a text label element's text to something that wasn't a string");
 		return kMiniscriptInstructionOutcomeFailed;
 	}
@@ -1969,14 +1980,14 @@ MiniscriptInstructionOutcome TextLabelElement::scriptSetLine(MiniscriptThread *t
 	uint32 startPos;
 	uint32 endPos;
 	if (findLineRange(lineIndex, startPos, endPos))
-		_text = _text.substr(0, startPos) + value.getString() + _text.substr(endPos, _text.size() - endPos);
+		_text = _text.substr(0, startPos) + derefValue.getString() + _text.substr(endPos, _text.size() - endPos);
 	else {
 		size_t numLines = countLines();
 		while (numLines <= lineIndex) {
 			_text += '\r';
 			numLines++;
 		}
-		_text += value.getString();
+		_text += derefValue.getString();
 	}
 
 	_needsRender = true;
@@ -2070,13 +2081,13 @@ bool SoundElement::readAttribute(MiniscriptThread *thread, DynamicValue &result,
 
 MiniscriptInstructionOutcome SoundElement::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
 	if (attrib == "loop") {
-		DynamicValueWriteFuncHelper<SoundElement, &SoundElement::scriptSetLoop>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<SoundElement, &SoundElement::scriptSetLoop, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "volume") {
-		DynamicValueWriteFuncHelper<SoundElement, &SoundElement::scriptSetVolume>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<SoundElement, &SoundElement::scriptSetVolume, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "balance") {
-		DynamicValueWriteFuncHelper<SoundElement, &SoundElement::scriptSetBalance>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<SoundElement, &SoundElement::scriptSetBalance, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
diff --git a/engines/mtropolis/miniscript.cpp b/engines/mtropolis/miniscript.cpp
index 3940b1f49b9..122f719bdf0 100644
--- a/engines/mtropolis/miniscript.cpp
+++ b/engines/mtropolis/miniscript.cpp
@@ -38,6 +38,8 @@ bool miniscriptEvaluateTruth(const DynamicValue &value) {
 		return (value.getInt() != 0);
 	case DynamicValueTypes::kFloat:
 		return !(value.getFloat() == 0.0);
+	case DynamicValueTypes::kObject:
+		return !value.getObject().object.expired();
 	default:
 		return false;
 	}
@@ -487,9 +489,7 @@ MiniscriptInstructionOutcome Set::execute(MiniscriptThread *thread) const {
 	}
 
 	// Convert value
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, true);
-	if (outcome != kMiniscriptInstructionOutcomeContinue)
-		return outcome;
+	MiniscriptInstructionOutcome outcome = kMiniscriptInstructionOutcomeContinue;
 
 	const MiniscriptStackValue &srcValue = thread->getStackValueFromTop(0);
 	MiniscriptStackValue &target = thread->getStackValueFromTop(1);
@@ -510,10 +510,7 @@ MiniscriptInstructionOutcome Set::execute(MiniscriptThread *thread) const {
 		}
 
 		if (var != nullptr) {
-			if (!var->varSetValue(thread, srcValue.value)) {
-				thread->error("Couldn't assign value to variable, probably wrong type");
-				return kMiniscriptInstructionOutcomeFailed;
-			}
+			(void)var->varSetValue(thread, srcValue.value);
 		} else {
 			thread->error("Can't assign to rvalue");
 			return kMiniscriptInstructionOutcomeFailed;
@@ -534,14 +531,6 @@ MiniscriptInstructionOutcome Send::execute(MiniscriptThread *thread) const {
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
-	if (outcome != kMiniscriptInstructionOutcomeContinue)
-		return outcome;
-
-	outcome = thread->dereferenceRValue(1, true);
-	if (outcome != kMiniscriptInstructionOutcomeContinue)
-		return outcome;
-
 	DynamicValue &targetValue = thread->getStackValueFromTop(0).value;
 	DynamicValue &payloadValue = thread->getStackValueFromTop(1).value;
 
@@ -590,11 +579,11 @@ MiniscriptInstructionOutcome BinaryArithInstruction::execute(MiniscriptThread *t
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
+	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
-	outcome = thread->dereferenceRValue(1, false);
+	outcome = thread->dereferenceRValue(1);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
@@ -719,11 +708,11 @@ MiniscriptInstructionOutcome UnorderedCompareInstruction::execute(MiniscriptThre
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
+	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
-	outcome = thread->dereferenceRValue(1, false);
+	outcome = thread->dereferenceRValue(1);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
@@ -827,11 +816,11 @@ MiniscriptInstructionOutcome And::execute(MiniscriptThread *thread) const {
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
+	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
-	outcome = thread->dereferenceRValue(1, false);
+	outcome = thread->dereferenceRValue(1);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
@@ -850,11 +839,11 @@ MiniscriptInstructionOutcome Or::execute(MiniscriptThread *thread) const {
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
+	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
-	outcome = thread->dereferenceRValue(1, false);
+	outcome = thread->dereferenceRValue(1);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
@@ -873,7 +862,7 @@ MiniscriptInstructionOutcome Neg::execute(MiniscriptThread *thread) const {
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
+	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
@@ -903,7 +892,7 @@ MiniscriptInstructionOutcome Not::execute(MiniscriptThread *thread) const {
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
+	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
@@ -919,11 +908,11 @@ MiniscriptInstructionOutcome OrderedCompareInstruction::execute(MiniscriptThread
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
+	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
-	outcome = thread->dereferenceRValue(1, false);
+	outcome = thread->dereferenceRValue(1);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
@@ -969,7 +958,7 @@ MiniscriptInstructionOutcome BuiltinFunc::execute(MiniscriptThread *thread) cons
 	}
 
 	for (size_t i = 0; i < stackArgsNeeded; i++) {
-		MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(i, false);
+		MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(i);
 		if (outcome != kMiniscriptInstructionOutcomeContinue)
 			return outcome;
 	}
@@ -1211,11 +1200,11 @@ MiniscriptInstructionOutcome StrConcat::execute(MiniscriptThread *thread) const
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
+	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
-	outcome = thread->dereferenceRValue(1, false);
+	outcome = thread->dereferenceRValue(1);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
@@ -1244,11 +1233,11 @@ MiniscriptInstructionOutcome PointCreate::execute(MiniscriptThread *thread) cons
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
+	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
-	outcome = thread->dereferenceRValue(1, false);
+	outcome = thread->dereferenceRValue(1);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
@@ -1302,11 +1291,11 @@ MiniscriptInstructionOutcome RangeCreate::execute(MiniscriptThread *thread) cons
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
+	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
-	outcome = thread->dereferenceRValue(1, false);
+	outcome = thread->dereferenceRValue(1);
 	if (outcome != kMiniscriptInstructionOutcomeContinue)
 		return outcome;
 
@@ -1376,7 +1365,7 @@ MiniscriptInstructionOutcome GetChild::execute(MiniscriptThread *thread) const {
 		}
 
 		// Convert index
-		outcome = thread->dereferenceRValue(0, false);
+		outcome = thread->dereferenceRValue(0);
 		if (outcome != kMiniscriptInstructionOutcomeContinue)
 			return outcome;
 
@@ -1607,26 +1596,18 @@ MiniscriptInstructionOutcome ListCreate::execute(MiniscriptThread *thread) const
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
-	if (outcome != kMiniscriptInstructionOutcomeContinue)
-		return outcome;
-
-	outcome = thread->dereferenceRValue(1, false);
-	if (outcome != kMiniscriptInstructionOutcomeContinue)
-		return outcome;
-
 	MiniscriptStackValue &rs = thread->getStackValueFromTop(0);
 	MiniscriptStackValue &lsDest = thread->getStackValueFromTop(1);
 
 	Common::SharedPtr<DynamicList> list(new DynamicList());
-	if (!list->setAtIndex(1, rs.value)) {
-		thread->error("Failed to set value 2 of list");
-		return kMiniscriptInstructionOutcomeFailed;
-	}
 	if (!list->setAtIndex(0, lsDest.value)) {
 		thread->error("Failed to set value 1 of list");
 		return kMiniscriptInstructionOutcomeFailed;
 	}
+	if (!list->setAtIndex(1, rs.value)) {
+		thread->error("Failed to set value 2 of list");
+		return kMiniscriptInstructionOutcomeFailed;
+	}
 
 	lsDest.value.setList(list);
 	thread->popValues(1);
@@ -1640,14 +1621,6 @@ MiniscriptInstructionOutcome ListAppend::execute(MiniscriptThread *thread) const
 		return kMiniscriptInstructionOutcomeFailed;
 	}
 
-	MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
-	if (outcome != kMiniscriptInstructionOutcomeContinue)
-		return outcome;
-
-	outcome = thread->dereferenceRValue(1, false);
-	if (outcome != kMiniscriptInstructionOutcomeContinue)
-		return outcome;
-
 	MiniscriptStackValue &rs = thread->getStackValueFromTop(0);
 	MiniscriptStackValue &lsDest = thread->getStackValueFromTop(1);
 
@@ -1826,7 +1799,7 @@ MiniscriptInstructionOutcome Jump::execute(MiniscriptThread *thread) const {
 			return kMiniscriptInstructionOutcomeFailed;
 		}
 
-		MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
+		MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0);
 		if (outcome != kMiniscriptInstructionOutcomeContinue)
 			return outcome;
 
@@ -1906,7 +1879,7 @@ MiniscriptStackValue &MiniscriptThread::getStackValueFromTop(size_t offset) {
 	return _stack[_stack.size() - 1 - offset];
 }
 
-MiniscriptInstructionOutcome MiniscriptThread::dereferenceRValue(size_t offset, bool cloneLists) {
+MiniscriptInstructionOutcome MiniscriptThread::dereferenceRValue(size_t offset) {
 	assert(offset < _stack.size());
 	MiniscriptStackValue &stackValue = _stack[_stack.size() - 1 - offset];
 
@@ -1924,9 +1897,8 @@ MiniscriptInstructionOutcome MiniscriptThread::dereferenceRValue(size_t offset,
 		this->error("Attempted to dereference an lvalue proxy");
 		return kMiniscriptInstructionOutcomeFailed;
 	case DynamicValueTypes::kList:
-			if (cloneLists)
-				stackValue.value.setList(stackValue.value.getList()->clone());
-			break;
+		stackValue.value.setList(stackValue.value.getList()->clone());
+		break;
 	default:
 		break;
 	}
@@ -1950,7 +1922,7 @@ bool MiniscriptThread::evaluateTruthOfResult(bool &isTrue) {
 		return false;
 	}
 
-	MiniscriptInstructionOutcome outcome = dereferenceRValue(0, false);
+	MiniscriptInstructionOutcome outcome = dereferenceRValue(0);
 	if (outcome != kMiniscriptInstructionOutcomeContinue) {
 		this->error("Miniscript program result couldn't be dereferenced");
 		return false;
diff --git a/engines/mtropolis/miniscript.h b/engines/mtropolis/miniscript.h
index 9d3e0cfe2c6..7ff7c1a0193 100644
--- a/engines/mtropolis/miniscript.h
+++ b/engines/mtropolis/miniscript.h
@@ -420,8 +420,7 @@ public:
 	void popValues(size_t count);
 	size_t getStackSize() const;
 	MiniscriptStackValue &getStackValueFromTop(size_t offset);
-
-	MiniscriptInstructionOutcome dereferenceRValue(size_t offset, bool cloneLists);
+	MiniscriptInstructionOutcome dereferenceRValue(size_t offset);
 
 	void jumpOffset(size_t offset);
 
diff --git a/engines/mtropolis/modifiers.cpp b/engines/mtropolis/modifiers.cpp
index 7578c7073ed..1d9a7b48ada 100644
--- a/engines/mtropolis/modifiers.cpp
+++ b/engines/mtropolis/modifiers.cpp
@@ -2395,19 +2395,11 @@ Common::SharedPtr<ModifierSaveLoad> BooleanVariableModifier::getSaveLoad() {
 }
 
 bool BooleanVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
-	switch (value.getType()) {
-	case DynamicValueTypes::kBoolean:
-		_value = value.getBool();
-		break;
-	case DynamicValueTypes::kFloat:
-		_value = (value.getFloat() != 0.0);
-		break;
-	case DynamicValueTypes::kInteger:
-		_value = (value.getInt() != 0);
-		break;
-	default:
+	DynamicValue boolValue;
+	if (!value.convertToType(DynamicValueTypes::kBoolean, boolValue))
 		return false;
-	}
+
+	_value = boolValue.getBool();
 
 	return true;
 }
@@ -2470,19 +2462,12 @@ Common::SharedPtr<ModifierSaveLoad> IntegerVariableModifier::getSaveLoad() {
 }
 
 bool IntegerVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
-	if (value.getType() == DynamicValueTypes::kFloat)
-		_value = static_cast<int32>(floor(value.getFloat() + 0.5));
-	else if (value.getType() == DynamicValueTypes::kInteger)
-		_value = value.getInt();
-	else if (value.getType() == DynamicValueTypes::kString) {
-		// Should this scan %lf to a double and round it instead?
-		int i;
-		if (!sscanf(value.getString().c_str(), "%i", &i))
-			return false;
-		_value = i;
-	} else
+	DynamicValue intValue;
+	if (!value.convertToType(DynamicValueTypes::kInteger, intValue))
 		return false;
 
+	_value = intValue.getInt();
+
 	return true;
 }
 
@@ -2542,11 +2527,12 @@ Common::SharedPtr<ModifierSaveLoad> IntegerRangeVariableModifier::getSaveLoad()
 }
 
 bool IntegerRangeVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
-	if (value.getType() == DynamicValueTypes::kIntegerRange)
-		_range = value.getIntRange();
-	else
+	DynamicValue intRangeValue;
+	if (!value.convertToType(DynamicValueTypes::kIntegerRange, intRangeValue))
 		return false;
 
+	_range = intRangeValue.getIntRange();
+
 	return true;
 }
 
@@ -2632,11 +2618,12 @@ Common::SharedPtr<ModifierSaveLoad> VectorVariableModifier::getSaveLoad() {
 }
 
 bool VectorVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
-	if (value.getType() == DynamicValueTypes::kVector)
-		_vector = value.getVector();
-	else
+	DynamicValue vectorValue;
+	if (!value.convertToType(DynamicValueTypes::kVector, vectorValue))
 		return false;
 
+	_vector = vectorValue.getVector();
+
 	return true;
 }
 
@@ -2722,11 +2709,12 @@ Common::SharedPtr<ModifierSaveLoad> PointVariableModifier::getSaveLoad() {
 }
 
 bool PointVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
-	if (value.getType() == DynamicValueTypes::kPoint)
-		_value = value.getPoint();
-	else
+	DynamicValue pointValue;
+	if (!value.convertToType(DynamicValueTypes::kPoint, pointValue))
 		return false;
 
+	_value = pointValue.getPoint();
+
 	return true;
 }
 
@@ -2816,13 +2804,12 @@ Common::SharedPtr<ModifierSaveLoad> FloatingPointVariableModifier::getSaveLoad()
 }
 
 bool FloatingPointVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
-	if (value.getType() == DynamicValueTypes::kInteger)
-		_value = value.getInt();
-	else if (value.getType() == DynamicValueTypes::kFloat)
-		_value = value.getFloat();
-	else
+	DynamicValue floatValue;
+	if (!value.convertToType(DynamicValueTypes::kFloat, floatValue))
 		return false;
 
+	_value = floatValue.getFloat();
+
 	return true;
 }
 
@@ -2881,11 +2868,12 @@ Common::SharedPtr<ModifierSaveLoad> StringVariableModifier::getSaveLoad() {
 }
 
 bool StringVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
-	if (value.getType() == DynamicValueTypes::kString)
-		_value = value.getString();
-	else
+	DynamicValue stringValue;
+	if (!value.convertToType(DynamicValueTypes::kString, stringValue))
 		return false;
 
+	_value = stringValue.getString();
+
 	return true;
 }
 
@@ -2971,6 +2959,8 @@ Common::SharedPtr<ModifierSaveLoad> ObjectReferenceVariableModifierV1::getSaveLo
 }
 
 bool ObjectReferenceVariableModifierV1::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
+	// 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();
 	else if (value.getType() == DynamicValueTypes::kObject)
@@ -2982,10 +2972,7 @@ bool ObjectReferenceVariableModifierV1::varSetValue(MiniscriptThread *thread, co
 }
 
 void ObjectReferenceVariableModifierV1::varGetValue(DynamicValue &dest) const {
-	if (_value.expired())
-		dest.clear();
-	else
-		dest.setObject(_value);
+	dest.setObject(getSelfReference());
 }
 
 Common::SharedPtr<Modifier> ObjectReferenceVariableModifierV1::shallowClone() const {
diff --git a/engines/mtropolis/mtropolis.cpp b/engines/mtropolis/mtropolis.cpp
index b2a677c9aa5..a1c9a5548a3 100644
--- a/engines/mtropolis/mtropolis.cpp
+++ b/engines/mtropolis/mtropolis.cpp
@@ -115,8 +115,6 @@ Common::Error MTropolisEngine::run() {
 
 	subRenderer.reset();
 
-	Common::SharedPtr<ProjectDescription> projectDesc = bootProject(*_gameDescription);
-
 	if (_gameDescription->gameID == GID_OBSIDIAN) {
 		preferredWidth = 640;
 		preferredHeight = 480;
@@ -157,8 +155,6 @@ Common::Error MTropolisEngine::run() {
 	if (ConfMan.getBool("mtropolis_mod_minimum_transition_duration"))
 		_runtime->getHacks().minTransitionDuration = 75;
 
-	_runtime->queueProject(projectDesc);
-
 	// Figure out pixel formats
 	Graphics::PixelFormat modePixelFormats[kColorDepthModeCount];
 	bool haveExactMode[kColorDepthModeCount];
@@ -255,6 +251,13 @@ Common::Error MTropolisEngine::run() {
 
 	initGraphics(preferredWidth, preferredHeight, &modePixelFormats[selectedMode]);
 
+
+
+	// Start the project
+	Common::SharedPtr<ProjectDescription> projectDesc = bootProject(*_gameDescription);
+	_runtime->queueProject(projectDesc);
+
+
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	if (ConfMan.getBool("mtropolis_debug_at_start")) {
 		_runtime->debugSetEnabled(true);
diff --git a/engines/mtropolis/mtropolis.h b/engines/mtropolis/mtropolis.h
index c43c88a8773..6dbf6c3db6f 100644
--- a/engines/mtropolis/mtropolis.h
+++ b/engines/mtropolis/mtropolis.h
@@ -82,7 +82,8 @@ private:
 
 	Common::String getUnpromptedSaveFileName(const Common::String &fileName);
 
-	static const uint kCurrentSaveFileVersion = 1;
+	static const uint kCurrentSaveFileVersion = 2;
+	static const uint kEarliestSupportedSaveFileVersion = 2;
 	static const uint kSavegameSignature = 0x6d545356;	// mTSV
 
 	ISaveWriter *_saveWriter;
diff --git a/engines/mtropolis/plugin/obsidian.cpp b/engines/mtropolis/plugin/obsidian.cpp
index 99a411d3358..bc4a89c1f51 100644
--- a/engines/mtropolis/plugin/obsidian.cpp
+++ b/engines/mtropolis/plugin/obsidian.cpp
@@ -399,10 +399,10 @@ MiniscriptInstructionOutcome TextWorkModifier::writeRefAttribute(MiniscriptThrea
 		DynamicValueWriteStringHelper::create(&_token, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "firstword") {
-		DynamicValueWriteFuncHelper<TextWorkModifier, &TextWorkModifier::scriptSetFirstWord>::create(this, result);
+		DynamicValueWriteFuncHelper<TextWorkModifier, &TextWorkModifier::scriptSetFirstWord, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "lastword") {
-		DynamicValueWriteFuncHelper<TextWorkModifier, &TextWorkModifier::scriptSetLastWord>::create(this, result);
+		DynamicValueWriteFuncHelper<TextWorkModifier, &TextWorkModifier::scriptSetLastWord, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -518,11 +518,11 @@ bool DictionaryModifier::readAttribute(MiniscriptThread *thread, DynamicValue &r
 
 MiniscriptInstructionOutcome DictionaryModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "index") {
-		DynamicValueWriteFuncHelper<DictionaryModifier, &DictionaryModifier::scriptSetIndex>::create(this, result);
+		DynamicValueWriteFuncHelper<DictionaryModifier, &DictionaryModifier::scriptSetIndex, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "string") {
-		DynamicValueWriteFuncHelper<DictionaryModifier, &DictionaryModifier::scriptSetString>::create(this, result);
+		DynamicValueWriteFuncHelper<DictionaryModifier, &DictionaryModifier::scriptSetString, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -651,11 +651,11 @@ bool WordMixerModifier::readAttribute(MiniscriptThread *thread, DynamicValue &re
 
 MiniscriptInstructionOutcome WordMixerModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "input") {
-		DynamicValueWriteFuncHelper<WordMixerModifier, &WordMixerModifier::scriptSetInput>::create(this, result);
+		DynamicValueWriteFuncHelper<WordMixerModifier, &WordMixerModifier::scriptSetInput, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "search") {
-		DynamicValueWriteFuncHelper<WordMixerModifier, &WordMixerModifier::scriptSetSearch>::create(this, result);
+		DynamicValueWriteFuncHelper<WordMixerModifier, &WordMixerModifier::scriptSetSearch, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -872,7 +872,7 @@ bool XorCheckModifier::readAttribute(MiniscriptThread *thread, DynamicValue &res
 
 MiniscriptInstructionOutcome XorCheckModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "checknow") {
-		DynamicValueWriteFuncHelper<XorCheckModifier, &XorCheckModifier::scriptSetCheckNow>::create(this, result);
+		DynamicValueWriteFuncHelper<XorCheckModifier, &XorCheckModifier::scriptSetCheckNow, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
diff --git a/engines/mtropolis/plugin/standard.cpp b/engines/mtropolis/plugin/standard.cpp
index 85c88837566..235a3a99e92 100644
--- a/engines/mtropolis/plugin/standard.cpp
+++ b/engines/mtropolis/plugin/standard.cpp
@@ -1693,10 +1693,10 @@ bool STransCtModifier::readAttribute(MiniscriptThread *thread, DynamicValue &res
 
 MiniscriptInstructionOutcome STransCtModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "rate") {
-		DynamicValueWriteFuncHelper<STransCtModifier, &STransCtModifier::scriptSetRate>::create(this, result);
+		DynamicValueWriteFuncHelper<STransCtModifier, &STransCtModifier::scriptSetRate, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "steps") {
-		DynamicValueWriteFuncHelper<STransCtModifier, &STransCtModifier::scriptSetSteps>::create(this, result);
+		DynamicValueWriteFuncHelper<STransCtModifier, &STransCtModifier::scriptSetSteps, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -2008,7 +2008,7 @@ bool ObjectReferenceVariableModifier::readAttribute(MiniscriptThread *thread, Dy
 
 MiniscriptInstructionOutcome ObjectReferenceVariableModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "path") {
-		DynamicValueWriteFuncHelper<ObjectReferenceVariableModifier, &ObjectReferenceVariableModifier::scriptSetPath>::create(this, result);
+		DynamicValueWriteFuncHelper<ObjectReferenceVariableModifier, &ObjectReferenceVariableModifier::scriptSetPath, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "object") {
@@ -2022,8 +2022,6 @@ MiniscriptInstructionOutcome ObjectReferenceVariableModifier::writeRefAttribute(
 }
 
 bool ObjectReferenceVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
-	// Somewhat strangely, setting an object reference variable to something sets the path or object,
-	// but getting the variable returns the modifier
 	switch (value.getType()) {
 	case DynamicValueTypes::kNull:
 	case DynamicValueTypes::kObject:
@@ -2036,7 +2034,7 @@ bool ObjectReferenceVariableModifier::varSetValue(MiniscriptThread *thread, cons
 }
 
 void ObjectReferenceVariableModifier::varGetValue(DynamicValue &dest) const {
-	dest.setObject(this->getSelfReference());
+	dest.setObject(getSelfReference());
 }
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
@@ -2471,28 +2469,28 @@ bool MidiModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result,
 
 MiniscriptInstructionOutcome MidiModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "volume") {
-		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetVolume>::create(this, result);
+		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetVolume, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "notevelocity") {
-		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetNoteVelocity>::create(this, result);
+		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetNoteVelocity, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "noteduration") {
-		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetNoteDuration>::create(this, result);
+		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetNoteDuration, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "notenum") {
-		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetNoteNum>::create(this, result);
+		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetNoteNum, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "loop") {
-		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetLoop>::create(this, result);
+		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetLoop, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "playnote") {
-		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetPlayNote>::create(this, result);
+		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetPlayNote, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "tempo") {
-		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetTempo>::create(this, result);
+		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetTempo, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "mutetrack") {
-		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetMuteTrack>::create(this, result);
+		DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetMuteTrack, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -2725,52 +2723,53 @@ ListVariableModifier::ListVariableModifier() : _list(new DynamicList()), _prefer
 }
 
 bool ListVariableModifier::load(const PlugInModifierLoaderContext &context, const Data::Standard::ListVariableModifier &data) {
-	if (!data.havePersistentData || data.numValues == 0)
-		return true;	// If the list is empty then we don't care, the actual value type is irrelevant because it can be reassigned
-
-	DynamicValueTypes::DynamicValueType expectedType = DynamicValueTypes::kInvalid;
+	_preferredContentType = DynamicValueTypes::kInvalid;
 	switch (data.contentsType) {
 	case Data::Standard::ListVariableModifier::kContentsTypeInteger:
-		expectedType = DynamicValueTypes::kInteger;
+		_preferredContentType = DynamicValueTypes::kInteger;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypePoint:
-		expectedType = DynamicValueTypes::kPoint;
+		_preferredContentType = DynamicValueTypes::kPoint;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeRange:
-		expectedType = DynamicValueTypes::kIntegerRange;
+		_preferredContentType = DynamicValueTypes::kIntegerRange;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeFloat:
-		expectedType = DynamicValueTypes::kFloat;
+		_preferredContentType = DynamicValueTypes::kFloat;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeString:
-		expectedType = DynamicValueTypes::kString;
+		_preferredContentType = DynamicValueTypes::kString;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeObject:
+		_preferredContentType = DynamicValueTypes::kObject;
 		if (data.persistentValuesGarbled) {
 			// Ignore and let the game fix it
 			return true;
 		} else {
-			warning("Object reference lists are not implemented");
-			return false;
+			warning("Loading object reference lists from data is not implemented");
+			return true;
 		}
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeVector:
-		expectedType = DynamicValueTypes::kVector;
+		_preferredContentType = DynamicValueTypes::kVector;
 		break;
 	case Data::Standard::ListVariableModifier::kContentsTypeBoolean:
-		expectedType = DynamicValueTypes::kBoolean;
+		_preferredContentType = DynamicValueTypes::kBoolean;
 		break;
 	default:
 		warning("Unknown list data type");
 		return false;
 	}
 
+	if (!data.havePersistentData || data.numValues == 0)
+		return true;
+
 	for (size_t i = 0; i < data.numValues; i++) {
 		DynamicValue dynValue;
 		if (!dynValue.loadConstant(data.values[i]))
 			return false;
 
-		if (dynValue.getType() != expectedType) {
+		if (dynValue.getType() != _preferredContentType) {
 			warning("List mod initialization element had the wrong type");
 			return false;
 		}
@@ -2781,8 +2780,10 @@ bool ListVariableModifier::load(const PlugInModifierLoaderContext &context, cons
 		}
 	}
 
-	_preferredContentType = expectedType;
+	return true;
+}
 
+bool ListVariableModifier::isListVariable() const {
 	return true;
 }
 
@@ -2791,19 +2792,71 @@ Common::SharedPtr<ModifierSaveLoad> ListVariableModifier::getSaveLoad() {
 }
 
 bool ListVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
-	if (value.getType() == DynamicValueTypes::kList)
-		_list = value.getList()->clone();
-	else {
-		if (!_list)
-			_list.reset(new DynamicList());
-		return _list->setAtIndex(0, value);
+	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();
+		Common::SharedPtr<DynamicList> newList(new DynamicList());
+
+		for (size_t i = 0; i < sourceList->getSize(); i++) {
+			DynamicValue sourceElement;
+			sourceList->getAtIndex(i, sourceElement);
+
+			DynamicValue convertedElement;
+
+			if (!sourceElement.convertToType(_preferredContentType, convertedElement)) {
+				thread->error("Failed to convert list when assigning to a list variable");
+				return false;
+			}
+
+			newList->setAtIndex(i, convertedElement);
+		}
+
+		_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> newList(new DynamicList());
+
+			bool failed = false;
+			for (size_t i = 0; i < sourceList->getSize(); i++) {
+				DynamicValue sourceElement;
+				sourceList->getAtIndex(i, sourceElement);
+
+				DynamicValue convertedElement;
+
+				if (!sourceElement.convertToType(_preferredContentType, convertedElement)) {
+					warning("Failed to convert list when assigning to a list variable.  (Non-fatal since it was directly assigned.)");
+					failed = true;
+					break;
+				}
+
+				newList->setAtIndex(i, convertedElement);
+			}
+
+			if (!failed)
+				_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)) {
+			Common::SharedPtr<DynamicList> newList(new DynamicList());
+			newList->setAtIndex(0, convertedValue);
+			_list = newList;
+		} else {
+			thread->error("Can't assign incompatible value type to a list variable");
+			return false;
+		}
 	}
 
 	return true;
 }
 
 void ListVariableModifier::varGetValue(DynamicValue &dest) const {
-	dest.setList(_list);
+	dest.setObject(this->getSelfReference());
 }
 
 bool ListVariableModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
@@ -2853,7 +2906,7 @@ bool ListVariableModifier::readAttributeIndexed(MiniscriptThread *thread, Dynami
 
 MiniscriptInstructionOutcome ListVariableModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
 	if (attrib == "count") {
-		DynamicValueWriteFuncHelper<ListVariableModifier, &ListVariableModifier::scriptSetCount>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<ListVariableModifier, &ListVariableModifier::scriptSetCount, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -2923,7 +2976,7 @@ void ListVariableModifier::debugInspect(IDebugInspectionReport *report) const {
 }
 #endif
 
-ListVariableModifier::ListVariableModifier(const ListVariableModifier &other) : VariableModifier(other), _preferredContentType(DynamicValueTypes::kNull) {
+ListVariableModifier::ListVariableModifier(const ListVariableModifier &other) : VariableModifier(other), _preferredContentType(other._preferredContentType) {
 	if (other._list)
 		_list = other._list->clone();
 }
@@ -2967,7 +3020,10 @@ ListVariableModifier::SaveLoad::SaveLoad(ListVariableModifier *modifier) : _modi
 }
 
 void ListVariableModifier::SaveLoad::commitLoad() const {
-	_modifier->_list = _list;
+	// 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;
 }
 
 void ListVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) const {
@@ -3021,8 +3077,7 @@ void ListVariableModifier::SaveLoad::recursiveWriteList(DynamicList *list, Commo
 		case DynamicValueTypes::kBoolean:
 			stream->writeByte(list->getBool()[i] ? 1 : 0);
 			break;
-		case DynamicValueTypes::kList:
-			recursiveWriteList(list->getList()[i].get(), stream);
+		case DynamicValueTypes::kObject:
 			break;
 		default:
 			error("Can't figure out how to write a saved variable");
@@ -3090,11 +3145,8 @@ Common::SharedPtr<DynamicList> ListVariableModifier::SaveLoad::recursiveReadList
 				byte b = stream->readByte();
 				val.setBool(b != 0);
 			} break;
-		case DynamicValueTypes::kList: {
-				Common::SharedPtr<DynamicList> childList = recursiveReadList(stream);
-				if (!childList)
-					return nullptr;
-				val.setList(childList);
+		case DynamicValueTypes::kObject: {
+				val.setObject(Common::WeakPtr<RuntimeObject>());
 			} break;
 		default:
 			error("Can't figure out how to write a saved variable");
diff --git a/engines/mtropolis/plugin/standard.h b/engines/mtropolis/plugin/standard.h
index c6f60dee2ef..3eb8ab9fb74 100644
--- a/engines/mtropolis/plugin/standard.h
+++ b/engines/mtropolis/plugin/standard.h
@@ -325,6 +325,8 @@ public:
 
 	bool load(const PlugInModifierLoaderContext &context, const Data::Standard::ListVariableModifier &data);
 
+	bool isListVariable() const override;
+
 	Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override;
 
 	bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) override;
diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp
index b73b4a43dd7..ab8a5acddaa 100644
--- a/engines/mtropolis/runtime.cpp
+++ b/engines/mtropolis/runtime.cpp
@@ -1018,21 +1018,20 @@ MiniscriptInstructionOutcome DynamicList::WriteProxyInterface::write(MiniscriptT
 MiniscriptInstructionOutcome DynamicList::WriteProxyInterface::refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib) {
 	DynamicList *list = static_cast<DynamicList *>(objectRef);
 
+	if (ptrOrOffset >= list->getSize()) {
+		// Only direct sets of elements may change the size of a list.  Accessing attributes of elements that aren't set is an error.
+		thread->error("List attrib write dereference was out of bounds");
+		return kMiniscriptInstructionOutcomeFailed;
+	}
+
 	switch (list->getType()) {
 	case DynamicValueTypes::kPoint:
-		list->expandToMinimumSize(ptrOrOffset + 1);
 		return pointWriteRefAttrib(list->getPoint()[ptrOrOffset], thread, proxy, attrib);
 	case DynamicValueTypes::kIntegerRange:
-		list->expandToMinimumSize(ptrOrOffset + 1);
 		return list->getIntRange()[ptrOrOffset].refAttrib(thread, proxy, attrib);
 	case DynamicValueTypes::kVector:
-		list->expandToMinimumSize(ptrOrOffset + 1);
 		return list->getVector()[ptrOrOffset].refAttrib(thread, proxy, attrib);
 	case DynamicValueTypes::kObject: {
-			if (list->getSize() <= ptrOrOffset) {
-				return kMiniscriptInstructionOutcomeFailed;
-				}
-
 			Common::SharedPtr<RuntimeObject> obj = list->getObjectReference()[ptrOrOffset].object.lock();
 			proxy.containerList.reset();
 			if (!obj) {
@@ -1064,7 +1063,7 @@ MiniscriptInstructionOutcome DynamicList::WriteProxyInterface::refAttribIndexed(
 			subList->createWriteProxyForIndex(subIndex, proxy);
 			proxy.containerList = subList;
 			return kMiniscriptInstructionOutcomeContinue;
-		}
+		} break;
 	case DynamicValueTypes::kObject: {
 			if (list->getSize() <= ptrOrOffset)
 				return kMiniscriptInstructionOutcomeFailed;
@@ -1075,7 +1074,7 @@ MiniscriptInstructionOutcome DynamicList::WriteProxyInterface::refAttribIndexed(
 				return kMiniscriptInstructionOutcomeFailed;
 
 			return kMiniscriptInstructionOutcomeContinue;
-		}
+		} break;
 	default:
 		return kMiniscriptInstructionOutcomeFailed;
 	}
@@ -1375,22 +1374,78 @@ bool DynamicValue::roundToInt(int32 &outInt) const {
 }
 
 bool DynamicValue::convertToType(DynamicValueTypes::DynamicValueType targetType, DynamicValue &result) const {
+	if (_type == DynamicValueTypes::kObject && targetType != DynamicValueTypes::kObject) {
+		RuntimeObject *obj = this->_value.asObj.object.lock().get();
+		if (obj && obj->isModifier() && static_cast<Modifier *>(obj)->isVariable()) {
+			DynamicValue varContents;
+			static_cast<VariableModifier *>(obj)->varGetValue(varContents);
+			return varContents.convertToTypeNoDereference(targetType, result);
+		}
+	}
+
+	if (_type == DynamicValueTypes::kList && targetType != DynamicValueTypes::kList) {
+		if (_value.asList.get() && _value.asList->getSize() == 1) {
+			// Single-value lists are convertible
+			DynamicValue firstListElement;
+			(void)_value.asList->getAtIndex(0, firstListElement);
+
+			return firstListElement.convertToType(targetType, result);
+		}
+	}
+
+	return convertToTypeNoDereference(targetType, result);
+}
+
+DynamicValue DynamicValue::dereference() const {
+	if (_type == DynamicValueTypes::kObject) {
+		RuntimeObject *obj = this->_value.asObj.object.lock().get();
+		if (obj && obj->isModifier() && static_cast<Modifier *>(obj)->isVariable()) {
+			DynamicValue varContents;
+			static_cast<VariableModifier *>(obj)->varGetValue(varContents);
+			return varContents;
+		}
+	}
+
+	if (_type == DynamicValueTypes::kList) {
+		if (_value.asList.get() && _value.asList->getSize() == 1) {
+			// Single-value lists are convertible
+			DynamicValue firstListElement;
+			(void)_value.asList->getAtIndex(0, firstListElement);
+
+			return firstListElement;
+		}
+	}
+
+	return *this;
+}
+
+bool DynamicValue::convertToTypeNoDereference(DynamicValueTypes::DynamicValueType targetType, DynamicValue &result) const {
 	if (_type == targetType) {
 		result = *this;
 		return true;
 	}
 
 	switch (_type) {
+	case DynamicValueTypes::kNull:
+		if (targetType == DynamicValueTypes::kObject) {
+			result.setObject(Common::WeakPtr<RuntimeObject>());
+			return true;
+		}
+		break;
 	case DynamicValueTypes::kInteger:
 		return convertIntToType(targetType, result);
 	case DynamicValueTypes::kFloat:
 		return convertFloatToType(targetType, result);
 	case DynamicValueTypes::kBoolean:
 		return convertBoolToType(targetType, result);
+	case DynamicValueTypes::kString:
+		return convertStringToType(targetType, result);
 	default:
-		warning("Couldn't convert dynamic value from source type");
-		return false;
+		break;
 	}
+
+	warning("Couldn't convert dynamic value from source type");
+	return false;
 }
 
 void DynamicValue::setObject(const ObjectReference &value) {
@@ -1556,6 +1611,35 @@ bool DynamicValue::convertBoolToType(DynamicValueTypes::DynamicValueType targetT
 	}
 }
 
+bool DynamicValue::convertStringToType(DynamicValueTypes::DynamicValueType targetType, DynamicValue &result) const {
+	const Common::String &value = this->getString();
+
+	// Docs say that strings are always false but they are not convertible
+	switch (targetType) {
+	case DynamicValueTypes::kInteger: {
+			// Passing float values is allowed, but they are truncated toward zero instead of rounded
+			double floatValue = 0;
+			if (sscanf(value.c_str(), "%lf", &floatValue))
+				result.setInt(static_cast<int>(floatValue));
+			else
+				result.setInt(0);
+			return true;
+		} break;
+	case DynamicValueTypes::kFloat: {
+			double floatValue = 0;
+			if (sscanf(value.c_str(), "%lf", &floatValue))
+				result.setFloat(floatValue);
+			else
+				result.setFloat(0.0);
+			return true;
+		} break;
+	default:
+		break;
+	}
+	warning("Unable to implicitly convert dynamic value");
+	return false;
+}
+
 void DynamicValue::setFromOther(const DynamicValue &other) {
 	if (this == &other)
 		return;
@@ -1723,7 +1807,7 @@ DynamicValue DynamicValueSource::produceValue(const DynamicValue &incomingData)
 		return incomingData;
 	case DynamicValueSourceTypes::kVariableReference: {
 			Common::SharedPtr<Modifier> resolution = _valueUnion._varReference.resolution.lock();
-			if (resolution->isVariable()) {
+			if (resolution && resolution->isVariable()) {
 				DynamicValue result;
 				static_cast<VariableModifier *>(resolution.get())->varGetValue(result);
 				return result;
@@ -1795,8 +1879,10 @@ void DynamicValueSource::initFromOther(DynamicValueSource &&other) {
 }
 
 MiniscriptInstructionOutcome DynamicValueWriteStringHelper::write(MiniscriptThread *thread, const DynamicValue &value, void *objectRef, uintptr ptrOrOffset) {
+	DynamicValue derefValue = value.dereference();
+
 	Common::String &dest = *static_cast<Common::String *>(objectRef);
-	switch (value.getType()) {
+	switch (derefValue.getType()) {
 	case DynamicValueTypes::kString:
 		dest = value.getString();
 		return kMiniscriptInstructionOutcomeContinue;
@@ -1839,6 +1925,8 @@ void DynamicValueWriteDiscardHelper::create(DynamicValueWriteProxy &proxy) {
 
 
 MiniscriptInstructionOutcome DynamicValueWritePointHelper::write(MiniscriptThread *thread, const DynamicValue &value, void *objectRef, uintptr ptrOrOffset) {
+	DynamicValue derefValue = value.dereference();
+
 	if (value.getType() != DynamicValueTypes::kPoint) {
 		thread->error("Can't set point to invalid type");
 		return kMiniscriptInstructionOutcomeFailed;
@@ -1874,10 +1962,12 @@ void DynamicValueWritePointHelper::create(Common::Point *pointValue, DynamicValu
 }
 
 MiniscriptInstructionOutcome DynamicValueWriteBoolHelper::write(MiniscriptThread *thread, const DynamicValue &value, void *objectRef, uintptr ptrOrOffset) {
+	DynamicValue derefValue = value.dereference();
+
 	bool &dest = *static_cast<bool *>(objectRef);
-	switch (value.getType()) {
+	switch (derefValue.getType()) {
 	case DynamicValueTypes::kBoolean:
-		dest = value.getBool();
+		dest = derefValue.getBool();
 		return kMiniscriptInstructionOutcomeContinue;
 	default:
 		return kMiniscriptInstructionOutcomeFailed;
@@ -2584,19 +2674,19 @@ bool WorldManagerInterface::readAttribute(MiniscriptThread *thread, DynamicValue
 
 MiniscriptInstructionOutcome WorldManagerInterface::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "currentscene") {
-		DynamicValueWriteFuncHelper<WorldManagerInterface, &WorldManagerInterface::setCurrentScene>::create(this, result);
+		DynamicValueWriteFuncHelper<WorldManagerInterface, &WorldManagerInterface::setCurrentScene, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "refreshcursor") {
-		DynamicValueWriteFuncHelper<WorldManagerInterface, &WorldManagerInterface::setRefreshCursor>::create(this, result);
+		DynamicValueWriteFuncHelper<WorldManagerInterface, &WorldManagerInterface::setRefreshCursor, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "autoresetcursor") {
-		DynamicValueWriteFuncHelper<WorldManagerInterface, &WorldManagerInterface::setAutoResetCursor>::create(this, result);
+		DynamicValueWriteFuncHelper<WorldManagerInterface, &WorldManagerInterface::setAutoResetCursor, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "winsndbuffersize") {
-		DynamicValueWriteFuncHelper<WorldManagerInterface, &WorldManagerInterface::setWinSndBufferSize>::create(this, result);
+		DynamicValueWriteFuncHelper<WorldManagerInterface, &WorldManagerInterface::setWinSndBufferSize, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "gamemode") {
@@ -2709,19 +2799,19 @@ bool SystemInterface::readAttributeIndexed(MiniscriptThread *thread, DynamicValu
 
 MiniscriptInstructionOutcome SystemInterface::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "ejectcd") {
-		DynamicValueWriteFuncHelper<SystemInterface, &SystemInterface::setEjectCD>::create(this, result);
+		DynamicValueWriteFuncHelper<SystemInterface, &SystemInterface::setEjectCD, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "gamemode") {
-		DynamicValueWriteFuncHelper<SystemInterface, &SystemInterface::setGameMode>::create(this, result);
+		DynamicValueWriteFuncHelper<SystemInterface, &SystemInterface::setGameMode, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "mastervolume") {
-		DynamicValueWriteFuncHelper<SystemInterface, &SystemInterface::setMasterVolume>::create(this, result);
+		DynamicValueWriteFuncHelper<SystemInterface, &SystemInterface::setMasterVolume, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "monitorbitdepth") {
-		DynamicValueWriteFuncHelper<SystemInterface, &SystemInterface::setMonitorBitDepth>::create(this, result);
+		DynamicValueWriteFuncHelper<SystemInterface, &SystemInterface::setMonitorBitDepth, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "volumename") {
-		DynamicValueWriteFuncHelper<SystemInterface, &SystemInterface::setVolumeName>::create(this, result);
+		DynamicValueWriteFuncHelper<SystemInterface, &SystemInterface::setVolumeName, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -2969,7 +3059,7 @@ MiniscriptInstructionOutcome Structural::writeRefAttribute(MiniscriptThread *thr
 		DynamicValueWriteStringHelper::create(&_name, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "paused") {
-		DynamicValueWriteFuncHelper<Structural, &Structural::scriptSetPaused>::create(this, result);
+		DynamicValueWriteFuncHelper<Structural, &Structural::scriptSetPaused, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "this") {
 		// Yes, "this" is an attribute
@@ -3010,10 +3100,10 @@ MiniscriptInstructionOutcome Structural::writeRefAttribute(MiniscriptThread *thr
 			return kMiniscriptInstructionOutcomeFailed;
 		}
 	} else if (attrib == "loop") {
-		DynamicValueWriteFuncHelper<Structural, &Structural::scriptSetLoop>::create(this, result);
+		DynamicValueWriteFuncHelper<Structural, &Structural::scriptSetLoop, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "debug") {
-		DynamicValueWriteFuncHelper<Structural, &Structural::scriptSetDebug>::create(this, result);
+		DynamicValueWriteFuncHelper<Structural, &Structural::scriptSetDebug, true>::create(this, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "flushpriority") {
 		DynamicValueWriteIntegerHelper<int32>::create(&_flushPriority, result);
@@ -7801,10 +7891,10 @@ bool VisualElement::readAttribute(MiniscriptThread *thread, DynamicValue &result
 
 MiniscriptInstructionOutcome VisualElement::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
 	if (attrib == "visible") {
-		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetVisibility>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetVisibility, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "direct") {
-		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetDirect>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetDirect, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "position") {
 		DynamicValueWriteOrRefAttribFuncHelper<VisualElement, &VisualElement::scriptSetPosition, &VisualElement::scriptWriteRefPositionAttribute>::create(this, writeProxy);
@@ -7813,13 +7903,13 @@ MiniscriptInstructionOutcome VisualElement::writeRefAttribute(MiniscriptThread *
 		DynamicValueWriteOrRefAttribFuncHelper<VisualElement, &VisualElement::scriptSetCenterPosition, &VisualElement::scriptWriteRefCenterPositionAttribute>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "width") {
-		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetWidth>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetWidth, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "height") {
-		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetHeight>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetHeight, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "layer") {
-		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetLayer>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetLayer, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -8152,10 +8242,10 @@ MiniscriptInstructionOutcome VisualElement::scriptSetLayer(MiniscriptThread *thr
 
 MiniscriptInstructionOutcome VisualElement::scriptWriteRefPositionAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
 	if (attrib == "x") {
-		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetPositionX>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetPositionX, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "y") {
-		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetPositionY>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetPositionY, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -8164,10 +8254,10 @@ MiniscriptInstructionOutcome VisualElement::scriptWriteRefPositionAttribute(Mini
 
 MiniscriptInstructionOutcome VisualElement::scriptWriteRefCenterPositionAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
 	if (attrib == "x") {
-		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetCenterPositionX>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetCenterPositionX, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	} else if (attrib == "y") {
-		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetCenterPositionY>::create(this, writeProxy);
+		DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetCenterPositionY, true>::create(this, writeProxy);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 
@@ -8490,6 +8580,10 @@ bool VariableModifier::isVariable() const {
 	return true;
 }
 
+bool VariableModifier::isListVariable() const {
+	return false;
+}
+
 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 37a31a5c599..d3fe79b85c7 100644
--- a/engines/mtropolis/runtime.h
+++ b/engines/mtropolis/runtime.h
@@ -856,6 +856,8 @@ struct DynamicValue {
 
 	bool convertToType(DynamicValueTypes::DynamicValueType targetType, DynamicValue &result) const;
 
+	DynamicValue dereference() const;
+
 	DynamicValue &operator=(const DynamicValue &other);
 
 	bool operator==(const DynamicValue &other) const;
@@ -903,6 +905,9 @@ private:
 	bool convertIntToType(DynamicValueTypes::DynamicValueType targetType, DynamicValue &result) const;
 	bool convertFloatToType(DynamicValueTypes::DynamicValueType targetType, DynamicValue &result) const;
 	bool convertBoolToType(DynamicValueTypes::DynamicValueType targetType, DynamicValue &result) const;
+	bool convertStringToType(DynamicValueTypes::DynamicValueType targetType, DynamicValue &result) const;
+
+	bool convertToTypeNoDereference(DynamicValueTypes::DynamicValueType targetType, DynamicValue &result) const;
 
 	void setFromOther(const DynamicValue &other);
 
@@ -951,13 +956,15 @@ private:
 template<class TFloat>
 struct DynamicValueWriteFloatHelper {
 	static MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &value, void *objectRef, uintptr ptrOrOffset) {
+		DynamicValue derefValue = value.dereference();
+
 		TFloat &dest = *static_cast<TFloat *>(objectRef);
-		switch (value.getType()) {
+		switch (derefValue.getType()) {
 		case DynamicValueTypes::kFloat:
-			dest = static_cast<TFloat>(value.getFloat());
+			dest = static_cast<TFloat>(derefValue.getFloat());
 			return kMiniscriptInstructionOutcomeContinue;
 		case DynamicValueTypes::kInteger:
-			dest = static_cast<TFloat>(value.getInt());
+			dest = static_cast<TFloat>(derefValue.getInt());
 			return kMiniscriptInstructionOutcomeContinue;
 		default:
 			return kMiniscriptInstructionOutcomeFailed;
@@ -980,13 +987,15 @@ struct DynamicValueWriteFloatHelper {
 template<class TInteger>
 struct DynamicValueWriteIntegerHelper {
 	static MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &value, void *objectRef, uintptr ptrOrOffset) {
+		DynamicValue derefValue = value.dereference();
+
 		TInteger &dest = *static_cast<TInteger *>(objectRef);
-		switch (value.getType()) {
+		switch (derefValue.getType()) {
 		case DynamicValueTypes::kFloat:
-			dest = static_cast<TInteger>(floor(value.getFloat() + 0.5));
+			dest = static_cast<TInteger>(floor(derefValue.getFloat() + 0.5));
 			return kMiniscriptInstructionOutcomeContinue;
 		case DynamicValueTypes::kInteger:
-			dest = static_cast<TInteger>(value.getInt());
+			dest = static_cast<TInteger>(derefValue.getInt());
 			return kMiniscriptInstructionOutcomeContinue;
 		default:
 			return kMiniscriptInstructionOutcomeFailed;
@@ -1041,7 +1050,9 @@ struct DynamicValueWriteDiscardHelper {
 template<class TClass, MiniscriptInstructionOutcome (TClass::*TWriteMethod)(MiniscriptThread *thread, const DynamicValue &dest), MiniscriptInstructionOutcome (TClass::*TRefAttribMethod)(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib)>
 struct DynamicValueWriteOrRefAttribFuncHelper {
 	static MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset) {
-		return (static_cast<TClass *>(objectRef)->*TWriteMethod)(thread, dest);
+		DynamicValue derefValue = dest.dereference();
+
+		return (static_cast<TClass *>(objectRef)->*TWriteMethod)(thread, derefValue);
 	}
 	static MiniscriptInstructionOutcome refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib) {
 		return (static_cast<TClass *>(objectRef)->*TRefAttribMethod)(thread, proxy, attrib);
@@ -1057,10 +1068,13 @@ struct DynamicValueWriteOrRefAttribFuncHelper {
 	}
 };
 
-template<class TClass, MiniscriptInstructionOutcome (TClass::*TWriteMethod)(MiniscriptThread *thread, const DynamicValue &dest)>
+template<class TClass, MiniscriptInstructionOutcome (TClass::*TWriteMethod)(MiniscriptThread *thread, const DynamicValue &dest), bool TDereference>
 struct DynamicValueWriteFuncHelper {
 	static MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset) {
-		return (static_cast<TClass *>(objectRef)->*TWriteMethod)(thread, dest);
+		if (TDereference) {
+			return (static_cast<TClass *>(objectRef)->*TWriteMethod)(thread, dest.dereference());
+		} else
+			return (static_cast<TClass *>(objectRef)->*TWriteMethod)(thread, dest);
 	}
 	static MiniscriptInstructionOutcome refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib) {
 		return kMiniscriptInstructionOutcomeFailed;
@@ -1072,7 +1086,7 @@ struct DynamicValueWriteFuncHelper {
 	static void create(TClass *obj, DynamicValueWriteProxy &proxy) {
 		proxy.pod.ptrOrOffset = 0;
 		proxy.pod.objectRef = obj;
-		proxy.pod.ifc = DynamicValueWriteInterfaceGlue<DynamicValueWriteFuncHelper<TClass, TWriteMethod> >::getInstance();
+		proxy.pod.ifc = DynamicValueWriteInterfaceGlue<DynamicValueWriteFuncHelper<TClass, TWriteMethod, TDereference> >::getInstance();
 	}
 };
 
@@ -2860,6 +2874,8 @@ protected:
 class VariableModifier : public Modifier {
 public:
 	virtual bool isVariable() const override;
+	virtual bool isListVariable() const;
+
 	virtual bool varSetValue(MiniscriptThread *thread, const DynamicValue &value) = 0;
 	virtual void varGetValue(DynamicValue &dest) const = 0;
 	virtual Common::SharedPtr<ModifierSaveLoad> getSaveLoad() override = 0;
diff --git a/engines/mtropolis/saveload.cpp b/engines/mtropolis/saveload.cpp
index 525b28a0acc..ebb82e8b5df 100644
--- a/engines/mtropolis/saveload.cpp
+++ b/engines/mtropolis/saveload.cpp
@@ -162,6 +162,14 @@ bool MTropolisEngine::load(ISaveReader *reader, const Common::String &saveFileNa
 		return false;
 	}
 
+	if (saveFileVersion < kEarliestSupportedSaveFileVersion) {
+		GUI::MessageDialog dialog(_("Saved game was created with an earlier, incompatible version of ScummVM. Unable to load."));
+		dialog.runModal();
+
+		warning("An error occurred while reading file '%s'", saveFileName.c_str());
+		return false;
+	}
+
 	if (!reader->readSave(in.get(), saveFileVersion)) {
 		GUI::MessageDialog dialog(_("Failed to load save, an error occurred when reading the save game data."));
 		dialog.runModal();




More information about the Scummvm-git-logs mailing list