[Scummvm-git-logs] scummvm master -> 1a1c8cc7fcc10aa0c2fdac2499384c5c7bef0cfc

bluegr noreply at scummvm.org
Sun Jul 5 00:43:02 UTC 2026


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

Summary:
26d3659d47 NANCY: NANCY10: Keep changes to Autotext isolated
3707bfa39d NANCY: NANCY11: Implement PC turn in BulPuzzle
945e42ee83 NANCY: NANCY11: Implement changes to MultiBuildPuzzle
d66b2a1086 NANCY: NANCY111: Fix count requirement for MultiBuildPuzzle
1a1c8cc7fc NANCY: NANCY10: Fix reading scene from CellPhonePopCellSceneFromStack


Commit: 26d3659d470ac1fa58839f02c6dee2bab71c3835
    https://github.com/scummvm/scummvm/commit/26d3659d470ac1fa58839f02c6dee2bab71c3835
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-05T03:42:50+03:00

Commit Message:
NANCY: NANCY10: Keep changes to Autotext isolated

This ensures that earlier games won't be broken by this change

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


diff --git a/engines/nancy/action/autotext.cpp b/engines/nancy/action/autotext.cpp
index 2d3a393540f..ee0b31466e7 100644
--- a/engines/nancy/action/autotext.cpp
+++ b/engines/nancy/action/autotext.cpp
@@ -195,17 +195,21 @@ void Autotext::execute() {
 
 		const Font *font = g_nancy->_graphics->getFont(_fontID);
 		assert(font);
+		uint d = (font->getFontHeight() + 1) / 2 + 1;
 
-		// The original indents the text within the surface by twice the width
-		// of a lowercase 'o' on the left, and once on top
-		uint indent = font->getStringWidth("o");
-		textBounds.top += indent;
-		textBounds.left += indent * 2;
+		textBounds.top += d + 1;
+		textBounds.left += d;
 
 		// Original engine uses a particular value in the TBOX chunk in all
 		// text rendering, including Autotext
 		auto *tbox = GetEngineData(TBOX);
 
+		if (g_nancy->getGameType() >= kGameTypeNancy10 && _surfaceID < 3) {
+			// Nancy 10+ viewport autotext aligns the first line flush with the
+			// wrapped lines instead of hanging-indenting it
+			textBounds.left = tbox->leftOffset;
+		}
+
 		drawAllText(textBounds, tbox->leftOffset - textBounds.left, _fontID, _fontID);
 		surfBounds = Common::Rect(_fullSurface.w, _drawnTextHeight);
 


Commit: 3707bfa39d365179a94fa132bceff11ed3329a45
    https://github.com/scummvm/scummvm/commit/3707bfa39d365179a94fa132bceff11ed3329a45
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-05T03:42:51+03:00

Commit Message:
NANCY: NANCY11: Implement PC turn in BulPuzzle

Fixes playing the Mayan puzzle with Jane

Changed paths:
    engines/nancy/action/puzzle/bulpuzzle.cpp
    engines/nancy/action/puzzle/bulpuzzle.h


diff --git a/engines/nancy/action/puzzle/bulpuzzle.cpp b/engines/nancy/action/puzzle/bulpuzzle.cpp
index 49f0ab17fa2..ca5397d31e5 100644
--- a/engines/nancy/action/puzzle/bulpuzzle.cpp
+++ b/engines/nancy/action/puzzle/bulpuzzle.cpp
@@ -57,6 +57,8 @@ void BulPuzzle::init() {
 }
 
 void BulPuzzle::updateGraphics() {
+	doAiTurn();
+
 	bool isPlayer = _turn / _numRolls == 0;
 
 	if (_currentAction == kCapture && g_nancy->getTotalPlayTime() > _nextMoveTime) {
@@ -223,7 +225,9 @@ void BulPuzzle::readData(Common::SeekableReadStream &stream) {
 	_numRolls = stream.readUint16LE();
 
 	if (g_nancy->getGameType() >= kGameTypeNancy11) {
-		stream.skip(2); // game mode + a flag byte
+		// A game mode (0 = play against the computer) and the computer's pass strategy
+		_playAgainstComputer = stream.readByte() == 0;
+		_aiPassStrategy = stream.readByte() != 0;
 	}
 
 	_playerStart = stream.readUint16LE();
@@ -294,19 +298,21 @@ void BulPuzzle::readData(Common::SeekableReadStream &stream) {
 			}
 		}
 
-		// Loss scene (two possible flags) then the solve scene (one flag). The event flags
-		// store a value rather than a simple on/off.
-		_exitScene._sceneChange.readData(stream);
-		_exitScene._sceneChange.continueSceneSound = stream.readUint16LE();
-		_exitScene._flag.label = stream.readSint16LE();
-		_exitScene._flag.flag = stream.readByte();
-		_loseFlag2.label = stream.readSint16LE();
-		_loseFlag2.flag = stream.readByte();
-
+		// Winning and losing use the same scene, told apart by which flag is set: the first flag
+		// is the win flag, the second the loss flag. The event flags store a value, not on/off.
 		_solveScene._sceneChange.readData(stream);
 		_solveScene._sceneChange.continueSceneSound = stream.readUint16LE();
+		_exitScene._sceneChange = _solveScene._sceneChange; // losing reuses the winning scene
 		_solveScene._flag.label = stream.readSint16LE();
 		_solveScene._flag.flag = stream.readByte();
+		_exitScene._flag.label = stream.readSint16LE();
+		_exitScene._flag.flag = stream.readByte();
+
+		// Giving up via the exit hotspot changes to its own scene
+		_giveUpScene._sceneChange.readData(stream);
+		_giveUpScene._sceneChange.continueSceneSound = stream.readUint16LE();
+		_giveUpScene._flag.label = stream.readSint16LE();
+		_giveUpScene._flag.flag = stream.readByte();
 	} else {
 		_moveSound.readNormal(stream);
 		_enemyCapturedSound.readNormal(stream);
@@ -422,19 +428,17 @@ void BulPuzzle::execute() {
 	case kActionTrigger: {
 		SoundDescription &sound = _playerWon ? _solveSound : _loseSound;
 
-		if (g_nancy->getTotalPlayTime() >= _nextMoveTime) {
+		if (_nextMoveTime != 0 && g_nancy->getTotalPlayTime() >= _nextMoveTime) {
 			_nextMoveTime = 0;
 			g_nancy->_sound->loadSound(sound);
 			g_nancy->_sound->playSound(sound);
 		}
 
 		if (_nextMoveTime == 0 && !g_nancy->_sound->isSoundPlaying(sound)) {
-			if (_playerWon) {
+			if (g_nancy->getGameType() >= kGameTypeNancy11 && _gaveUp) {
+				_giveUpScene.execute();
+			} else if (_playerWon) {
 				_solveScene.execute();
-			} else if (g_nancy->getGameType() >= kGameTypeNancy11) {
-				// The second flag marks giving up via the exit hotspot rather than being beaten
-				NancySceneState.changeScene(_exitScene._sceneChange);
-				NancySceneState.setEventFlag(_gaveUp ? _loseFlag2 : _exitScene._flag);
 			} else {
 				_exitScene.execute();
 			}
@@ -445,6 +449,47 @@ void BulPuzzle::execute() {
 	}
 }
 
+void BulPuzzle::doAiTurn() {
+	if (!_playAgainstComputer || _turn / _numRolls == 0) {
+		// Not the computer opponent's turn
+		return;
+	}
+
+	// Wait until the board is idle before the computer acts. Note _nextMoveTime is deliberately
+	// not checked: it keeps a stale value after a move completes, so the same conditions the
+	// player's canClick uses (no action in flight, move sound finished) apply here too.
+	if (_currentAction != kNone || _pushedButton || _changeLight ||
+		g_nancy->_sound->isSoundPlaying(_moveSound)) {
+		return;
+	}
+
+	// The computer always rolls on its first turn; on its second it may pass
+	bool pass = false;
+	if (_turn % _numRolls) {
+		if (_aiPassStrategy) {
+			int diff = _enemyPos - _playerPos;
+			pass = diff >= 6 || (diff < 0 && diff + _numCells > 5);
+		} else {
+			pass = g_nancy->_randomSource->getRandomBit();
+		}
+	}
+
+	// Push the button on the computer's behalf, mirroring a player click
+	if (pass) {
+		_drawSurface.blitFrom(_image, _passButtonSrc, _passButtonDest);
+		g_nancy->_sound->playSound(_passSound);
+		_currentAction = kPass;
+	} else {
+		_drawSurface.blitFrom(_image, _rollButtonSrc, _rollButtonDest);
+		g_nancy->_sound->playSound(_rollSound);
+		_currentAction = kRoll;
+	}
+
+	_needsRedraw = true;
+	_pushedButton = true;
+	_nextMoveTime = g_nancy->getTotalPlayTime() + 250;
+}
+
 void BulPuzzle::handleInput(NancyInput &input) {
 	if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
 		g_nancy->_cursor->setCursorType(g_nancy->_cursor->_puzzleExitCursor);
@@ -464,7 +509,10 @@ void BulPuzzle::handleInput(NancyInput &input) {
 
 	bool canClick = _currentAction == kNone && !g_nancy->_sound->isSoundPlaying(_moveSound);
 
-	if (NancySceneState.getViewport().convertViewportToScreen(_rollButtonDest).contains(input.mousePos)) {
+	// The player cannot use the roll/pass buttons during the computer opponent's turn
+	bool isPcTurn = _playAgainstComputer && _turn / _numRolls != 0;
+
+	if (!isPcTurn && NancySceneState.getViewport().convertViewportToScreen(_rollButtonDest).contains(input.mousePos)) {
 		g_nancy->_cursor->setCursorType(CursorManager::kHotspot);
 
 		if (canClick && input.input & NancyInput::kLeftMouseButtonUp) {
@@ -479,7 +527,7 @@ void BulPuzzle::handleInput(NancyInput &input) {
 		return;
 	}
 
-	if ((_turn % _numRolls) && NancySceneState.getViewport().convertViewportToScreen(_passButtonDest).contains(input.mousePos)) {
+	if (!isPcTurn && (_turn % _numRolls) && NancySceneState.getViewport().convertViewportToScreen(_passButtonDest).contains(input.mousePos)) {
 		g_nancy->_cursor->setCursorType(CursorManager::kHotspot);
 
 		if (canClick && input.input & NancyInput::kLeftMouseButtonUp) {
diff --git a/engines/nancy/action/puzzle/bulpuzzle.h b/engines/nancy/action/puzzle/bulpuzzle.h
index 22c08e1324d..4cb9b9d81b2 100644
--- a/engines/nancy/action/puzzle/bulpuzzle.h
+++ b/engines/nancy/action/puzzle/bulpuzzle.h
@@ -48,6 +48,9 @@ protected:
 	void movePiece(bool player);
 	void reset(bool capture);
 
+	// Nancy 11: drive the computer opponent's roll/pass during its turn
+	void doAiTurn();
+
 	// Nancy 11: pick (and optionally play) a random clip from one of the per-player voice tables
 	Common::String pickVoiceLine(int player, int table);
 	void playVoiceLine(int player, int table);
@@ -111,10 +114,10 @@ protected:
 	uint16 _solveSoundDelay = 0;
 	SoundDescription _solveSound;
 
-	SceneChangeWithFlag _exitScene; // also when losing
+	SceneChangeWithFlag _exitScene; // when losing (Nancy 11 shares the win scene, set apart by the flag)
 	uint16 _loseSoundDelay = 0;
 	SoundDescription _loseSound;
-	FlagDescription _loseFlag2; // nancy11: event flag set when the player gives up rather than loses
+	SceneChangeWithFlag _giveUpScene; // nancy11: separate scene reached by giving up via the exit hotspot
 	Common::Rect _exitHotspot;
 
 	// Nancy 11 voice clips: two players, seven tables each (entry counts 1,1,4,4,4,4,4).
@@ -125,6 +128,11 @@ protected:
 	int _prevSide = -1;
 	bool _gaveUp = false;
 
+	// Nancy 11: when true the opponent is computer-controlled and takes its own turns
+	bool _playAgainstComputer = false;
+	// Nancy 11: the computer passes strategically (based on piece positions) rather than randomly
+	bool _aiPassStrategy = false;
+
 	Graphics::ManagedSurface _image;
 
 	int16 _playerPos = 0;


Commit: 945e42ee839d9d118915863f5bcf3001a6463b7a
    https://github.com/scummvm/scummvm/commit/945e42ee839d9d118915863f5bcf3001a6463b7a
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-05T03:42:51+03:00

Commit Message:
NANCY: NANCY11: Implement changes to MultiBuildPuzzle

Fixes Mrs Drake's plant potting puzzle

Changed paths:
    engines/nancy/action/puzzle/multibuildpuzzle.cpp


diff --git a/engines/nancy/action/puzzle/multibuildpuzzle.cpp b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
index a50e4b2ef64..6493fe8d7b7 100644
--- a/engines/nancy/action/puzzle/multibuildpuzzle.cpp
+++ b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
@@ -36,7 +36,7 @@ namespace Action {
 // Nancy 10 TODOs:
 //  - completion-animation playback (data read, never rendered)
 //  - _retainState save/restore via a PuzzleData entry
-//  - 4th cursor id, 7 × 33-byte string slots, per-piece reserved rect
+//  - 4th cursor id, 7 × 33-byte string slots
 void MultiBuildPuzzle::init() {
 	g_nancy->_resource->loadImage(_primaryImageName, _primaryImage);
 	_primaryImage.setTransparentColor(_drawSurface.getTransparentColor());
@@ -157,15 +157,18 @@ void MultiBuildPuzzle::readData(Common::SeekableReadStream &stream) {
 		if (i < _numPieces) {
 			Piece &p = _pieces[i];
 			if (isNancy10) {
-				stream.skip(16); // TODO: leading reserved rect, empty in cake puzzle.
+				// Same rect order as Nancy 9, with placedDstRect appended.
+				// srcRect is empty when the unplaced piece is baked into the
+				// scene overlay (cake mixing) and non-empty when it must be
+				// rendered live at rest (plant potting).
 				readRect(stream, p.srcRect);
+				readRect(stream, p.homeRect);
 				readRect(stream, p.altSrcRect);
 				readRect(stream, p.cuSrcRect);
 				readRect(stream, p.placedDstRect);
 				p.counterByte  = stream.readByte();
 				p.mustPlace    = stream.readByte();
 				p.mustNotPlace = stream.readByte();
-				p.homeRect = p.srcRect;
 			} else {
 				readRect(stream, p.srcRect);
 				readRect(stream, p.homeRect);
@@ -798,10 +801,13 @@ void MultiBuildPuzzle::updatePieceRender(int pieceIdx) {
 	bool isSelected = (!_isDragging && pieceIdx == _selectedPiece);
 	bool isDragging  = (_isDragging  && pieceIdx == _pickedUpPiece);
 
-	// Nancy 10: at-rest pieces are baked into the primary overlay; placed
-	// pieces hide too when the completion animation will cover them.
+	// Nancy 10+: a piece with no source rect is baked into the scene overlay
+	// at rest (cake mixing) and must stay hidden. Placed pieces also hide when
+	// a completion animation will cover them. Pieces that do carry a source
+	// rect (plant potting) render normally at rest.
 	if (g_nancy->getGameType() >= kGameTypeNancy10 && !isDragging && !isSelected) {
-		if (!p.isPlaced || _hasAnimImage) {
+		bool bakedAtRest = !p.isPlaced && p.srcRect.isEmpty();
+		if (bakedAtRest || (p.isPlaced && _hasAnimImage)) {
 			p.setVisible(false);
 			p.moveTo(p.gameRect);
 			return;


Commit: d66b2a108661aa3670ae5e063464ac5e7f621134
    https://github.com/scummvm/scummvm/commit/d66b2a108661aa3670ae5e063464ac5e7f621134
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-05T03:42:52+03:00

Commit Message:
NANCY: NANCY111: Fix count requirement for MultiBuildPuzzle

Fixes baking a cake for Loulou

Changed paths:
    engines/nancy/action/puzzle/multibuildpuzzle.cpp
    engines/nancy/action/puzzle/multibuildpuzzle.h


diff --git a/engines/nancy/action/puzzle/multibuildpuzzle.cpp b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
index 6493fe8d7b7..ba1743d9832 100644
--- a/engines/nancy/action/puzzle/multibuildpuzzle.cpp
+++ b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
@@ -716,7 +716,11 @@ bool MultiBuildPuzzle::updateSolveFlags() {
 	for (uint i = 0; i < _numPieces; ++i) {
 		if (_pieces[i].placeCount > 0 && _pieces[i].mustNotPlace > 0)
 			return false;
-		if (_pieces[i].placeCount != _pieces[i].mustPlace)
+		// mustPlace is an exact required count only when non-zero. A zero
+		// mustPlace means the piece has no count requirement (e.g. cake
+		// cooking, where the win rule is just "place enough good ingredients
+		// and no bad ones"); placing it must not fail the check.
+		if (_pieces[i].mustPlace > 0 && _pieces[i].placeCount != _pieces[i].mustPlace)
 			return false;
 	}
 
diff --git a/engines/nancy/action/puzzle/multibuildpuzzle.h b/engines/nancy/action/puzzle/multibuildpuzzle.h
index 66f8a9d6317..1078efadc09 100644
--- a/engines/nancy/action/puzzle/multibuildpuzzle.h
+++ b/engines/nancy/action/puzzle/multibuildpuzzle.h
@@ -66,7 +66,7 @@ protected:
 		Common::Rect cuSrcRect;   // Source in closeup image
 		Common::Rect placedDstRect; // Nancy 10: placement destination on screen
 		uint8 counterByte = 0;    // Non-zero: respawns on placement; doesn't count toward solve
-		uint8 mustPlace = 0;      // Required placement count (exact match for solve). 0/1 in Nancy 9; arbitrary in Nancy 10+ (e.g. cake mixing recipe quantities)
+		uint8 mustPlace = 0;      // Exact required placement count, checked only when non-zero (0 = no count requirement). 0/1 in Nancy 9; recipe quantities in cake mixing; 0 for all pieces in cake cooking
 		uint8 mustNotPlace = 0;   // Non-zero: placing this fails the solution check
 		uint8 placeCount = 0;     // Runtime: number of times this piece (or any clone of it) has been placed
 


Commit: 1a1c8cc7fcc10aa0c2fdac2499384c5c7bef0cfc
    https://github.com/scummvm/scummvm/commit/1a1c8cc7fcc10aa0c2fdac2499384c5c7bef0cfc
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-05T03:42:53+03:00

Commit Message:
NANCY: NANCY10: Fix reading scene from CellPhonePopCellSceneFromStack

Death screens work correctly now, without reading junk data

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


diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index eceec99fa7f..f641f0bda14 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -374,7 +374,7 @@ void ChangeCellPhoneInfo::execute() {
 }
 
 void CellPhonePopCellSceneFromStack::readData(Common::SeekableReadStream &stream) {
-	_sceneChange.readData(stream);
+	_sceneChange.sceneID = stream.readUint16LE();
 }
 
 void CellPhonePopCellSceneFromStack::execute() {




More information about the Scummvm-git-logs mailing list