[Scummvm-git-logs] scummvm master -> af770cafc43f92b256e4f4d802ad2e5574bce5cc

bluegr noreply at scummvm.org
Sat Aug 8 07:10:29 UTC 2026


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

Summary:
31a5144a79 NANCY: NANCY13: Handle special movie frames in more places
97df3c2e7a NANCY: Clean up and simplify changeScene() / changeFlag() usage
413413cea8 NANCY: NANCY14: Exit scene handling for HangmanPuzzle and PaintPuzzle
af770cafc4 NANCY: NANCY16: Add handling for CIF file / tree version 3


Commit: 31a5144a794c4346b191f9c9c4856465c3af825c
    https://github.com/scummvm/scummvm/commit/31a5144a794c4346b191f9c9c4856465c3af825c
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-08-08T10:10:12+03:00

Commit Message:
NANCY: NANCY13: Handle special movie frames in more places

Fixes showing outside window animations when around the train wagons

Changed paths:
    engines/nancy/action/secondarymovie.cpp


diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 3d907a4c677..7924a27ddfe 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -733,6 +733,7 @@ void PlaySecondaryMovie::execute() {
 		// gets triggered, and teleports the player to the wrong place instead of making them lose the game
 		if (!_decoder.isPlaying() && _isVisible && !_isFinished) {
 			_decoder.start();
+			resolveSentinelFrames();
 
 			if (_playDirection == kPlayMovieReverse) {
 				_decoder.setRate(-_decoder.getRate());


Commit: 97df3c2e7ada4b8eab4b1971a7eef285270566f0
    https://github.com/scummvm/scummvm/commit/97df3c2e7ada4b8eab4b1971a7eef285270566f0
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-08-08T10:10:16+03:00

Commit Message:
NANCY: Clean up and simplify changeScene() / changeFlag() usage

These functions already check for -1 / kNoScene, so we can remove these
checks when calling them

Changed paths:
    engines/nancy/action/puzzle/beadpuzzle.cpp
    engines/nancy/action/puzzle/blockspuzzle.cpp
    engines/nancy/action/puzzle/cuttingpuzzle.cpp
    engines/nancy/action/puzzle/dotconnectpuzzle.cpp
    engines/nancy/action/puzzle/drivingpuzzle.cpp
    engines/nancy/action/puzzle/dropsortpuzzle.cpp
    engines/nancy/action/puzzle/gridmappuzzle.cpp
    engines/nancy/action/puzzle/magnetmazepuzzle.cpp
    engines/nancy/action/puzzle/pachinkopuzzle.cpp
    engines/nancy/action/puzzle/pegspuzzle.cpp
    engines/nancy/action/puzzle/scalepuzzle.cpp
    engines/nancy/action/puzzle/sortpuzzle.cpp
    engines/nancy/action/puzzle/stepobjectspuzzle.cpp
    engines/nancy/action/puzzle/typingquizpuzzle.cpp
    engines/nancy/action/puzzle/wordfindpuzzle.cpp


diff --git a/engines/nancy/action/puzzle/beadpuzzle.cpp b/engines/nancy/action/puzzle/beadpuzzle.cpp
index ec6d860ff01..e5b8663c814 100644
--- a/engines/nancy/action/puzzle/beadpuzzle.cpp
+++ b/engines/nancy/action/puzzle/beadpuzzle.cpp
@@ -199,11 +199,7 @@ void BeadPuzzle::execute() {
 		g_nancy->_sound->stopSound(_partialSound);
 		g_nancy->_sound->stopSound(_wrongSound);
 		g_nancy->_sound->stopSound(_perfectSound);
-		{
-			const SceneChangeDescription &dest = (_subState == kExitToSolved) ? _solvedScene : _defaultScene;
-			if (dest.sceneID != kNoScene)
-				NancySceneState.changeScene(dest);
-		}
+		NancySceneState.changeScene(_subState == kExitToSolved ? _solvedScene : _defaultScene);
 		finishExecution();
 		break;
 	}
diff --git a/engines/nancy/action/puzzle/blockspuzzle.cpp b/engines/nancy/action/puzzle/blockspuzzle.cpp
index ea1888afd85..1137ccd0a02 100644
--- a/engines/nancy/action/puzzle/blockspuzzle.cpp
+++ b/engines/nancy/action/puzzle/blockspuzzle.cpp
@@ -371,15 +371,10 @@ void BlocksPuzzle::execute() {
 	case kActionTrigger:
 		if (_exitRequested) {
 			NancySceneState.setEventFlag(_exitFlag);
-			if (_exitScene.sceneID != kNoScene) {
-				NancySceneState.changeScene(_exitScene);
-			}
+			NancySceneState.changeScene(_exitScene);
 		} else {
-			if (_solveScene.sceneID != kNoScene) {
-				NancySceneState.changeScene(_solveScene);
-			}
-
 			NancySceneState.setEventFlag(_solveFlag);
+			NancySceneState.changeScene(_solveScene);
 		}
 
 		finishExecution();
diff --git a/engines/nancy/action/puzzle/cuttingpuzzle.cpp b/engines/nancy/action/puzzle/cuttingpuzzle.cpp
index f6442dddb90..efb199e70f7 100644
--- a/engines/nancy/action/puzzle/cuttingpuzzle.cpp
+++ b/engines/nancy/action/puzzle/cuttingpuzzle.cpp
@@ -324,8 +324,7 @@ void CuttingPuzzle::execute() {
 			}
 			if (anyGroove)
 				NancySceneState.setEventFlag(_cancelScene._flag);
-			if (_cancelScene._sceneChange.sceneID != kNoScene)
-				NancySceneState.changeScene(_cancelScene._sceneChange);
+			NancySceneState.changeScene(_cancelScene._sceneChange);
 		} else if (_solved) {
 			_puzzleSolvedScene.execute();
 		} else if (_gogglesMissing) {
diff --git a/engines/nancy/action/puzzle/dotconnectpuzzle.cpp b/engines/nancy/action/puzzle/dotconnectpuzzle.cpp
index a3194f6db63..9cda9c2a8bd 100644
--- a/engines/nancy/action/puzzle/dotconnectpuzzle.cpp
+++ b/engines/nancy/action/puzzle/dotconnectpuzzle.cpp
@@ -151,15 +151,11 @@ void DotConnectPuzzle::execute() {
 		g_nancy->_sound->stopSound(_allCoveredSound);
 		g_nancy->_sound->stopSound(_winSound);
 		if (_subState == kExitToWin) {
-			if (_winFlag.label != -1)
-				NancySceneState.setEventFlag(_winFlag);
-			if (_winScene.sceneID != kNoScene)
-				NancySceneState.changeScene(_winScene);
+			NancySceneState.setEventFlag(_winFlag);
+			NancySceneState.changeScene(_winScene);
 		} else {
-			if (_exitFlag.label != -1)
-				NancySceneState.setEventFlag(_exitFlag);
-			if (_exitScene.sceneID != kNoScene)
-				NancySceneState.changeScene(_exitScene);
+			NancySceneState.setEventFlag(_exitFlag);
+			NancySceneState.changeScene(_exitScene);
 		}
 		finishExecution();
 		break;
diff --git a/engines/nancy/action/puzzle/drivingpuzzle.cpp b/engines/nancy/action/puzzle/drivingpuzzle.cpp
index ee20833dc15..1384d880758 100644
--- a/engines/nancy/action/puzzle/drivingpuzzle.cpp
+++ b/engines/nancy/action/puzzle/drivingpuzzle.cpp
@@ -720,15 +720,10 @@ void DrivingPuzzle::execute() {
 		break;
 	case kActionTrigger:
 		g_nancy->_sound->stopSound(_soundBlocks[2].channel);	// stop the engine ambience
-		if (_exitFlag != -1) {
-			NancySceneState.setEventFlag(_exitFlag, _exitFlagValue ? g_nancy->_true : g_nancy->_false);
-		}
-		if (_exitHasFade) {
+		NancySceneState.setEventFlag(_exitFlag, _exitFlagValue ? g_nancy->_true : g_nancy->_false);
+		if (_exitHasFade)
 			NancySceneState.specialEffect(_exitFadeType, _exitFadeTotalTime, _exitFadeToBlackTime, _exitFadeRect);
-		}
-		if (_exitScene.sceneID != kNoScene) {
-			NancySceneState.changeScene(_exitScene);
-		}
+		NancySceneState.changeScene(_exitScene);
 		finishExecution();
 		break;
 	}
@@ -765,11 +760,9 @@ void DrivingPuzzle::handleInput(NancyInput &input) {
 	if (_flatTirePending) {
 		if (!g_nancy->_sound->isSoundPlaying(_soundBlocks[0].channel)) {
 			saveState();
-			if (_finishScene != kNoScene) {
-				SceneChangeDescription scene;
-				scene.sceneID = _finishScene;
-				NancySceneState.changeScene(scene);
-			}
+			SceneChangeDescription scene;
+			scene.sceneID = _finishScene;
+			NancySceneState.changeScene(scene);
 			finishExecution();
 		}
 		return;
diff --git a/engines/nancy/action/puzzle/dropsortpuzzle.cpp b/engines/nancy/action/puzzle/dropsortpuzzle.cpp
index 27fe80d370b..11f17f5a817 100644
--- a/engines/nancy/action/puzzle/dropsortpuzzle.cpp
+++ b/engines/nancy/action/puzzle/dropsortpuzzle.cpp
@@ -418,19 +418,13 @@ void DropSortPuzzle::execute() {
 	case kActionTrigger:
 		if (_exitRequested) {
 			NancySceneState.setEventFlag(_exitFlag);
-			if (_exitScene.sceneID != kNoScene) {
-				NancySceneState.changeScene(_exitScene);
-			}
+			NancySceneState.changeScene(_exitScene);
 		} else if (_solved) {
 			NancySceneState.setEventFlag(_winFlag);
-			if (_winScene.sceneID != kNoScene) {
-				NancySceneState.changeScene(_winScene);
-			}
+			NancySceneState.changeScene(_winScene);
 		} else {
 			NancySceneState.setEventFlag(_loseFlag);
-			if (_loseScene.sceneID != kNoScene) {
-				NancySceneState.changeScene(_loseScene);
-			}
+			NancySceneState.changeScene(_loseScene);
 		}
 
 		finishExecution();
diff --git a/engines/nancy/action/puzzle/gridmappuzzle.cpp b/engines/nancy/action/puzzle/gridmappuzzle.cpp
index 3b89655466d..bfdf8a71780 100644
--- a/engines/nancy/action/puzzle/gridmappuzzle.cpp
+++ b/engines/nancy/action/puzzle/gridmappuzzle.cpp
@@ -240,15 +240,11 @@ void GridMapPuzzle::execute() {
 			GridMapPuzzleData *gmd = (GridMapPuzzleData *)NancySceneState.getPuzzleData(GridMapPuzzleData::getTag());
 			if (gmd)
 				gmd->itemState.clear();
-			if (_winFlag.label != -1)
-				NancySceneState.setEventFlag(_winFlag);
-			if (_winScene.sceneID != kNoScene)
-				NancySceneState.changeScene(_winScene);
+			NancySceneState.setEventFlag(_winFlag);
+			NancySceneState.changeScene(_winScene);
 		} else {
-			if (_cancelFlag.label != -1)
-				NancySceneState.setEventFlag(_cancelFlag);
-			if (_cancelScene.sceneID != kNoScene)
-				NancySceneState.changeScene(_cancelScene);
+			NancySceneState.setEventFlag(_cancelFlag);
+			NancySceneState.changeScene(_cancelScene);
 		}
 		finishExecution();
 		break;
diff --git a/engines/nancy/action/puzzle/magnetmazepuzzle.cpp b/engines/nancy/action/puzzle/magnetmazepuzzle.cpp
index 50c623adf51..3b541aecc47 100644
--- a/engines/nancy/action/puzzle/magnetmazepuzzle.cpp
+++ b/engines/nancy/action/puzzle/magnetmazepuzzle.cpp
@@ -189,15 +189,11 @@ void MagnetMazePuzzle::execute() {
 			MagnetMazePuzzleData *mmd = (MagnetMazePuzzleData *)NancySceneState.getPuzzleData(MagnetMazePuzzleData::getTag());
 			if (mmd)
 				mmd->magnetState.clear();
-			if (_winFlag.label != -1)
-				NancySceneState.setEventFlag(_winFlag);
-			if (_winScene.sceneID != kNoScene)
-				NancySceneState.changeScene(_winScene);
+			NancySceneState.setEventFlag(_winFlag);
+			NancySceneState.changeScene(_winScene);
 		} else {
-			if (_cancelFlag.label != -1)
-				NancySceneState.setEventFlag(_cancelFlag);
-			if (_cancelScene.sceneID != kNoScene)
-				NancySceneState.changeScene(_cancelScene);
+			NancySceneState.setEventFlag(_cancelFlag);
+			NancySceneState.changeScene(_cancelScene);
 		}
 		finishExecution();
 		break;
diff --git a/engines/nancy/action/puzzle/pachinkopuzzle.cpp b/engines/nancy/action/puzzle/pachinkopuzzle.cpp
index c6ec92b8616..422b4333ba3 100644
--- a/engines/nancy/action/puzzle/pachinkopuzzle.cpp
+++ b/engines/nancy/action/puzzle/pachinkopuzzle.cpp
@@ -599,9 +599,7 @@ void PachinkoPuzzle::execute() {
 		// The give-up hotspot and the completion path both route to the exit scene; the
 		// win/lose branch is driven downstream by the solved flag and the puzzle event flag.
 		NancySceneState.setEventFlag(_exitFlag);
-		if (_exitScene.sceneID != kNoScene) {
-			NancySceneState.changeScene(_exitScene);
-		}
+		NancySceneState.changeScene(_exitScene);
 		finishExecution();
 		break;
 	}
diff --git a/engines/nancy/action/puzzle/pegspuzzle.cpp b/engines/nancy/action/puzzle/pegspuzzle.cpp
index 6c765590df9..98e86c67e7e 100644
--- a/engines/nancy/action/puzzle/pegspuzzle.cpp
+++ b/engines/nancy/action/puzzle/pegspuzzle.cpp
@@ -350,14 +350,9 @@ void PegsPuzzle::execute() {
 	case kActionTrigger: {
 		if (_exitRequested) {
 			NancySceneState.setEventFlag(_exitFlag);
-			if (_exitScene.sceneID != kNoScene) {
-				NancySceneState.changeScene(_exitScene);
-			}
+			NancySceneState.changeScene(_exitScene);
 		} else {
-			const SceneChangeDescription &sc = _solved ? _winScene : _loseScene;
-			if (sc.sceneID != kNoScene) {
-				NancySceneState.changeScene(sc);
-			}
+			NancySceneState.changeScene(_solved ? _winScene : _loseScene);
 		}
 
 		finishExecution();
diff --git a/engines/nancy/action/puzzle/scalepuzzle.cpp b/engines/nancy/action/puzzle/scalepuzzle.cpp
index d7c08e7a723..26ee614f320 100644
--- a/engines/nancy/action/puzzle/scalepuzzle.cpp
+++ b/engines/nancy/action/puzzle/scalepuzzle.cpp
@@ -379,18 +379,13 @@ void ScalePuzzle::execute() {
 	case kActionTrigger:
 		if (_exitRequested) {
 			NancySceneState.setEventFlag(_exitFlag);
-			if (_exitScene.sceneID != kNoScene) {
-				NancySceneState.changeScene(_exitScene);
-			}
+			NancySceneState.changeScene(_exitScene);
 		} else {
 			// Solved: play the latch sound, set the solve flag, change scene (9999 = stay).
 			playSoundBlock(_latchSound);
 			NancySceneState.setEventFlag(_solveFlag);
-			if (_solveScene.sceneID != kNoScene) {
-				NancySceneState.changeScene(_solveScene);
-			}
+			NancySceneState.changeScene(_solveScene);
 		}
-
 		finishExecution();
 		break;
 	}
diff --git a/engines/nancy/action/puzzle/sortpuzzle.cpp b/engines/nancy/action/puzzle/sortpuzzle.cpp
index ee8e98724bb..fee922edc11 100644
--- a/engines/nancy/action/puzzle/sortpuzzle.cpp
+++ b/engines/nancy/action/puzzle/sortpuzzle.cpp
@@ -361,15 +361,11 @@ void SortPuzzle::execute() {
 				spd->currentState.clear();
 				spd->solvedState.clear();
 			}
-			if (_winFlag.label != -1)
-				NancySceneState.setEventFlag(_winFlag);
-			if (_winScene.sceneID != kNoScene)
-				NancySceneState.changeScene(_winScene);
+			NancySceneState.setEventFlag(_winFlag);
+			NancySceneState.changeScene(_winScene);
 		} else {
-			if (_cancelFlag.label != -1)
-				NancySceneState.setEventFlag(_cancelFlag);
-			if (_cancelScene.sceneID != kNoScene)
-				NancySceneState.changeScene(_cancelScene);
+			NancySceneState.setEventFlag(_cancelFlag);
+			NancySceneState.changeScene(_cancelScene);
 		}
 		finishExecution();
 		break;
diff --git a/engines/nancy/action/puzzle/stepobjectspuzzle.cpp b/engines/nancy/action/puzzle/stepobjectspuzzle.cpp
index 3baa8dc1344..31bd29f7572 100644
--- a/engines/nancy/action/puzzle/stepobjectspuzzle.cpp
+++ b/engines/nancy/action/puzzle/stepobjectspuzzle.cpp
@@ -395,15 +395,10 @@ void StepObjectsPuzzle::execute() {
 	case kActionTrigger:
 		if (_solved) {
 			NancySceneState.setEventFlag(_solveFlag);
-
-			if (_solveScene.sceneID != kNoScene) {
-				NancySceneState.changeScene(_solveScene);
-			}
+			NancySceneState.changeScene(_solveScene);
 		} else {
 			NancySceneState.setEventFlag(_exitFlag);
-			if (_exitScene.sceneID != kNoScene) {
-				NancySceneState.changeScene(_exitScene);
-			}
+			NancySceneState.changeScene(_exitScene);
 		}
 
 		finishExecution();
diff --git a/engines/nancy/action/puzzle/typingquizpuzzle.cpp b/engines/nancy/action/puzzle/typingquizpuzzle.cpp
index a825d1722ab..3ca4f701dc5 100644
--- a/engines/nancy/action/puzzle/typingquizpuzzle.cpp
+++ b/engines/nancy/action/puzzle/typingquizpuzzle.cpp
@@ -380,16 +380,14 @@ void TypingQuizPuzzle::redraw() {
 
 void TypingQuizPuzzle::triggerSceneChange() {
 	if (_reachedTarget) {
-		if (_winScene.sceneID != kNoScene)
-			NancySceneState.changeScene(_winScene);
-		if (_winFlag != -1)
-			NancySceneState.setEventFlag(_winFlag, g_nancy->_true);
+		NancySceneState.setEventFlag(_winFlag, g_nancy->_true);
+		NancySceneState.changeScene(_winScene);
 	} else {
-		NancySceneState.changeScene(_defaultScene);
 		if (_reachedThreshold && _flagThreshold != -1)
 			NancySceneState.setEventFlag(_flagThreshold, g_nancy->_true);
 		else if (_flagFail != -1)
 			NancySceneState.setEventFlag(_flagFail, g_nancy->_true);
+		NancySceneState.changeScene(_defaultScene);
 	}
 }
 
diff --git a/engines/nancy/action/puzzle/wordfindpuzzle.cpp b/engines/nancy/action/puzzle/wordfindpuzzle.cpp
index fa765576b4b..ed4e9246f62 100644
--- a/engines/nancy/action/puzzle/wordfindpuzzle.cpp
+++ b/engines/nancy/action/puzzle/wordfindpuzzle.cpp
@@ -326,10 +326,7 @@ void WordFindPuzzle::execute() {
 		if (!_allFound) {
 			NancySceneState.setEventFlag(_exitFlag);
 		}
-		const SceneChangeDescription &sc = _allFound ? _solveScene : _exitScene;
-		if (sc.sceneID != kNoScene) {
-			NancySceneState.changeScene(sc);
-		}
+		NancySceneState.changeScene(_allFound ? _solveScene : _exitScene);
 
 		finishExecution();
 		break;


Commit: 413413cea8d962e15e9e24f7586b861bc208b96f
    https://github.com/scummvm/scummvm/commit/413413cea8d962e15e9e24f7586b861bc208b96f
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-08-08T10:10:18+03:00

Commit Message:
NANCY: NANCY14: Exit scene handling for HangmanPuzzle and PaintPuzzle

Changed paths:
    engines/nancy/action/puzzle/hangmanpuzzle.cpp
    engines/nancy/action/puzzle/hangmanpuzzle.h
    engines/nancy/action/puzzle/paintpuzzle.cpp
    engines/nancy/action/puzzle/paintpuzzle.h


diff --git a/engines/nancy/action/puzzle/hangmanpuzzle.cpp b/engines/nancy/action/puzzle/hangmanpuzzle.cpp
index b64a1382ea7..1bec805cf24 100644
--- a/engines/nancy/action/puzzle/hangmanpuzzle.cpp
+++ b/engines/nancy/action/puzzle/hangmanpuzzle.cpp
@@ -86,16 +86,27 @@ void HangmanPuzzle::readData(Common::SeekableReadStream &stream) {
 		outcome->sound.readData(stream);
 	}
 
-	// Trailing block read by the base record reader (25 bytes).
-	_tailField0 = stream.readSint32LE();
-	_tailField1 = stream.readSint16LE();
-	_tailVector[0] = stream.readSint32LE();
-	_tailVector[1] = stream.readSint32LE();
-	_tailVector[2] = stream.readSint32LE();
-	_tailField2 = stream.readSint16LE();
-	_tailSceneID = stream.readSint16LE();
-	_tailFlag = stream.readSint16LE();
-	_tailByte = stream.readByte();
+	// Trailing count-prefixed array of 23-byte give-up hotspots
+	// {Rect, uint16 cursorType, uint16 sceneID, int16 flagLabel, byte flagValue}.
+	// The exit always jumps to the scene's first frame.
+	int16 numExitZones = stream.readSint16LE();
+	for (int16 i = 0; i < numExitZones; ++i) {
+		Common::Rect r;
+		readRect(stream, r);
+		uint16 cursorType = stream.readUint16LE();
+		uint16 sceneID = stream.readUint16LE();
+		int16 flagLabel = stream.readSint16LE();
+		byte flagValue = stream.readByte();
+
+		if (i == 0) {
+			_exitHotspot = r;
+			_exitCursorType = cursorType;
+			_exitScene.sceneID = sceneID;
+			_exitScene.frameID = 0;
+			_exitFlag.label = flagLabel;
+			_exitFlag.flag = flagValue;
+		}
+	}
 }
 
 HangmanData *HangmanPuzzle::getPuzzleData() const {
@@ -298,6 +309,17 @@ void HangmanPuzzle::handleInput(NancyInput &input) {
 		return;
 	}
 
+	// Give-up hotspot: leave the puzzle.
+	if (!_exitHotspot.isEmpty() &&
+			NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+		g_nancy->_cursor->setCursorType((CursorManager::CursorType)_exitCursorType, true);
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			_exitRequested = true;
+		}
+		input.eatMouseInput();
+		return;
+	}
+
 	int tile = tileAtCursor(input.mousePos);
 	if (tile >= 0 && !_letters[tile].used) {
 		// Clickable-hotspot cursor for puzzles (the blue pointing hand).
@@ -317,6 +339,11 @@ void HangmanPuzzle::execute() {
 		_state = kRun;
 		break;
 	case kRun:
+		if (_exitRequested) {
+			NancySceneState.setEventFlag(_exitFlag);
+			NancySceneState.changeScene(_exitScene);
+			break;
+		}
 		if ((_solved || _lost) && !_outcomeApplied) {
 			_outcomeApplied = true;
 			applyOutcome(_solved ? _winScene : _loseScene);
diff --git a/engines/nancy/action/puzzle/hangmanpuzzle.h b/engines/nancy/action/puzzle/hangmanpuzzle.h
index f167d67545d..3d8992cdbd1 100644
--- a/engines/nancy/action/puzzle/hangmanpuzzle.h
+++ b/engines/nancy/action/puzzle/hangmanpuzzle.h
@@ -107,15 +107,11 @@ protected:
 	SceneOutcome _winScene2;	// 0x2f1
 	SceneOutcome _loseScene;	// 0x352
 
-	// Trailing block read by the base record reader (vtable+0x24): a fixed
-	// 25-byte {int32, int16, 3x int32 vector, 2x int16, int16, byte} structure.
-	int32 _tailField0 = 0;
-	int16 _tailField1 = 0;
-	int32 _tailVector[3] = { 0, 0, 0 };
-	int16 _tailField2 = 0;
-	int16 _tailSceneID = 0;
-	int16 _tailFlag = 0;
-	byte _tailByte = 0;
+	// Give-up hotspot (count-prefixed 23-byte trailer): click to leave the puzzle.
+	Common::Rect _exitHotspot;
+	uint16 _exitCursorType = 0;
+	SceneChangeDescription _exitScene;
+	FlagDescription _exitFlag;
 
 	// -- Runtime state --
 	Graphics::ManagedSurface _puzzleImage;
@@ -129,6 +125,7 @@ protected:
 	bool _solved = false;
 	bool _lost = false;
 	bool _outcomeApplied = false;
+	bool _exitRequested = false;
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/puzzle/paintpuzzle.cpp b/engines/nancy/action/puzzle/paintpuzzle.cpp
index 341df841eb5..583f7b3b0a4 100644
--- a/engines/nancy/action/puzzle/paintpuzzle.cpp
+++ b/engines/nancy/action/puzzle/paintpuzzle.cpp
@@ -71,16 +71,27 @@ void PaintPuzzle::readData(Common::SeekableReadStream &stream) {
 
 	_sounds[2].readData(stream);
 
-	// Trailing block read by the base record reader (25 bytes).
-	_tailField0 = stream.readSint32LE();
-	_tailField1 = stream.readSint16LE();
-	_tailVector[0] = stream.readSint32LE();
-	_tailVector[1] = stream.readSint32LE();
-	_tailVector[2] = stream.readSint32LE();
-	_tailField2 = stream.readSint16LE();
-	_tailSceneID = stream.readSint16LE();
-	_tailFlag = stream.readSint16LE();
-	_tailByte = stream.readByte();
+	// Trailing count-prefixed array of 23-byte give-up hotspots
+	// {Rect, uint16 cursorType, uint16 sceneID, int16 flagLabel, byte flagValue}.
+	// The exit always jumps to the scene's first frame.
+	int16 numExitZones = stream.readSint16LE();
+	for (int16 i = 0; i < numExitZones; ++i) {
+		Common::Rect r;
+		readRect(stream, r);
+		uint16 cursorType = stream.readUint16LE();
+		uint16 sceneID = stream.readUint16LE();
+		int16 flagLabel = stream.readSint16LE();
+		byte flagValue = stream.readByte();
+
+		if (i == 0) {
+			_exitHotspot = r;
+			_exitCursorType = cursorType;
+			_exitScene.sceneID = sceneID;
+			_exitScene.frameID = 0;
+			_exitFlag.label = flagLabel;
+			_exitFlag.flag = flagValue;
+		}
+	}
 }
 
 void PaintPuzzle::init() {
@@ -232,6 +243,17 @@ void PaintPuzzle::handleInput(NancyInput &input) {
 		return;
 	}
 
+	// Give-up hotspot: leave the puzzle.
+	if (!_exitHotspot.isEmpty() &&
+			NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+		g_nancy->_cursor->setCursorType((CursorManager::CursorType)_exitCursorType, true);
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			_exitRequested = true;
+		}
+		input.eatMouseInput();
+		return;
+	}
+
 	int color = colorSwatchAtCursor(input.mousePos);
 	if (color >= 0) {
 		// Over a color swatch: show the blue puzzle-hotspot hand and pick the
@@ -263,6 +285,11 @@ void PaintPuzzle::execute() {
 		_state = kRun;
 		break;
 	case kRun:
+		if (_exitRequested) {
+			NancySceneState.setEventFlag(_exitFlag);
+			NancySceneState.changeScene(_exitScene);
+			break;
+		}
 		if (_solved && !_outcomeApplied) {
 			_outcomeApplied = true;
 			applyOutcome(_outcome);
diff --git a/engines/nancy/action/puzzle/paintpuzzle.h b/engines/nancy/action/puzzle/paintpuzzle.h
index 713f13dc1c8..6a272db9464 100644
--- a/engines/nancy/action/puzzle/paintpuzzle.h
+++ b/engines/nancy/action/puzzle/paintpuzzle.h
@@ -97,14 +97,11 @@ protected:
 	int16 _field1a6 = 0;		// 0x1a6
 	SceneOutcome _outcome;		// 0x1a8
 
-	// Trailing block read by the base record reader (vtable+0x24), 25 bytes.
-	int32 _tailField0 = 0;
-	int16 _tailField1 = 0;
-	int32 _tailVector[3] = { 0, 0, 0 };
-	int16 _tailField2 = 0;
-	int16 _tailSceneID = 0;
-	int16 _tailFlag = 0;
-	byte _tailByte = 0;
+	// Give-up hotspot (count-prefixed 23-byte trailer): click to leave the puzzle.
+	Common::Rect _exitHotspot;
+	uint16 _exitCursorType = 0;
+	SceneChangeDescription _exitScene;
+	FlagDescription _exitFlag;
 
 	// -- Runtime state --
 	Graphics::ManagedSurface _image;
@@ -114,6 +111,7 @@ protected:
 	int _hoverColor = -1;
 	bool _solved = false;
 	bool _outcomeApplied = false;
+	bool _exitRequested = false;
 };
 
 } // End of namespace Action


Commit: af770cafc43f92b256e4f4d802ad2e5574bce5cc
    https://github.com/scummvm/scummvm/commit/af770cafc43f92b256e4f4d802ad2e5574bce5cc
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-08-08T10:10:20+03:00

Commit Message:
NANCY: NANCY16: Add handling for CIF file / tree version 3

Turns out there wasn't any actual change with this version, so it's
already supported

Changed paths:
    engines/nancy/cif.cpp


diff --git a/engines/nancy/cif.cpp b/engines/nancy/cif.cpp
index 7ee50847bbf..76c0aa48e66 100644
--- a/engines/nancy/cif.cpp
+++ b/engines/nancy/cif.cpp
@@ -97,6 +97,22 @@ enum {
 	kHashMapSize = 1024
 };
 
+// The version number stored inside CifFile and CifTree headers. Nancy12 bumped it
+// to 2, and Nancy16 to 3, without changing the layout of either structure
+static uint16 getCifVersion() {
+	GameType gameType = g_nancy->getGameType();
+
+	if (gameType <= kGameTypeNancy1) {
+		return 0;
+	} else if (gameType <= kGameTypeNancy11) {
+		return 1;
+	} else if (gameType <= kGameTypeNancy15) {
+		return 2;
+	}
+
+	return 3;
+}
+
 CifFile::CifFile(Common::SeekableReadStream *stream, const Common::Path &name) {
 	assert(stream);
 	_stream = stream;
@@ -169,10 +185,10 @@ bool CifFile::sync(Common::Serializer &ser) {
 	uint16 hi = 2;
 	ser.syncAsUint16LE(hi);
 
-	uint32 ver = (g_nancy->getGameType() <= kGameTypeNancy1) ? 0 : 1;
+	uint32 ver = getCifVersion();
 	ser.syncAsUint16LE(ver);
 
-	if (ver != 0 && ver != 1 && ver != 2) {
+	if (ver > 3) {
 		warning("Unsupported version %d found in CifFile '%s'", ver, _info.name.toString().c_str());
 		return false;
 	}
@@ -308,11 +324,10 @@ bool CifTree::sync(Common::Serializer &ser) {
 	uint16 hi = 2;
 	ser.syncAsUint16LE(hi);
 
-	uint32 ver = (g_nancy->getGameType() <= kGameTypeNancy1) ? 0 : 1;
+	uint32 ver = getCifVersion();
 	ser.syncAsUint16LE(ver);
 
-	// TODO: Nancy16 introduced version 3
-	if (ver != 0 && ver != 1 && ver != 2) {
+	if (ver > 3) {
 		warning("Unsupported version %d found in CifTree '%s'", ver, _name.toString().c_str());
 		return false;
 	}




More information about the Scummvm-git-logs mailing list