[Scummvm-git-logs] scummvm master -> 50e3033998dbc375ddfab203b29c839a0467837b

bluegr noreply at scummvm.org
Sun Mar 15 19:21:58 UTC 2026


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

Summary:
50e3033998 TINSEL: New DW1 introduction skip technique


Commit: 50e3033998dbc375ddfab203b29c839a0467837b
    https://github.com/scummvm/scummvm/commit/50e3033998dbc375ddfab203b29c839a0467837b
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2026-03-15T21:21:54+02:00

Commit Message:
TINSEL: New DW1 introduction skip technique

Pressing Escape during the introduction logos and credits
now skips to the title screen in all versions.

Fixes bug #14627

Changed paths:
    engines/tinsel/debugger.cpp
    engines/tinsel/handle.cpp
    engines/tinsel/handle.h
    engines/tinsel/pcode.cpp
    engines/tinsel/saveload.cpp
    engines/tinsel/scene.cpp
    engines/tinsel/scene.h
    engines/tinsel/tinlib.cpp
    engines/tinsel/tinsel.cpp


diff --git a/engines/tinsel/debugger.cpp b/engines/tinsel/debugger.cpp
index 702bee887c9..1357f8b5e94 100644
--- a/engines/tinsel/debugger.cpp
+++ b/engines/tinsel/debugger.cpp
@@ -152,6 +152,7 @@ bool Console::cmd_scene(int argc, const char **argv) {
 		return true;
 	}
 
+	EndDw1Intro();
 	SetNewScene(sceneNumber << SCNHANDLE_SHIFT, entryNumber, TRANS_CUT);
 	return false;
 }
diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp
index 83706c9bd35..a6ae23bbd40 100644
--- a/engines/tinsel/handle.cpp
+++ b/engines/tinsel/handle.cpp
@@ -67,7 +67,8 @@ enum {
 #define MEMFLAGS(x) ((TinselVersion == 3) ? x->flags2 : x->filesize)
 #define MEMFLAGSET(x, mask) ((TinselVersion == 3) ? x->flags2 |= mask : x->filesize |= mask)
 
-Handle::Handle() : _handleTable(0), _numHandles(0), _cdPlayHandle((uint32)-1), _cdBaseHandle(0), _cdTopHandle(0), _cdGraphStream(nullptr) {
+Handle::Handle() : _handleTable(0), _numHandles(0), _cdPlayHandle((uint32)-1), _cdBaseHandle(0), _cdTopHandle(0), _cdGraphStream(nullptr),
+	_dw1TitleSceneHandle(0) {
 }
 
 Handle::~Handle() {
@@ -166,6 +167,19 @@ void Handle::SetupHandleTable() {
 			assert(pH->_node);
 		}
 	}
+
+	// Detect DW1 title scene handle for introduction skipping
+	if (TinselVersion == 1) {
+		// PSX versions swapped the title and turtle scene order
+		const char *titleSceneName = TinselV1PSX ? "turtle." : "title.";
+		const int sceneNameLength = strlen(titleSceneName);
+		for (i = 0; i < _numHandles; i++) {
+			if (!scumm_strnicmp(_handleTable[i].szName, titleSceneName, sceneNameLength)) {
+				_dw1TitleSceneHandle = i << SCNHANDLE_SHIFT;
+				break;
+			}
+		}
+	}
 }
 
 /**
diff --git a/engines/tinsel/handle.h b/engines/tinsel/handle.h
index 75b57ce0dca..f8c2b5269ea 100644
--- a/engines/tinsel/handle.h
+++ b/engines/tinsel/handle.h
@@ -78,6 +78,9 @@ public:
 
 	int CdNumber(SCNHANDLE offset);
 
+	// Used for skipping DW1 introduction
+	SCNHANDLE GetDw1TitleSceneHandle() { return _dw1TitleSceneHandle; }
+
 	// Noir
 	SCNHANDLE FindLanguageSceneHandle(const char *fileName);
 
@@ -106,6 +109,8 @@ private:
 	Common::File *_cdGraphStream;
 
 	Common::Path _szCdPlayFile;
+
+	SCNHANDLE _dw1TitleSceneHandle;
 };
 
 } // End of namespace Tinsel
diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp
index 0011bb07bb9..07dd7cce398 100644
--- a/engines/tinsel/pcode.cpp
+++ b/engines/tinsel/pcode.cpp
@@ -41,6 +41,9 @@ namespace Tinsel {
 
 extern int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pic, RESUME_STATE *pResumeState);
 
+// in SCENE.CPP
+extern bool InDw1Intro();
+
 //----------------- LOCAL DEFINES --------------------
 
 #define	GLOBALS_FILENAME	"gdata"		// name of globals file
@@ -957,14 +960,20 @@ void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
 			break;
 
 		case OP_ESCON:
-			g_bNoPause = true;
-			ic->escOn = true;
-			ic->myEscape = GetEscEvents();
+			// Ignore EscapeOn during DW1 intro. We implement our own skipping.
+			if (!InDw1Intro()) {
+				g_bNoPause = true;
+				ic->escOn = true;
+				ic->myEscape = GetEscEvents();
+			}
 			break;
 
 		case OP_ESCOFF:
-			ic->escOn = false;
-			ic->myEscape = 0;
+			// Ignore EscapeOff during DW1 intro. We implement our own skipping.
+			if (!InDw1Intro()) {
+				ic->escOn = false;
+				ic->myEscape = 0;
+			}
 			break;
 
 		case OP_NOOP:
diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp
index 7ed00a73ea3..5c86442f222 100644
--- a/engines/tinsel/saveload.cpp
+++ b/engines/tinsel/saveload.cpp
@@ -67,7 +67,8 @@ extern void syncGlobInfo(Common::Serializer &s);
 // in POLYGONS.C
 extern void syncPolyInfo(Common::Serializer &s);
 
-extern int g_sceneCtr;
+// in SCENE.CPP
+extern void EndDw1Intro();
 
 extern bool g_ASceneIsSaved;
 
@@ -685,10 +686,9 @@ static void DoSave() {
 void ProcessSRQueue() {
 	switch (g_SRstate) {
 	case SR_DORESTORE:
-		// If a load has been done directly from title screens, set a larger value for scene ctr so the
-		// code used to skip the title screens in Discworld 1 gets properly disabled
-		if (g_sceneCtr < 10)
-			g_sceneCtr = 10;
+		// Clear the DW1 flag used for implementing introduction skipping.
+		// This restore may have come from the ScummVM GMM or launcher.
+		EndDw1Intro();
 
 		if (DoRestore()) {
 			DoRestoreScene(g_srsd, false);
diff --git a/engines/tinsel/scene.cpp b/engines/tinsel/scene.cpp
index a6f7ef7b390..7a2a1d73bf2 100644
--- a/engines/tinsel/scene.cpp
+++ b/engines/tinsel/scene.cpp
@@ -131,7 +131,7 @@ struct LIGHT_STRUC {
 static bool g_ShowPosition = false;	// Set when showpos() has been called
 #endif
 
-int g_sceneCtr = 0;
+static bool g_dw1Intro = true;
 static int g_initialMyEscape = 0;
 
 static SCNHANDLE g_SceneHandle = 0;	// Current scene handle - stored in case of Save_Scene()
@@ -146,8 +146,7 @@ struct TP_INIT {
 };
 
 void ResetVarsScene() {
-	g_sceneCtr = 0;
-	g_initialMyEscape = 0;
+	ResetDw1Intro();
 
 	g_SceneHandle = 0;
 	g_isViewSet = false;
@@ -235,16 +234,30 @@ static void SceneTinselProcess(CORO_PARAM, const void *param) {
 
 	CORO_BEGIN_CODE(_ctx);
 
-	// The following myEscape value setting is used for enabling title screen skipping in DW1
-	if ((TinselVersion == 1) && (g_sceneCtr == 1)) g_initialMyEscape = GetEscEvents();
-	// DW1 PSX, Saturn and Mac has its own scene skipping script code for scenes 2 and 3 (bug #6094).
-	_ctx->myEscape = ((TinselVersion == 1) && (g_sceneCtr < ((TinselV1PSX || TinselV1Saturn || TinselV1Mac) ? 2 : 4))) ? g_initialMyEscape : 0;
-
 	// get the stuff copied to process when it was created
 	_ctx->pInit = (const TP_INIT *)param;
 	assert(_ctx->pInit);
 	assert(_ctx->pInit->hTinselCode);		// Must have some code to run
 
+	// Handle DW1 introduction skipping
+	if (InDw1Intro()) {
+		// Record initial escape events so that Escape can be detected after a scene
+		if (g_initialMyEscape == 0) {
+			g_initialMyEscape = GetEscEvents();
+		}
+		SCNHANDLE sceneHandle = FROM_32(_ctx->pInit->hTinselCode) & HANDLEMASK;
+		if (sceneHandle == _vm->_handle->GetDw1TitleSceneHandle()) {
+			// Title screen reached; introduction has ended
+			EndDw1Intro();
+			_ctx->myEscape = 0;
+		} else {
+			// Enable Escape mode during introduction
+			_ctx->myEscape = GetEscEvents();
+		}
+	} else {
+		_ctx->myEscape = 0;
+	}
+
 	_ctx->pic = InitInterpretContext(GS_SCENE,
 		FROM_32(_ctx->pInit->hTinselCode),
 		(TinselVersion >= 2) ? _ctx->pInit->event : NOEVENT,
@@ -578,4 +591,61 @@ void SetView(int sceneId, int scale) {
 	// TODO: Update the ground plane
 }
 
+// DW1 Introduction Skipping
+//
+// The DW1 intro has many logos that cannot be skipped with the Escape key.
+// Later versions added limited ability to use Escape on some logos, but only 
+// after a long delay. Other versions added even more logos.
+// We allow users to press Escape during any of these logos to skip straight
+// to the title scene. On PSX this is the turtle scene, as they swapped orders.
+//
+// 1. The title scene handle is detected by name in Handle::SetupHandleTable()
+// 2. We assume we are in the DW1 intro until the title scene is reached, or
+//    until a game is restored from launcher or the debugger sets a scene.
+// 3. We manually enable Escape mode at the start of intro scenes by initializing
+//    ctx->myEscape to GetEscEvents() in SceneTinselProcess().
+// 4. We detect if Escape was pressed during a previous scene by comparing
+//    GetEscEvents() with g_initialMyEscape in SetNewScene().
+//    If Escape was pressed, we set the next scene to the title scene.
+// 5. We disable the escape opcodes (OP_ESCON, OP_ESCOFF) during the intro,
+//    so that the versions that added these calls do not conflict.
+
+/**
+ * Returns true if DW1 is in one of its introduction scenes.
+ * This allows implementing skipping the entire introduction with the Escape key
+ * by initializing ctx->myEscape and ignoring EscapeOn during introduction scenes.
+ */
+bool InDw1Intro() {
+	// Fail-safe: Disable introduction detection if we were
+	// unable to detect the DW1 title scene handle.
+	return (TinselVersion == 1) && g_dw1Intro &&
+		(_vm->_handle->GetDw1TitleSceneHandle() != 0);
+}
+
+/**
+ * Clears the DW1 introduction flag. Called when the title screen is started,
+ * a game is restored, or when loading a scene from the debugger.
+ */
+void EndDw1Intro() {
+	g_dw1Intro = false;
+}
+
+/**
+ * Resets the DW1 introduction state. Called when restarting the game or
+ * destroying the engine.
+ */
+void ResetDw1Intro() {
+	g_dw1Intro = true;
+	g_initialMyEscape = 0;
+}
+
+/**
+ * Returns true if Escape was pressed during the DW1 introduction.
+ * Used to override the scene passed by the script to NewScene()
+ * with the title scene instead.
+ */
+bool WasDw1IntroSkipped() {
+	return InDw1Intro() && g_initialMyEscape != 0 && g_initialMyEscape != GetEscEvents();
+}
+
 } // End of namespace Tinsel
diff --git a/engines/tinsel/scene.h b/engines/tinsel/scene.h
index 617dc2f94cc..01a5d5adbc3 100644
--- a/engines/tinsel/scene.h
+++ b/engines/tinsel/scene.h
@@ -90,6 +90,11 @@ void SendSceneTinselProcess(TINSEL_EVENT event);
 
 void SetView(int id, int scale);
 
+bool InDw1Intro();
+void EndDw1Intro();
+void ResetDw1Intro();
+bool WasDw1IntroSkipped();
+
 } // End of namespace Tinsel
 
 #endif	// TINSEL_SCENE_H
diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp
index 89ba5322647..62c721fd673 100644
--- a/engines/tinsel/tinlib.cpp
+++ b/engines/tinsel/tinlib.cpp
@@ -99,7 +99,7 @@ extern int NewestSavedGame();
 
 // in SCENE.CPP
 extern void setshowpos();
-extern int g_sceneCtr;
+extern void ResetDw1Intro();
 
 // in TINSEL.CPP
 extern void SetCdChangeScene(SCNHANDLE hScene);
@@ -1477,9 +1477,6 @@ void NewScene(CORO_PARAM, SCNHANDLE scene, int entrance, int transition) {
 	else
 		GetControl(CONTROL_STARTOFF);
 
-	if (TinselVersion == 1)
-		++g_sceneCtr;
-
 	// Prevent code subsequent to this call running before scene changes
 	if (CoroScheduler.getCurrentPID() != PID_MASTER_SCR)
 		CORO_KILL_SELF();
@@ -2505,7 +2502,11 @@ void FnRestartGame() {
 	StopSample();
 
 	g_bRestart = true;
-	g_sceneCtr = 0;
+	if (!TinselV1PSX) {
+		// Reset the DW1 intro detection unless PSX.
+		// PSX scripts restart directly to the title screen.
+		ResetDw1Intro();
+	}
 }
 
 /**
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index 0f757f1a125..1f80f366a6a 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -81,6 +81,7 @@ extern void InventoryProcess(CORO_PARAM, const void *);
 
 // In SCENE.CPP
 extern SCNHANDLE GetSceneHandle();
+extern bool WasDw1IntroSkipped();
 
 extern void ResetVarsDrives();
 extern void ResetVarsEvents();
@@ -528,6 +529,13 @@ void SetNewScene(SCNHANDLE scene, int entrance, int transition) {
 		g_HookScene.scene = 0;
 	}
 
+	// Skip DW1 introduction if Escape was pressed by switching to title screen
+	if (WasDw1IntroSkipped()) {
+		g_NextScene.scene = _vm->_handle->GetDw1TitleSceneHandle();
+		g_NextScene.entry = 1;
+		g_NextScene.trans = TRANS_DEF;
+	}
+
 	// Workaround for "Missing Red Dragon in square" bug in Discworld 1 PSX, act IV.
 	// This happens with the original interpreter on PSX too: the red dragon in Act IV
 	// doesn't show up inside the square at the right time. Original game required the




More information about the Scummvm-git-logs mailing list