[Scummvm-git-logs] scummvm master -> 0658576196e2c767268eb77e2ee1f14c03b3762a

npjg noreply at scummvm.org
Tue Mar 25 23:35:34 UTC 2025


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

Summary:
84517ad6cb MEDIASTATION: Implement collection script methods
97ea2d6504 MEDIASTATION: Stub Unk1 document method
ca95c3596c MEDIASTATION: Don't release contexts too early
d6a62221a6 MEDIASTATION: Permit scripts to coerce some types
61859559cd MEDIASTATION: Allow movies to move under script control
0658576196 MEDIASTATION: Make path playback non-blocking


Commit: 84517ad6cbbcf819cf5dca1f563a796c5fcff680
    https://github.com/scummvm/scummvm/commit/84517ad6cbbcf819cf5dca1f563a796c5fcff680
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-03-25T18:21:36-04:00

Commit Message:
MEDIASTATION: Implement collection script methods

Changed paths:
    engines/mediastation/mediascript/codechunk.cpp
    engines/mediastation/mediascript/codechunk.h
    engines/mediastation/mediascript/operand.cpp
    engines/mediastation/mediascript/operand.h
    engines/mediastation/mediascript/variable.cpp
    engines/mediastation/mediascript/variable.h
    engines/mediastation/mediastation.h


diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index 159bbadd095..810aa3f8c38 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -19,14 +19,13 @@
  *
  */
 
+#include "common/ptr.h"
+
 #include "mediastation/mediastation.h"
 #include "mediastation/mediascript/codechunk.h"
 #include "mediastation/datum.h"
 #include "mediastation/debugchannels.h"
 
-#include "mediastation/assets/movie.h"
-#include "mediastation/assets/path.h"
-
 namespace MediaStation {
 
 CodeChunk::CodeChunk(Common::SeekableReadStream &chunk) {
@@ -362,10 +361,17 @@ Operand CodeChunk::executeNextStatement() {
 			return operand;
 		}
 
+		case kOperandTypeMethod: {
+			BuiltInMethod methodId = static_cast<BuiltInMethod>(Datum(*_bytecode).u.i);
+			debugC(5, kDebugScript, "%s ", builtInMethodToStr(methodId));
+			operand.putMethodId(methodId);
+			return operand;
+		}
+
 		case kOperandTypeFunction: {
 			uint functionId = Datum(*_bytecode).u.i;
 			debugC(5, kDebugScript, "%d ", functionId);
-			operand.putFunction(functionId);
+			operand.putFunctionId(functionId);
 			return operand;
 		}
 
@@ -452,7 +458,7 @@ Operand CodeChunk::getVariable(uint32 id, VariableScope scope) {
 	}
 }
 
-void CodeChunk::putVariable(uint32 id, VariableScope scope, Operand value) {
+void CodeChunk::putVariable(uint32 id, VariableScope scope, Operand &value) {
 	switch (scope) {
 	case kVariableScopeGlobal: {
 		Variable *variable = g_engine->_variables.getVal(id);
@@ -479,7 +485,7 @@ void CodeChunk::putVariable(uint32 id, VariableScope scope, Operand value) {
 	}
 }
 
-Operand CodeChunk::callBuiltInMethod(BuiltInMethod method, Operand self, Common::Array<Operand> &args) {
+Operand CodeChunk::callBuiltInMethod(BuiltInMethod method, Operand &self, Common::Array<Operand> &args) {
 	Operand literalSelf = self.getLiteralValue();
 	OperandType literalType = literalSelf.getType();
 	switch (literalType) {
@@ -509,7 +515,7 @@ Operand CodeChunk::callBuiltInMethod(BuiltInMethod method, Operand self, Common:
 	}
 
 	case kOperandTypeCollection: {
-		Collection *collection = literalSelf.getCollection();
+		Common::SharedPtr<Collection> collection = literalSelf.getCollection();
 		Operand returnValue = collection->callMethod(method, args);
 		return returnValue;
 	}
diff --git a/engines/mediastation/mediascript/codechunk.h b/engines/mediastation/mediascript/codechunk.h
index 79031b17797..c2cd09f000e 100644
--- a/engines/mediastation/mediascript/codechunk.h
+++ b/engines/mediastation/mediascript/codechunk.h
@@ -39,12 +39,13 @@ public:
 
 	Operand execute(Common::Array<Operand> *args = nullptr, Common::Array<Operand> *locals = nullptr);
 
+	static Operand callBuiltInMethod(BuiltInMethod method, Operand &self, Common::Array<Operand> &args);
+
 private:
 	Operand executeNextStatement();
-	Operand callBuiltInMethod(BuiltInMethod method, Operand self, Common::Array<Operand> &args);
 	Operand callFunction(uint functionId, uint parameterCount);
 	Operand getVariable(uint32 id, VariableScope scope);
-	void putVariable(uint32 id, VariableScope scope, Operand value);
+	void putVariable(uint32 id, VariableScope scope, Operand &value);
 
 	bool _weOwnLocals = false;
 	Common::Array<Operand> *_locals = nullptr;
diff --git a/engines/mediastation/mediascript/operand.cpp b/engines/mediastation/mediascript/operand.cpp
index afe0c331fd9..631721d400e 100644
--- a/engines/mediastation/mediascript/operand.cpp
+++ b/engines/mediastation/mediascript/operand.cpp
@@ -162,7 +162,7 @@ Variable *Operand::getVariable() {
 	}
 }
 
-void Operand::putFunction(uint functionId) {
+void Operand::putFunctionId(uint functionId) {
 	switch (_type) {
 	case kOperandTypeFunction: {
 		_u.functionId = functionId;
@@ -170,7 +170,7 @@ void Operand::putFunction(uint functionId) {
 	}
 
 	default:
-		error("Operand::putFunction(): Attempt to put function ID into operand type %s (%d)",
+		error("Operand::putFunctionId(): Attempt to put function ID into operand type %s (%d)",
 			operandTypeToStr(_type), static_cast<uint>(_type));
 	}
 }
@@ -192,6 +192,31 @@ uint Operand::getFunctionId() {
 	}
 }
 
+void Operand::putMethodId(BuiltInMethod methodId) {
+	switch (_type) {
+	case kOperandTypeMethod: {
+		_u.methodId = methodId;
+		break;
+	}
+
+	default:
+		error("Operand::putFunctionId(): Attempt to put method ID into operand type %s (%d)",
+			operandTypeToStr(_type), static_cast<uint>(_type));
+	}
+}
+
+BuiltInMethod Operand::getMethodId() {
+	switch (_type) {
+	case kOperandTypeMethod: {
+		return _u.methodId;
+	}
+
+	default:
+		error("Operand::getFunction(): Attempt to get method ID from operand type %s (%d)",
+			operandTypeToStr(_type), static_cast<uint>(_type));
+	}
+}
+
 void Operand::putAsset(uint32 assetId) {
 	switch (_type) {
 	case kOperandTypeAssetId: {
@@ -249,16 +274,16 @@ uint32 Operand::getAssetId() {
 	}
 }
 
-void Operand::putCollection(Collection *collection) {
+void Operand::putCollection(Common::SharedPtr<Collection> collection) {
 	switch (_type) {
 	case kOperandTypeCollection: {
-		_u.collection = collection;
+		_collection = collection;
 		break;
 	}
 
 	case kOperandTypeVariableDeclaration: {
 		assert(_u.variable->_type == kVariableTypeCollection);
-		_u.variable->_value.collection = collection;
+		_u.variable->_c = collection;
 		break;
 	}
 
@@ -268,15 +293,15 @@ void Operand::putCollection(Collection *collection) {
 	}
 }
 
-Collection *Operand::getCollection() {
+Common::SharedPtr<Collection> Operand::getCollection() {
 	switch (_type) {
 	case kOperandTypeCollection: {
-		return _u.collection;
+		return _collection;
 	}
 
 	case kOperandTypeVariableDeclaration: {
 		assert(_u.variable->_type == kVariableTypeCollection);
-		return _u.variable->_value.collection;
+		return _u.variable->_c;
 	}
 
 	default:
diff --git a/engines/mediastation/mediascript/operand.h b/engines/mediastation/mediascript/operand.h
index a9bc9387897..3adba4951bc 100644
--- a/engines/mediastation/mediascript/operand.h
+++ b/engines/mediastation/mediascript/operand.h
@@ -22,6 +22,7 @@
 #ifndef MEDIASTATION_MEDIASCRIPT_OPERAND_H
 #define MEDIASTATION_MEDIASCRIPT_OPERAND_H
 
+#include "common/ptr.h"
 #include "common/str.h"
 
 #include "mediastation/mediascript/scriptconstants.h"
@@ -52,15 +53,18 @@ public:
 	void putVariable(Variable *variable);
 	Variable *getVariable();
 
-	void putFunction(uint functionId);
+	void putFunctionId(uint functionId);
 	uint getFunctionId();
 
+	void putMethodId(BuiltInMethod methodId);
+	BuiltInMethod getMethodId();
+
 	void putAsset(uint32 assetId);
 	Asset *getAsset();
 	uint32 getAssetId();
 
-	void putCollection(Collection *collection);
-	Collection *getCollection();
+	void putCollection(Common::SharedPtr<Collection> collection);
+	Common::SharedPtr<Collection> getCollection();
 
 	Operand getLiteralValue() const;
 
@@ -88,12 +92,13 @@ private:
 	union {
 		uint assetId = 0;
 		uint functionId;
+		BuiltInMethod methodId;
 		Common::String *string;
 		Variable *variable;
 		int i;
 		double d;
-		Collection *collection;
 	} _u;
+	Common::SharedPtr<Collection> _collection;
 };
 
 } // End of namespace MediaStation
diff --git a/engines/mediastation/mediascript/variable.cpp b/engines/mediastation/mediascript/variable.cpp
index c515993b22b..8cf8c6c36d0 100644
--- a/engines/mediastation/mediascript/variable.cpp
+++ b/engines/mediastation/mediascript/variable.cpp
@@ -19,20 +19,20 @@
  *
  */
 
+#include "mediastation/mediastation.h"
 #include "mediastation/mediascript/variable.h"
+#include "mediastation/mediascript/operand.h"
+#include "mediastation/mediascript/codechunk.h"
 #include "mediastation/datum.h"
 #include "mediastation/debugchannels.h"
-#include "mediastation/mediascript/operand.h"
 
 namespace MediaStation {
 
 Operand Collection::callMethod(BuiltInMethod method, Common::Array<Operand> &args) {
 	switch (method) {
 	case kIsEmptyMethod: {
-		// This is a built-in method that checks if a collection is empty.
-		// We can just check the size of the collection.
 		Operand returnValue(kOperandTypeLiteral1);
-		returnValue.putInteger(empty());
+		returnValue.putInteger(static_cast<uint>(empty()));
 		return returnValue;
 	}
 
@@ -48,6 +48,78 @@ Operand Collection::callMethod(BuiltInMethod method, Common::Array<Operand> &arg
 		return returnValue;
 	}
 
+	case kDeleteAtMethod: {
+		// Find the item in the collection, then remove and return it.
+		assert(args.size() == 1);
+		for (uint i = 0; i < size(); i++) {
+			if (args[0] == operator[](i)) {
+				Operand returnValue = remove_at(i);
+				return returnValue;
+			}
+		}
+
+		// The item wasn't found.
+		return Operand();
+	}
+
+	case kCountMethod: {
+		Operand returnValue = Operand(kOperandTypeLiteral1);
+		returnValue.putInteger(size());
+		return returnValue;
+	}
+
+	case kGetAtMethod: {
+		assert(args.size() == 1);
+		Operand returnValue = operator[](args[0].getInteger());
+		return returnValue;
+	}
+
+	case kSendMethod: {
+		// Call a method on each item in the collection.
+		BuiltInMethod methodToSend = static_cast<BuiltInMethod>(args[0].getMethodId());
+		Common::Array<Operand> sendArgs;
+		for (uint i = 0; i < size(); i++) {
+			Operand self = operator[](i);
+			CodeChunk::callBuiltInMethod(methodToSend, self, sendArgs);
+		}
+		return Operand();
+	}
+
+	case kSeekMethod: {
+		// Find the item in the collection if it exists.
+		assert(args.size() == 1);
+		for (uint i = 0; i < size(); i++) {
+			if (args[0] == operator[](i)) {
+				return operator[](i);
+			}
+		}
+
+		// The item wasn't found.
+		Operand returnValue(kOperandTypeLiteral1);
+		returnValue.putInteger(-1);
+		return returnValue;
+	}
+
+	case kJumbleMethod: {
+		// Scramble the items in the collection.
+		for (uint i = size() - 1; i > 0; --i) {
+			uint j = g_engine->_randomSource.getRandomNumber(size() - 1);
+			SWAP(operator[](i), operator[](j));
+		}
+		return Operand();
+	}
+
+	case kSortMethod: {
+		assert(args.empty());
+		Common::sort(begin(), end());
+		return Operand();
+	}
+
+	case kEmptyMethod: {
+		clear();
+		return Operand();
+	}
+
 	default:
 		error("Collection::callMethod(): Attempt to call unimplemented method %s (%d)", builtInMethodToStr(method), static_cast<uint>(method));
 	}
@@ -57,17 +129,18 @@ Variable::Variable(Chunk &chunk, bool readId) {
 	if (readId) {
 		_id = Datum(chunk).u.i;
 	}
+
 	_type = static_cast<VariableType>(Datum(chunk).u.i);
-	debugC(1, kDebugLoading, "Variable::Variable(): id = %d, type %s (%d) (@0x%llx)",
+	debugC(7, kDebugScript, "Variable::Variable(): id = %d, type %s (%d) (@0x%llx)",
 		_id, variableTypeToStr(_type), static_cast<uint>(_type), static_cast<long long int>(chunk.pos()));
-	switch ((VariableType)_type) {
+	switch (_type) {
 	case kVariableTypeCollection: {
 		uint totalItems = Datum(chunk).u.i;
-		_value.collection = new Collection;
+		_c = Common::SharedPtr<Collection>(new Collection);
 		for (uint i = 0; i < totalItems; i++) {
 			debugC(7, kDebugLoading, "Variable::Variable(): %s: Value %d of %d", variableTypeToStr(_type), i, totalItems);
 			Variable variable = Variable(chunk, readId = false);
-			_value.collection->push_back(variable.getValue());
+			_c->push_back(variable.getValue());
 		}
 		break;
 	}
@@ -119,21 +192,7 @@ Variable::Variable(Chunk &chunk, bool readId) {
 }
 
 Variable::~Variable() {
-	switch (_type) {
-	case kVariableTypeCollection: {
-		_value.collection->clear();
-		delete _value.collection;
-		break;
-	}
-
-	case kVariableTypeString: {
-		delete _value.string;
-		break;
-	}
-
-	default:
-		break;
-	}
+	clear();
 }
 
 Operand Variable::getValue() {
@@ -144,7 +203,7 @@ Operand Variable::getValue() {
 
 	case kVariableTypeCollection: {
 		Operand returnValue(kOperandTypeCollection);
-		returnValue.putCollection(_value.collection);
+		returnValue.putCollection(_c);
 		return returnValue;
 	}
 
@@ -190,6 +249,8 @@ Operand Variable::getValue() {
 }
 
 void Variable::putValue(Operand value) {
+	clear();
+
 	switch (value.getType()) {
 	case kOperandTypeEmpty: {
 		error("Variable::putValue(): Assigning an empty operand to a variable not supported");
@@ -233,10 +294,36 @@ void Variable::putValue(Operand value) {
 		break;
 	}
 
+	case kOperandTypeCollection: {
+		_type = kVariableTypeCollection;
+		_c = value.getCollection();
+		break;
+	}
+
 	default:
 		error("Variable::putValue(): Assigning an unknown operand type %s (%d) to a variable not supported",
 			operandTypeToStr(value.getType()), static_cast<uint>(value.getType()));
 	}
 }
 
+void Variable::clear() {
+	switch (_type) {
+	case kVariableTypeCollection: {
+		_c.reset();
+		break;
+	}
+
+	case kVariableTypeString: {
+		delete _value.string;
+		_value.string = nullptr;
+		break;
+	}
+
+	default:
+		break;
+	}
+
+	_type = kVariableTypeEmpty;
+}
+
 } // End of namespace MediaStation
diff --git a/engines/mediastation/mediascript/variable.h b/engines/mediastation/mediascript/variable.h
index a27d66c1c6f..22abd70b89f 100644
--- a/engines/mediastation/mediascript/variable.h
+++ b/engines/mediastation/mediascript/variable.h
@@ -22,6 +22,7 @@
 #ifndef MEDIASTATION_MEDIASCRIPT_VARIABLE_DECLARATION_H
 #define MEDIASTATION_MEDIASCRIPT_VARIABLE_DECLARATION_H
 
+#include "common/ptr.h"
 #include "common/str.h"
 #include "common/array.h"
 
@@ -44,19 +45,22 @@ public:
 	VariableType _type = kVariableTypeEmpty;
 	union {
 		Common::String *string;
-		Collection *collection;
 		uint functionId;
 		int i;
 		double d;
 		uint assetId;
 	} _value;
+	Common::SharedPtr<Collection> _c;
 
 	Variable();
 	Variable(Chunk &chunk, bool readId = true);
+	~Variable();
 
 	Operand getValue();
 	void putValue(Operand value);
-	~Variable();
+
+private:
+	void clear();
 };
 
 } // End of namespace MediaStation
diff --git a/engines/mediastation/mediastation.h b/engines/mediastation/mediastation.h
index dcc9b02c145..64bff78dbd9 100644
--- a/engines/mediastation/mediastation.h
+++ b/engines/mediastation/mediastation.h
@@ -89,6 +89,7 @@ public:
 	Operand callMethod(BuiltInMethod methodId, Common::Array<Operand> &args);
 	Operand callBuiltInFunction(BuiltInFunction function, Common::Array<Operand> &args);
 	Common::HashMap<uint32, Variable *> _variables;
+	Common::RandomSource _randomSource;
 
 	Graphics::Screen *_screen = nullptr;
 	Context *_currentContext = nullptr;
@@ -108,7 +109,6 @@ private:
 	Common::Event _event;
 	Common::FSNode _gameDataDir;
 	const ADGameDescription *_gameDescription;
-	Common::RandomSource _randomSource;
 
 	// In Media Station, only the cursors are stored in the executable; everything
 	// else is in the Context (*.CXT) data files.


Commit: 97ea2d6504e8c35d65a9822b019907b0201c1465
    https://github.com/scummvm/scummvm/commit/97ea2d6504e8c35d65a9822b019907b0201c1465
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-03-25T18:21:36-04:00

Commit Message:
MEDIASTATION: Stub Unk1 document method

Changed paths:
    engines/mediastation/mediascript/scriptconstants.cpp
    engines/mediastation/mediascript/scriptconstants.h
    engines/mediastation/mediastation.cpp


diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index be272355817..77776b9c3c9 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -108,6 +108,8 @@ const char *variableScopeToStr(VariableScope scope) {
 
 const char *builtInFunctionToStr(BuiltInFunction function) {
 	switch (function) {
+	case kUnk1Function:
+		return "Unk1Function";
 	case kEffectTransitionFunction:
 		return "EffectTransition";
 	case kEffectTransitionOnSyncFunction:
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index 6f3d27f5b2f..20de67f078d 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -73,6 +73,7 @@ enum VariableScope {
 const char *variableScopeToStr(VariableScope scope);
 
 enum BuiltInFunction {
+	kUnk1Function = 10,
 	// TODO: Figure out if effectTransitionOnSync = 13 is consistent across titles?
 	kEffectTransitionFunction = 12, // PARAMS: 1
 	kEffectTransitionOnSyncFunction = 13,
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 28bff731ab3..9c115d033eb 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -504,6 +504,13 @@ Operand MediaStationEngine::callBuiltInFunction(BuiltInFunction function, Common
 		return Operand();
 	}
 
+	case kUnk1Function: {
+		warning("MediaStationEngine::callBuiltInFunction(): Function 10 not implemented");
+		Operand returnValue = Operand(kOperandTypeLiteral1);
+		returnValue.putInteger(1);
+		return returnValue;
+	}
+
 	default:
 		error("MediaStationEngine::callBuiltInFunction(): Got unknown built-in function %s (%d)", builtInFunctionToStr(function), static_cast<uint>(function));
 	}


Commit: ca95c3596cdee62e95a3613fb1eda50d85446b17
    https://github.com/scummvm/scummvm/commit/ca95c3596cdee62e95a3613fb1eda50d85446b17
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-03-25T18:21:36-04:00

Commit Message:
MEDIASTATION: Don't release contexts too early

Changed paths:
    engines/mediastation/mediastation.cpp
    engines/mediastation/mediastation.h


diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 9c115d033eb..1a826eb8572 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -159,6 +159,13 @@ Common::Error MediaStationEngine::run() {
 			break;
 		}
 
+		if (!_requestedContextReleaseId.empty()) {
+			for (uint contextId : _requestedContextReleaseId) {
+				releaseContext(contextId);
+			}
+			_requestedContextReleaseId.clear();
+		}
+
 		debugC(5, kDebugGraphics, "***** START SCREEN UPDATE ***");
 		for (auto it = _assetsPlaying.begin(); it != _assetsPlaying.end();) {
 			(*it)->process();
@@ -403,7 +410,7 @@ Operand MediaStationEngine::callMethod(BuiltInMethod methodId, Common::Array<Ope
 	case kReleaseContextMethod: {
 		assert(args.size() == 1);
 		uint32 contextId = args[0].getAssetId();
-		releaseContext(contextId);
+		_requestedContextReleaseId.push_back(contextId);
 		return Operand();
 	}
 
@@ -452,6 +459,17 @@ void MediaStationEngine::releaseContext(uint32 contextId) {
 		error("MediaStationEngine::releaseContext(): Attempted to unload context %d that is not currently loaded", contextId);
 	}
 
+	// Make sure nothing is still using this context.
+	for (auto it = _loadedContexts.begin(); it != _loadedContexts.end(); ++it) {
+		uint id = it->_key;
+		ContextDeclaration *contextDeclaration = _boot->_contextDeclarations.getValOrDefault(id);
+		for (uint32 childContextId : contextDeclaration->_fileReferences) {
+			if (childContextId == contextId) {
+				return;
+			}
+		}
+	}
+
 	// Unload any assets currently playing from this context. They should have
 	// already been stopped by scripts, but this is a last check.
 	for (auto it = _assetsPlaying.begin(); it != _assetsPlaying.end();) {
diff --git a/engines/mediastation/mediastation.h b/engines/mediastation/mediastation.h
index 64bff78dbd9..dd11d511290 100644
--- a/engines/mediastation/mediastation.h
+++ b/engines/mediastation/mediastation.h
@@ -121,6 +121,7 @@ private:
 	Asset *_currentHotspot = nullptr;
 
 	uint _requestedScreenBranchId = 0;
+	Common::Array<uint> _requestedContextReleaseId;
 	void doBranchToScreen();
 
 	Context *loadContext(uint32 contextId);


Commit: d6a62221a698ebed8ecb1694c0388c24ff3707fd
    https://github.com/scummvm/scummvm/commit/d6a62221a698ebed8ecb1694c0388c24ff3707fd
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-03-25T19:26:27-04:00

Commit Message:
MEDIASTATION: Permit scripts to coerce some types

Changed paths:
    engines/mediastation/mediascript/operand.cpp


diff --git a/engines/mediastation/mediascript/operand.cpp b/engines/mediastation/mediascript/operand.cpp
index 631721d400e..f0a10727b32 100644
--- a/engines/mediastation/mediascript/operand.cpp
+++ b/engines/mediastation/mediascript/operand.cpp
@@ -53,6 +53,10 @@ int Operand::getInteger() {
 		return _u.i;
 	}
 
+	case kOperandTypeFloat1: {
+		return static_cast<int>(_u.d);
+	}
+
 	case kOperandTypeVariableDeclaration: {
 		return _u.variable->_value.i;
 	}
@@ -90,6 +94,11 @@ double Operand::getDouble() {
 		return _u.d;
 	}
 
+	case kOperandTypeLiteral1:
+	case kOperandTypeLiteral2: {
+		return static_cast<double>(_u.i);
+	}
+
 	case kOperandTypeVariableDeclaration: {
 		// TODO: Add assertion that this is the proper type.
 		return _u.variable->_value.d;
@@ -336,7 +345,17 @@ bool Operand::operator==(const Operand &other) const {
 			return lhs.getInteger() == rhs.getInteger();
 
 		case kOperandTypeAssetId:
-			return lhs.getAssetId() == rhs.getAssetId();
+			if (rhs.getType() == kOperandTypeLiteral2) {
+				// This might happen if, for example, a given asset wasn't found
+				// in a collection and the script sets the return value to -1.
+				return static_cast<int>(lhs.getAssetId()) == rhs.getInteger();
+			} else {
+				// If the types are incompatiable, rhs will raise the error.
+				return lhs.getAssetId() == rhs.getAssetId();
+			}
+
+		case kOperandTypeString:
+			return *lhs.getString() == *rhs.getString();
 
 		default:
 			error("Operand::operator==(): Unimplemented operand types %s and %s", operandTypeToStr(lhs.getType()), operandTypeToStr(rhs.getType()));


Commit: 61859559cd8fabd26ed7ca7da073b8268cbabfe0
    https://github.com/scummvm/scummvm/commit/61859559cd8fabd26ed7ca7da073b8268cbabfe0
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-03-25T19:26:27-04:00

Commit Message:
MEDIASTATION: Allow movies to move under script control

In Dalmatians 185.CXT, the soot "cursor" is a movie, not a
sprite as with the other larger cursors in Dalmatians.

Changed paths:
    engines/mediastation/assets/movie.cpp
    engines/mediastation/assets/movie.h
    engines/mediastation/mediascript/scriptconstants.cpp
    engines/mediastation/mediascript/scriptconstants.h


diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index 3bf832c9876..e75f9dd5b39 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -212,13 +212,53 @@ Operand Movie::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args)
 		return Operand();
 	}
 
+	case kIsVisibleMethod: {
+		assert(args.empty());
+		Operand returnValue(kOperandTypeLiteral1);
+		returnValue.putInteger(_isShowing);
+		return returnValue;
+	}
+
+	case kSpatialCenterMoveToMethod: {
+		assert(args.size() == 2);
+		spatialCenterMoveTo(args[0].getInteger(), args[1].getInteger());
+		return Operand();
+	}
+
+	case kSpatialMoveToMethod: {
+		assert(args.size() == 2);
+		spatialMoveTo(args[0].getInteger(), args[1].getInteger());
+		return Operand();
+	}
+
 	case kIsPlayingMethod: {
 		assert(args.empty());
 		Operand returnValue(kOperandTypeLiteral1);
-		returnValue.putInteger(_isPlaying);
+		returnValue.putInteger(static_cast<uint>(_isPlaying));
 		return returnValue;
 	}
 
+	case kXPositionMethod: {
+		assert(args.empty());
+		Operand returnValue(kOperandTypeLiteral1);
+		returnValue.putInteger(_header->_boundingBox->left);
+		return returnValue;
+
+	}
+
+	case kYPositionMethod: {
+		assert(args.empty());
+		Operand returnValue(kOperandTypeLiteral1);
+		returnValue.putInteger(_header->_boundingBox->top);
+		return returnValue;
+	}
+
+	case kSetDissolveFactorMethod: {
+		assert(args.size() == 1);
+		warning("Movie::callMethod(): setDissolveFactor not implemented yet");
+		return Operand();
+	}
+
 	default:
 		error("Movie::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
 	}
@@ -330,8 +370,52 @@ void Movie::timeStop() {
 	runEventHandlerIfExists(kMovieStoppedEvent);
 }
 
+void Movie::spatialCenterMoveTo(int x, int y) {
+	// Mark the previous location dirty.
+	for (MovieFrame *frame : _framesOnScreen) {
+		Common::Rect bbox = getFrameBoundingBox(frame);
+		g_engine->_dirtyRects.push_back(bbox);
+	}
+
+	// Calculate the center of the movie, and update location.
+	int frameWidth = _header->_boundingBox->width();
+	int frameHeight = _header->_boundingBox->height();
+	int centerX = x - frameWidth / 2;
+	int centerY = y - frameHeight / 2;
+
+	debugC(5, kDebugScript, "Movie::callMethod(): (%d) Moving movie center to (%d, %d)", _header->_id, x, y);
+	// Unlike the sprites, movie bounding boxes must be moved too.
+	_header->_boundingBox->moveTo(centerX, centerY);
+
+	// Mark the new location dirty.
+	for (MovieFrame *frame : _framesOnScreen) {
+		Common::Rect bbox = getFrameBoundingBox(frame);
+		g_engine->_dirtyRects.push_back(bbox);
+	}
+}
+
+void Movie::spatialMoveTo(int x, int y) {
+	// Mark the previous location dirty.
+	for (MovieFrame *frame : _framesOnScreen) {
+		Common::Rect bbox = getFrameBoundingBox(frame);
+		g_engine->_dirtyRects.push_back(bbox);
+	}
+
+	// Update the location and mark the new location dirty.
+	debugC(5, kDebugGraphics, "Movie::callMethod(): (%d) Moving movie to (%d, %d)", _header->_id, x, y);
+	// Unlike the sprites, movie bounding boxes must be moved too.
+	_header->_boundingBox->moveTo(x, y);
+
+	for (MovieFrame *frame : _framesOnScreen) {
+		Common::Rect bbox = getFrameBoundingBox(frame);
+		g_engine->_dirtyRects.push_back(bbox);
+	}
+}
+
 void Movie::process() {
-	processTimeEventHandlers();
+	if (_isPlaying) {
+		processTimeEventHandlers();
+	}
 	updateFrameState();
 }
 
@@ -406,7 +490,6 @@ void Movie::updateFrameState() {
 		return;
 	}
 
-
 	// Show the frames that are currently active, for debugging purposes.
 	for (MovieFrame *frame : _framesOnScreen) {
 		debugC(5, kDebugGraphics, "   (time: %d ms) Frame %d (%d x %d) @ (%d, %d); start: %d ms, end: %d ms, keyframeEnd: %d ms, zIndex = %d", movieTime, frame->index(), frame->width(), frame->height(), frame->left(), frame->top(), frame->startInMilliseconds(), frame->endInMilliseconds(), frame->keyframeEndInMilliseconds(), frame->zCoordinate());
@@ -432,6 +515,7 @@ void Movie::redraw(Common::Rect &rect) {
 		if (!areaToRedraw.isEmpty()) {
 			Common::Point originOnScreen(areaToRedraw.left, areaToRedraw.top);
 			areaToRedraw.translate(-frame->left() - _header->_boundingBox->left, -frame->top() - _header->_boundingBox->top);
+			areaToRedraw.clip(Common::Rect(0, 0, frame->width(), frame->height()));
 			g_engine->_screen->simpleBlitFrom(frame->_surface, areaToRedraw, originOnScreen);
 		}
 	}
diff --git a/engines/mediastation/assets/movie.h b/engines/mediastation/assets/movie.h
index 52647d9ffa3..5b1841449b7 100644
--- a/engines/mediastation/assets/movie.h
+++ b/engines/mediastation/assets/movie.h
@@ -121,6 +121,8 @@ private:
 	void timeStop();
 	void spatialShow();
 	void spatialHide();
+	void spatialCenterMoveTo(int x, int y);
+	void spatialMoveTo(int x, int y);
 
 	void updateFrameState();
 	void showPersistentFrame();
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index 77776b9c3c9..4a11bc9d408 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -133,6 +133,8 @@ const char *builtInMethodToStr(BuiltInMethod method) {
 		return "SpatialMoveTo";
 	case kSpatialZMoveToMethod:
 		return "SpatialZMoveTo";
+	case kSpatialCenterMoveToMethod:
+		return "SpatialCenterMoveTo";
 	case kSpatialShowMethod:
 		return "SpatialShow";
 	case kTimePlayMethod:
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index 20de67f078d..be33460b6a0 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -98,6 +98,7 @@ enum BuiltInMethod {
 	kTimeStopMethod = 207, // PARAMS: 0
 	kIsPlayingMethod = 372, // PARAMS: 0
 	kSetDissolveFactorMethod = 241, // PARAMS: 1
+	kSpatialCenterMoveToMethod = 230,
 
 	// HOTSPOT METHODS.
 	kMouseActivateMethod = 210, // PARAMS: 1


Commit: 0658576196e2c767268eb77e2ee1f14c03b3762a
    https://github.com/scummvm/scummvm/commit/0658576196e2c767268eb77e2ee1f14c03b3762a
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-03-25T19:26:27-04:00

Commit Message:
MEDIASTATION: Make path playback non-blocking

Changed paths:
    engines/mediastation/assetheader.cpp
    engines/mediastation/assetheader.h
    engines/mediastation/assets/path.cpp
    engines/mediastation/assets/path.h


diff --git a/engines/mediastation/assetheader.cpp b/engines/mediastation/assetheader.cpp
index 2af6e18635e..33196c88e86 100644
--- a/engines/mediastation/assetheader.cpp
+++ b/engines/mediastation/assetheader.cpp
@@ -365,6 +365,11 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 		break;
 	}
 
+    case kAssetHeaderPathTotalSteps: {
+		_totalSteps = Datum(chunk).u.i;
+		break;
+	}
+
 	default:
 		error("AssetHeader::readSection(): Unknown section type 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
 	}
diff --git a/engines/mediastation/assetheader.h b/engines/mediastation/assetheader.h
index 29cbbbce553..b8f327fbd55 100644
--- a/engines/mediastation/assetheader.h
+++ b/engines/mediastation/assetheader.h
@@ -108,7 +108,7 @@ enum AssetHeaderSectionType {
 	// PATH FIELDS.
 	kAssetHeaderStartPoint = 0x060e,
 	kAssetHeaderEndPoint = 0x060f,
-	kAssetHeaderPathUnk1 = 0x0610,
+	kAssetHeaderPathTotalSteps = 0x0610,
 	kAssetHeaderStepRate = 0x0611,
 	kAssetHeaderDuration = 0x0612,
 
@@ -200,6 +200,7 @@ public:
 	Common::Point *_endPoint = nullptr;
 	uint32 _stepRate = 0;
 	uint32 _duration = 0;
+	uint _totalSteps = 0;
 
 	// EVENT HANDLER FIELDS.
 	Common::HashMap<uint, EventHandler *> _eventHandlers;
diff --git a/engines/mediastation/assets/path.cpp b/engines/mediastation/assets/path.cpp
index 1b3ca6205d8..c6744ceab6e 100644
--- a/engines/mediastation/assets/path.cpp
+++ b/engines/mediastation/assets/path.cpp
@@ -56,43 +56,70 @@ Operand Path::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) {
 		return Operand();
 	}
 
+	case kIsPlayingMethod: {
+		assert(args.empty());
+		Operand returnValue(kOperandTypeLiteral1);
+		returnValue.putInteger(_isActive);
+		return returnValue;
+	}
+
 	default:
 		error("Path::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
 	}
 }
 
 void Path::timePlay() {
-	// TODO: Check that itʻs zero before we reset it, since this function isn't re-entrant!
-	_percentComplete = 0.0;
+	if (_isActive) {
+		warning("Path::timePlay(): Attempted to play a path that is already playing");
+		return;
+	}
 
 	if (_header->_duration == 0) {
 		warning("Path::timePlay(): Got zero duration");
 	} else if (_header->_stepRate == 0) {
 		error("Path::timePlay(): Got zero step rate");
 	}
-	debugC(5, kDebugScript, "Path::timePlay(): Path playback started");
-	uint totalSteps = (_header->_duration * _header->_stepRate) / 1000;
-	//uint stepDurationInMilliseconds = 1000 / _header->_stepRate;
+
+	setActive();
+	_percentComplete = 0;
+	_nextPathStepTime = 0;
+	_currentStep = 0;
+	_totalSteps = (_header->_duration * _header->_stepRate) / 1000;
+	_stepDurationInMilliseconds = 1000 / _header->_stepRate;
 
 	// TODO: Run the path start event. Haven't seen one the wild yet, don't know its ID.
 	debugC(5, kDebugScript, "Path::timePlay(): No PathStart event handler");
+}
+
+void Path::process() {
+	uint currentTime = g_system->getMillis();
+	uint pathTime = currentTime - _startTime;
 
-	// Step the path.
-	for (uint i = 0; i < totalSteps; i++) {
-		_percentComplete = (double)(i + 1) / totalSteps;
-		debugC(5, kDebugScript, "Path::timePlay(): Step %d of %d", i, totalSteps);
+	bool doNextStep = pathTime >= _nextPathStepTime;
+	if (!doNextStep) {
+		return;
+	}
+
+	_percentComplete = static_cast<double>(_currentStep + 1) / _totalSteps;
+	debugC(2, kDebugScript, "Path::timePlay(): Step %d of %d", _currentStep, _totalSteps);
+
+	if (_currentStep < _totalSteps) {
 		// TODO: Actually step the path. It seems they mostly just use this for
 		// palette animation in the On Step event handler, so nothing is actually drawn on the screen now.
 
+		// We donʻt run a step event for the last step.
 		runEventHandlerIfExists(kStepEvent);
+		_nextPathStepTime = ++_currentStep * _stepDurationInMilliseconds;
+	} else {
+		setInactive();
+		_percentComplete = 0;
+		_nextPathStepTime = 0;
+		_currentStep = 0;
+		_totalSteps = 0;
+		_stepDurationInMilliseconds = 0;
+
+		runEventHandlerIfExists(kPathEndEvent);
 	}
-
-	runEventHandlerIfExists(kPathEndEvent);
-	_percentComplete = 0;
-}
-
-void Path::process() {
-	// TODO: Handle this case.
 }
 
 void Path::setDuration(uint durationInMilliseconds) {
diff --git a/engines/mediastation/assets/path.h b/engines/mediastation/assets/path.h
index 07908f53592..939dfdf1e41 100644
--- a/engines/mediastation/assets/path.h
+++ b/engines/mediastation/assets/path.h
@@ -40,6 +40,10 @@ public:
 
 private:
 	double _percentComplete = 0.0;
+	uint _totalSteps = 0;
+	uint _currentStep = 0;
+	uint _nextPathStepTime = 0;
+	uint _stepDurationInMilliseconds = 0;
 
 	// Method implementations.
 	void timePlay();




More information about the Scummvm-git-logs mailing list