[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