[Scummvm-git-logs] scummvm master -> 028c5c6bba1f5305d4100bf225b224d147591e54

sev- noreply at scummvm.org
Sun Jun 28 20:59:27 UTC 2026


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

Summary:
7acb94791d DIRECTOR: LINGO: Implement sendSprite, sendAllSprites and call
028c5c6bba DIRECTOR: Add quirks for TKKG 5-9 and Oscar Mountain


Commit: 7acb94791d6250e364b4349c3c79778fd1fde331
    https://github.com/scummvm/scummvm/commit/7acb94791d6250e364b4349c3c79778fd1fde331
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-28T22:59:22+02:00

Commit Message:
DIRECTOR: LINGO: Implement sendSprite, sendAllSprites and call

Tested with TKKG 2-5 and Löwenzahn 2-4

Changed paths:
    engines/director/lingo/lingo-builtins.cpp
    engines/director/lingo/lingo-code.cpp


diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index ef0815fbd16..c5936651696 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -123,7 +123,7 @@ static const BuiltinProto builtins[] = {
 	// Control
 	{ "abort",			LB::b_abort,		0, 0, 400, CBLTIN },	//			D4 c
 	{ "cancelIdleLoad",	LB::b_cancelIdleLoad,1,1, 500, CBLTIN },	//				D5 c
-	{ "call",			LB::b_call,			-1,0, 600, CBLTIN },	// 					D6 c
+	{ "call",			LB::b_call,			-1,0, 600, HBLTIN },	// 					D6 c (also usable as function)
 	{ "callAncestor",	LB::b_callAncestor,	-1,0, 600, CBLTIN },	// 					D6 c
 	{ "continue",		LB::b_continue,		0, 0, 200, CBLTIN },	// D2 c
 	{ "dontPassEvent",	LB::b_dontPassEvent,0, 0, 200, CBLTIN },	// D2 c
@@ -146,7 +146,7 @@ static const BuiltinProto builtins[] = {
 	{ "quit",			LB::b_quit,			0, 0, 200, CBLTIN },	// D2 c
 	{ "restart",		LB::b_restart,		0, 0, 200, CBLTIN },	// D2 c
 	{ "return",			LB::b_return,		0, 1, 200, CBLTIN },	// D2 f
-	{ "send",			LB::b_call,			-1,0, 400, CBLTIN },	//			D4 c, undocumented
+	{ "send",			LB::b_call,			-1,0, 400, HBLTIN },	//			D4 c, undocumented (also usable as function)
 	{ "sendAncestor",	LB::b_callAncestor,	-1,0, 400, CBLTIN },	//			D4 c, undocumented
 	{ "shutDown",		LB::b_shutDown,		0, 0, 200, CBLTIN },	// D2 c
 	{ "startTimer",		LB::b_startTimer,	0, 0, 200, CBLTIN },	// D2 c
@@ -202,8 +202,8 @@ static const BuiltinProto builtins[] = {
 	{ "puppetTransition",LB::b_puppetTransition,-1,0,200, CBLTIN },	// D2 c
 	{ "ramNeeded",		LB::b_ramNeeded,	2, 2, 300, FBLTIN },	//		D3.1 f
 	{ "rollOver",		LB::b_rollOver,		0, 1, 200, FBLTIN },	// D2 f
-	{ "sendAllSprites",	LB::b_sendAllSprites,-1,0,600, CBLTIN },	// 					D6 c
-	{ "sendSprite",		LB::b_sendSprite,	-1,0, 600, CBLTIN },	// 					D6 c
+	{ "sendAllSprites",	LB::b_sendAllSprites,-1,0,600, HBLTIN },	// 					D6 c (also usable as function)
+	{ "sendSprite",		LB::b_sendSprite,	-1,0, 600, HBLTIN },	// 					D6 c (also usable as function)
 	{ "spriteBox",		LB::b_spriteBox,	5, 5, 200, CBLTIN },	// D2 c
 	{ "unLoad",			LB::b_unLoad,		0, 2, 300, CBLTIN },	//		D3.1 c
 	{ "unLoadCast",		LB::b_unLoadCast,	0, 2, 300, CBLTIN },	//		D3.1 c
@@ -1938,9 +1938,57 @@ void LB::b_abort(int nargs) {
 	g_lingo->_abort = true;
 }
 
+// Helper: call a named handler on a single behavior instance with extra args.
+// extraArgs is stored in reverse order (as popped from stack).
+static Datum callBehaviorHandler(Datum &instance, const Common::String &msgName, const Common::Array<Datum> &extraArgs) {
+	Symbol sym = instance.u.obj->getMethod(msgName);
+	if (sym.type == VOIDSYM)
+		return Datum();
+
+	g_lingo->push(instance);
+	for (int j = (int)extraArgs.size() - 1; j >= 0; j--)
+		g_lingo->push(extraArgs[j]);
+
+	int frame = g_lingo->_state->callstack.size();
+	LC::call(sym, 1 + (int)extraArgs.size(), true);
+	g_lingo->execute(frame);
+	return g_lingo->pop();
+}
+
 void LB::b_call(int nargs) {
-	g_lingo->printSTUBWithArglist("b_call", nargs);
-	g_lingo->dropStack(nargs);
+	bool allowRetVal = g_lingo->pop().asInt() != 0;
+	if (nargs < 2) {
+		warning("b_call: expected at least 2 args, got %d", nargs);
+		g_lingo->dropStack(nargs);
+		if (allowRetVal)
+			g_lingo->pushVoid();
+		return;
+	}
+
+	int numExtraArgs = nargs - 2;
+	Common::Array<Datum> extraArgs;
+	for (int i = 0; i < numExtraArgs; i++)
+		extraArgs.push_back(g_lingo->pop());
+
+	Datum script = g_lingo->pop();
+	Datum message = g_lingo->pop();
+	Common::String msgName = message.asString();
+
+	Datum result;
+	if (script.type == OBJECT) {
+		result = callBehaviorHandler(script, msgName, extraArgs);
+	} else if (script.type == ARRAY) {
+		for (uint i = 0; i < script.u.farr->arr.size(); i++) {
+			Datum instance = script.u.farr->arr[i];
+			if (instance.type == OBJECT)
+				result = callBehaviorHandler(instance, msgName, extraArgs);
+		}
+	} else {
+		warning("b_call: expected OBJECT or list for script argument");
+	}
+
+	if (allowRetVal)
+		g_lingo->push(result);
 }
 
 void LB::b_callAncestor(int nargs) {
@@ -3444,13 +3492,122 @@ void LB::b_rollOver(int nargs) {
 }
 
 void LB::b_sendAllSprites(int nargs) {
-	g_lingo->printSTUBWithArglist("b_sendAllSprites", nargs);
-	g_lingo->dropStack(nargs);
+	bool allowRetVal = g_lingo->pop().asInt() != 0;
+	if (nargs < 1) {
+		warning("b_sendAllSprites: expected at least 1 arg, got %d", nargs);
+		if (allowRetVal)
+			g_lingo->pushVoid();
+		return;
+	}
+
+	int numExtraArgs = nargs - 1;
+	Common::Array<Datum> extraArgs;
+	for (int i = 0; i < numExtraArgs; i++)
+		extraArgs.push_back(g_lingo->pop());
+
+	Datum message = g_lingo->pop();
+	Common::String msgName = message.asString();
+
+	Datum result;
+	Movie *movie = g_director->getCurrentMovie();
+	Score *score = movie ? movie->getScore() : nullptr;
+	if (score) {
+		score->createScriptInstances(score->getCurrentFrameNum());
+
+		bool anyHandled = false;
+		uint savedSpriteNum = movie->_currentSpriteNum;
+		for (uint ch = 1; ch < score->_channels.size(); ch++) {
+			Channel *channel = score->_channels[ch];
+			if (!channel)
+				continue;
+			for (uint i = 0; i < channel->_scriptInstanceList.size(); i++) {
+				Datum instance = channel->_scriptInstanceList[i];
+				if (instance.type != OBJECT)
+					continue;
+				Symbol sym = instance.u.obj->getMethod(msgName);
+				if (sym.type == VOIDSYM)
+					continue;
+				movie->_currentSpriteNum = ch;
+				result = callBehaviorHandler(instance, msgName, extraArgs);
+				movie->_currentSpriteNum = savedSpriteNum;
+				anyHandled = true;
+			}
+		}
+
+		if (!anyHandled) {
+			Symbol h = g_lingo->getHandler(msgName);
+			if (h.type != VOIDSYM) {
+				for (int j = (int)extraArgs.size() - 1; j >= 0; j--)
+					g_lingo->push(extraArgs[j]);
+				int frame = g_lingo->_state->callstack.size();
+				LC::call(h, numExtraArgs, true);
+				g_lingo->execute(frame);
+				result = g_lingo->pop();
+			}
+		}
+	}
+
+	if (allowRetVal)
+		g_lingo->push(result);
 }
 
 void LB::b_sendSprite(int nargs) {
-	g_lingo->printSTUBWithArglist("b_sendSprite", nargs);
-	g_lingo->dropStack(nargs);
+	bool allowRetVal = g_lingo->pop().asInt() != 0;
+	if (nargs < 2) {
+		warning("b_sendSprite: expected at least 2 args, got %d", nargs);
+		g_lingo->dropStack(nargs);
+		if (allowRetVal)
+			g_lingo->pushVoid();
+		return;
+	}
+
+	int numExtraArgs = nargs - 2;
+	Common::Array<Datum> extraArgs;
+	for (int i = 0; i < numExtraArgs; i++)
+		extraArgs.push_back(g_lingo->pop());
+
+	Datum message = g_lingo->pop();
+	int spriteNum = g_lingo->pop().asInt();
+	Common::String msgName = message.asString();
+
+	Datum result;
+	Movie *movie = g_director->getCurrentMovie();
+	Score *score = movie ? movie->getScore() : nullptr;
+	Channel *channel = score ? score->getChannelById((uint16)spriteNum) : nullptr;
+	if (channel) {
+		if (channel->_scriptInstanceList.empty() && channel->_sprite && !channel->_sprite->_behaviors.empty())
+			score->createScriptInstances(score->getCurrentFrameNum());
+
+		bool handled = false;
+		uint savedSpriteNum = movie->_currentSpriteNum;
+		for (uint i = 0; i < channel->_scriptInstanceList.size(); i++) {
+			Datum instance = channel->_scriptInstanceList[i];
+			if (instance.type != OBJECT)
+				continue;
+			Symbol sym = instance.u.obj->getMethod(msgName);
+			if (sym.type == VOIDSYM)
+				continue;
+			movie->_currentSpriteNum = (uint)spriteNum;
+			result = callBehaviorHandler(instance, msgName, extraArgs);
+			movie->_currentSpriteNum = savedSpriteNum;
+			handled = true;
+		}
+
+		if (!handled) {
+			Symbol h = g_lingo->getHandler(msgName);
+			if (h.type != VOIDSYM) {
+				for (int j = (int)extraArgs.size() - 1; j >= 0; j--)
+					g_lingo->push(extraArgs[j]);
+				int frame = g_lingo->_state->callstack.size();
+				LC::call(h, numExtraArgs, true);
+				g_lingo->execute(frame);
+				result = g_lingo->pop();
+			}
+		}
+	}
+
+	if (allowRetVal)
+		g_lingo->push(result);
 }
 
 void LB::b_spriteBox(int nargs) {
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 6d1a422f89c..967390ab122 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -1807,6 +1807,14 @@ void LC::call(const Symbol &funcSym, int nargs, bool allowRetVal) {
 			(*funcSym.u.bltin)(nargs);
 			g_lingo->_state->me = retMe;
 		} else {
+			// sendSprite/sendAllSprites/call/send can be used both as a command and
+			// as a function (returning the handler's result). 
+			if (funcSym.name && (funcSym.name->equalsIgnoreCase("sendSprite") ||
+					funcSym.name->equalsIgnoreCase("sendAllSprites") ||
+					funcSym.name->equalsIgnoreCase("call") ||
+					funcSym.name->equalsIgnoreCase("send")))
+				g_lingo->push(Datum(allowRetVal));
+
 			(*funcSym.u.bltin)(nargs);
 		}
 


Commit: 028c5c6bba1f5305d4100bf225b224d147591e54
    https://github.com/scummvm/scummvm/commit/028c5c6bba1f5305d4100bf225b224d147591e54
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-28T22:59:22+02:00

Commit Message:
DIRECTOR: Add quirks for TKKG 5-9 and Oscar Mountain

Changed paths:
    engines/director/game-quirks.cpp


diff --git a/engines/director/game-quirks.cpp b/engines/director/game-quirks.cpp
index 26601391c4e..82308bcdac6 100644
--- a/engines/director/game-quirks.cpp
+++ b/engines/director/game-quirks.cpp
@@ -123,6 +123,30 @@ struct CachedFile {
 		"PATH.INI",
 		(const byte *)"[cd-path]\r\npath=d:\\\r\n", -1
 	},
+	{"tkkg5", Common::kPlatformWindows,
+		"TKKG_5.ini",
+		(const byte *)"[CD]\r\npath=d\r\n", -1
+	},
+	{"tkkg6", Common::kPlatformWindows,
+		"TKKG_6.ini",
+		(const byte *)"[CD]\r\npath=d\r\n", -1
+	},
+	{"tkkg7", Common::kPlatformWindows,
+		"TKKG_7.ini",
+		(const byte *)"[CDpath]\r\npath=d:\\\r\n", -1
+	},
+	{"tkkg8", Common::kPlatformWindows,
+		"TKKG_8.ini",
+		(const byte *)"[CDpath]\r\npath=d:\\\r\n", -1
+	},
+	{"tkkg9", Common::kPlatformWindows,
+		"TKKG_9.ini",
+		(const byte *)"[CDpath]\r\npath=d:\\\r\n", -1
+	},
+	{"oscar5", Common::kPlatformWindows,
+		"Oscar_Be.ini",
+		(const byte *)"[CDpath]\r\npath=d:\\\r\n", -1
+	},
 
 	// Professor Finkle's Times Table Factory has an installer that copies a bunch of empty files,
 	// which the game gets upset about if they don't exist.




More information about the Scummvm-git-logs mailing list