[Scummvm-git-logs] scummvm master -> 9e90b84e1f326c88f377d52ccfee07f7635ab4d1

bluegr noreply at scummvm.org
Mon Jul 13 02:06:29 UTC 2026


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

Summary:
458babd447 NANCY: Remove unused parameter of exportCif()
8c4d1e293c NANCY: NANCY6: Treat invalid mark tokens as newline
855fbb88f9 NANCY: NANCY6: Check only the last key presses in OrderingPuzzle
ae97cc0053 NANCY: NANCY12: Add safeguards for unimplemented conditional dialogues
7861004392 NANCY: NANCY12: Use the mouse for controlling the car in DrivingPuzzle
9e90b84e1f NANCY: NANCY10: Partial fix for textbox padding


Commit: 458babd447b2a604d165ea92296097f0b3544215
    https://github.com/scummvm/scummvm/commit/458babd447b2a604d165ea92296097f0b3544215
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-13T05:06:14+03:00

Commit Message:
NANCY: Remove unused parameter of exportCif()

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index fe3019ce90a..b7b2e9ae01d 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -154,13 +154,13 @@ void NancyConsole::postEnter() {
 }
 
 bool NancyConsole::Cmd_cifExport(int argc, const char **argv) {
-	if (argc < 2 || argc > 3) {
+	if (argc != 2) {
 		debugPrintf("Exports the specified resource to .cif file\n");
-		debugPrintf("Usage: %s <name> [cal]\n", argv[0]);
+		debugPrintf("Usage: %s <name>\n", argv[0]);
 		return true;
 	}
 
-	if (!g_nancy->_resource->exportCif((argc == 2 ? "" : argv[2]), argv[1]))
+	if (!g_nancy->_resource->exportCif(argv[1]))
 		debugPrintf("Failed to export '%s'\n", argv[1]);
 
 	return true;
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 7c2bdedf21d..a25e5291871 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -315,7 +315,7 @@ void ResourceManager::list(const Common::String &treeName, Common::Array<Common:
 	}
 }
 
-bool ResourceManager::exportCif(const Common::String &treeName, const Common::Path &name) {
+bool ResourceManager::exportCif(const Common::Path &name) {
 	if (!SearchMan.hasFile(name)) {
 		return false;
 	}
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 3db7e6bf5ae..1c2d3a75ce3 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -59,7 +59,7 @@ public:
 	void list(const Common::String &treeName, Common::Array<Common::Path> &outList, CifInfo::ResType type) const;
 
 	// Exports a single resource as a standalone .cif file
-	bool exportCif(const Common::String &treeName, const Common::Path &name);
+	bool exportCif(const Common::Path &name);
 
 	// Exports a collection of resources as a ciftree
 	bool exportCifTree(const Common::String &treeName, const Common::Array<Common::Path> &names);


Commit: 8c4d1e293c21e980a2c0855923d880067accf9d6
    https://github.com/scummvm/scummvm/commit/8c4d1e293c21e980a2c0855923d880067accf9d6
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-13T05:06:15+03:00

Commit Message:
NANCY: NANCY6: Treat invalid mark tokens as newline

Mark tokens were used in Nancy 8 and newer games. In earlier games (for
example, in a stray mark token found in Nancy6), they're rendered as
newline.

Fix #16935

Changed paths:
    engines/nancy/misc/hypertext.cpp


diff --git a/engines/nancy/misc/hypertext.cpp b/engines/nancy/misc/hypertext.cpp
index cd77a5133f4..f24d0d858ca 100644
--- a/engines/nancy/misc/hypertext.cpp
+++ b/engines/nancy/misc/hypertext.cpp
@@ -204,17 +204,18 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffse
 				case '3':
 				case '4':
 				case '5':
-					// Mark token for Nancy 8 and later games. no-op for earlier games
-					if (g_nancy->getGameType() <= kGameTypeNancy7) {
-						continue;
-					}
-
 					if (curToken.size() != 1) {
 						break;
 					}
 
-					metaInfo.push({MetaInfo::kMark, numNonSpaceChars, (byte)(curToken[0] - '1')});
-					hasMark = true;
+					// Mark token for Nancy 8 and later games. Treated as newline in earlier games
+					if (g_nancy->getGameType() <= kGameTypeNancy7) {
+						currentLine += '\n';
+						newlineTokens.push(numNonSpaceChars);
+					} else {
+						metaInfo.push({MetaInfo::kMark, numNonSpaceChars, (byte)(curToken[0] - '1')});
+						hasMark = true;
+					}
 					continue;
 				default:
 					break;


Commit: 855fbb88f9a0bd2da2aafef0a1d77e6052f7cd5e
    https://github.com/scummvm/scummvm/commit/855fbb88f9a0bd2da2aafef0a1d77e6052f7cd5e
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-13T05:06:16+03:00

Commit Message:
NANCY: NANCY6: Check only the last key presses in OrderingPuzzle

Fix #16935

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


diff --git a/engines/nancy/action/puzzle/orderingpuzzle.cpp b/engines/nancy/action/puzzle/orderingpuzzle.cpp
index 21eea76a44f..d130dd186d5 100644
--- a/engines/nancy/action/puzzle/orderingpuzzle.cpp
+++ b/engines/nancy/action/puzzle/orderingpuzzle.cpp
@@ -460,6 +460,15 @@ void OrderingPuzzle::execute() {
 			bool solved = true;
 
 			if (_puzzleType != kPiano) {
+				// The pre-nancy7 keypad whose buttons pop back up is a rolling entry pad: only the
+				// most recent presses count, so the correct code opens the lock even if wrong digits
+				// were entered first. Keep just the last few presses so the check below matches a
+				// sliding window (same approach as the piano puzzle below).
+				if (_puzzleType == kKeypad && !_itemsStayDown && g_nancy->getGameType() <= kGameTypeNancy6 &&
+						_clickedSequence.size() > _correctSequence.size() && !_correctSequence.empty()) {
+					_clickedSequence.erase(&_clickedSequence[0], &_clickedSequence[_clickedSequence.size() - _correctSequence.size()]);
+				}
+
 				if (_clickedSequence.size() >= _correctSequence.size()) {
 					bool equal = true;
 					if (_checkOrder) {


Commit: ae97cc0053952cff20981ff1a1f22cb9fa9d6ca5
    https://github.com/scummvm/scummvm/commit/ae97cc0053952cff20981ff1a1f22cb9fa9d6ca5
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-13T05:06:17+03:00

Commit Message:
NANCY: NANCY12: Add safeguards for unimplemented conditional dialogues

This will allow the game to continue without crashing, for now

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


diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index 46a6fbd8971..b3adda572e9 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -442,6 +442,13 @@ void ConversationSound::execute() {
 }
 
 void ConversationSound::addConditionalDialogue() {
+	if (g_nancy->getGameType() >= kGameTypeNancy12) {
+		// TODO: Nancy12+ moved the per-character conditional dialogue out
+		// of the executable and into separate data files.
+		warning("Conditional dialogue for character %d is not implemented in Nancy12+", _conditionalResponseCharacterID);
+		return;
+	}
+
 	// We adjust the base label for Nancy 11 event flags, which can be
 	// larger than 1000.
 	int16 baseLabel = g_nancy->getGameType() >= kGameTypeNancy11 ? 1000 : 0;
@@ -512,6 +519,13 @@ void ConversationSound::addConditionalDialogue() {
 }
 
 void ConversationSound::addGoodbye() {
+	if (g_nancy->getGameType() >= kGameTypeNancy12) {
+		// TODO: Nancy12+ moved the per-character goodbye dialogue out
+		// of the executable and into separate data files.
+		warning("Goodbye dialogue for character %d is not implemented in Nancy12+", _goodbyeResponseCharacterID);
+		return;
+	}
+
 	// We adjust the base label for Nancy 11 event flags, which can be
 	// larger than 1000.
 	int16 baseLabel = g_nancy->getGameType() >= kGameTypeNancy11 ? 1000 : 0;


Commit: 786100439213fd772e4533fd4915b9bcb45c3d58
    https://github.com/scummvm/scummvm/commit/786100439213fd772e4533fd4915b9bcb45c3d58
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-13T05:06:18+03:00

Commit Message:
NANCY: NANCY12: Use the mouse for controlling the car in DrivingPuzzle

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


diff --git a/engines/nancy/action/puzzle/drivingpuzzle.cpp b/engines/nancy/action/puzzle/drivingpuzzle.cpp
index e45fdfd6362..7d99c38a672 100644
--- a/engines/nancy/action/puzzle/drivingpuzzle.cpp
+++ b/engines/nancy/action/puzzle/drivingpuzzle.cpp
@@ -177,7 +177,7 @@ void DrivingPuzzle::playSoundBlock(const RandomSoundBlock &block) {
 	SoundDescription desc;
 	desc.name = name;
 	desc.channelID = block.channel;
-	desc.numLoops = block.numLoops > 0 ? block.numLoops : 1;
+	desc.numLoops = block.numLoops;	// 0 == loop forever (the engine ambience)
 	desc.volume = block.volume;
 
 	g_nancy->_sound->loadSound(desc);
@@ -235,15 +235,19 @@ uint DrivingPuzzle::frameIndexForHeading(double heading, uint frameCount) const
 	return idx % frameCount;
 }
 
-void DrivingPuzzle::drawScene() {
-	int w = _drawSurface.w;
-	int h = _drawSurface.h;
-
+Common::Point DrivingPuzzle::cameraOffset() const {
 	// Car-centered camera, clamped to the map bounds.
-	int camX = CLIP<int>((int)(_carX + 0.5) - w / 2, 0, MAX(0, _image.w - w));
-	int camY = CLIP<int>((int)(_carY + 0.5) - h / 2, 0, MAX(0, _image.h - h));
+	int camX = CLIP<int>((int)(_carX + 0.5) - _drawSurface.w / 2, 0, MAX(0, _image.w - _drawSurface.w));
+	int camY = CLIP<int>((int)(_carY + 0.5) - _drawSurface.h / 2, 0, MAX(0, _image.h - _drawSurface.h));
+	return Common::Point(camX, camY);
+}
+
+void DrivingPuzzle::drawScene() {
+	Common::Point cam = cameraOffset();
+	int camX = cam.x;
+	int camY = cam.y;
 
-	_drawSurface.blitFrom(_image, Common::Rect(camX, camY, camX + w, camY + h), Common::Point(0, 0));
+	_drawSurface.blitFrom(_image, Common::Rect(camX, camY, camX + _drawSurface.w, camY + _drawSurface.h), Common::Point(0, 0));
 
 	// The chaser car (kChase), drawn under the player car.
 	if (_variant == kChase && !_frameRects2.empty() && _chaseCarImage.w > 0) {
@@ -300,29 +304,17 @@ void DrivingPuzzle::updateChaser() {
 	// (_chaseParams) and the "chaser left the viewport" loss branch are not simulated.
 }
 
-// Per-frame car physics. The acceleration divisors (1.0/0.4) and 0.02 timestep are
-// exact; the steering rate and velocity decay are playability stand-ins.
-void DrivingPuzzle::updatePhysics(const NancyInput &input) {
+// Per-frame car physics. Throttle is +1 (forward), -1 (reverse) or 0 (coast); the car
+// already faces the cursor (steering happens in handleInput). The acceleration divisors
+// (1.0/0.4) and 0.02 timestep are exact; the velocity decay is a playability stand-in.
+void DrivingPuzzle::updatePhysics(int throttle) {
 	const double timeStep = 0.02;
-	const double steerRate = 0.05;
 	const double decay = 0.98;
 	const double forwardCap = MAX(0.0, _speedCap);
 
-	if (input.input & NancyInput::kMoveLeft) {
-		_carHeading += steerRate;
-	}
-	if (input.input & NancyInput::kMoveRight) {
-		_carHeading -= steerRate;
-	}
-	if (_carHeading < 0.0) {
-		_carHeading += 2.0 * M_PI;
-	} else if (_carHeading >= 2.0 * M_PI) {
-		_carHeading -= 2.0 * M_PI;
-	}
-
-	if (input.input & NancyInput::kMoveUp) {
+	if (throttle > 0) {
 		_carVelocity += (forwardCap / 1.0) * timeStep;
-	} else if (input.input & NancyInput::kMoveDown) {
+	} else if (throttle < 0) {
 		_carVelocity -= ((double)_forwardSpeed / 0.4) * timeStep;
 	} else {
 		_carVelocity *= decay;
@@ -391,6 +383,7 @@ void DrivingPuzzle::execute() {
 	case kRun:
 		break;
 	case kActionTrigger:
+		g_nancy->_sound->stopSound(_soundBlocks[2].channel);	// stop the engine ambience
 		if (_triggeredDest >= 0 && _triggeredDest < (int)_destinations.size()) {
 			const DestinationZone &dest = _destinations[_triggeredDest];
 			if (dest.eventFlag != -1) {
@@ -411,12 +404,35 @@ void DrivingPuzzle::handleInput(NancyInput &input) {
 		return;
 	}
 
-	// Drive continuously so momentum, steering and the chaser animate every frame.
+	// Throttle with the mouse buttons: left drives forward, right reverses.
+	int throttle = 0;
+	if (input.input & NancyInput::kLeftMouseButtonHeld) {
+		throttle = 1;
+	} else if (input.input & NancyInput::kRightMouseButtonHeld) {
+		throttle = -1;
+	}
+
+	// Steer the car to face the cursor while driving (its distance is irrelevant).
+	if (throttle != 0) {
+		Common::Point cam = cameraOffset();
+		Common::Rect mouseVp = NancySceneState.getViewport().convertScreenToViewport(
+			Common::Rect(input.mousePos.x, input.mousePos.y, input.mousePos.x + 1, input.mousePos.y + 1));
+		double dx = (double)mouseVp.left - (_carX - cam.x);
+		double dy = (double)mouseVp.top - (_carY - cam.y);
+		if (dx != 0.0 || dy != 0.0) {
+			_carHeading = atan2(-dy, dx);
+			if (_carHeading < 0.0) {
+				_carHeading += 2.0 * M_PI;
+			}
+		}
+	}
+
+	// Drive continuously so momentum and the chaser animate every frame.
 	if (_variant == kChase) {
 		updateChaser();
 	}
 
-	updatePhysics(input);
+	updatePhysics(throttle);
 
 	if (_state == kRun) {
 		drawScene();
diff --git a/engines/nancy/action/puzzle/drivingpuzzle.h b/engines/nancy/action/puzzle/drivingpuzzle.h
index 074514128ba..7571d82592d 100644
--- a/engines/nancy/action/puzzle/drivingpuzzle.h
+++ b/engines/nancy/action/puzzle/drivingpuzzle.h
@@ -43,14 +43,18 @@ namespace Action {
 // destination scene id and the transition effect), type 0x14 zones are boundaries,
 // and the remaining subtypes are decorations and driving hazards.
 //
+// Controls: the car steers to face the cursor (distance is irrelevant); the left mouse
+// button drives forward (accelerating while held) and the right button reverses.
+//
 // TODO (need runtime tuning):
 //  - Fuel: burn the gas-gauge UI resource (index _frictionIndex) while driving; it
 //    currently stays at its seeded value. The DT_RESOURCE dependency that reads it is
 //    handled in ActionManager::processDependency.
-//  - Steering: approximated with the arrow keys (the game uses click-to-steer).
+//  - Hazards: potholes (type 0x17) should damage the car (more the faster it is hit,
+//    building to a flat tire) and mud puddles should be penalised; both are ignored.
 //  - Collision: only the type 0x14 boundary rects block the car (no per-pixel mask).
 //  - kChase: no second-path switch or "chaser left the viewport" loss branch.
-//  - Overlay / hazard zone subtypes (0x0d/0x0e/0x0f/0x05/0x03/0x17) are ignored.
+//  - The decorative overlay zone subtypes (0x0d and friends) are ignored.
 class DrivingPuzzle : public RenderActionRecord {
 public:
 	enum Variant { kDriving = 0, kChase };
@@ -122,9 +126,12 @@ protected:
 	// Plays one (randomly chosen) entry of a random-sound block.
 	void playSoundBlock(const RandomSoundBlock &block);
 
-	// Advances the car's heading/velocity/position for one frame from the current
-	// movement input.
-	void updatePhysics(const NancyInput &input);
+	// Advances the car's velocity/position for one frame. Throttle is +1 forward, -1
+	// reverse, 0 coast; the heading is set separately (steering toward the cursor).
+	void updatePhysics(int throttle);
+
+	// Top-left of the car-centered camera window into the map, clamped to its bounds.
+	Common::Point cameraOffset() const;
 
 	// Advances the chaser along its recorded path (kChase) and slows the player's
 	// speed cap the closer the chaser gets.


Commit: 9e90b84e1f326c88f377d52ccfee07f7635ab4d1
    https://github.com/scummvm/scummvm/commit/9e90b84e1f326c88f377d52ccfee07f7635ab4d1
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-13T05:06:19+03:00

Commit Message:
NANCY: NANCY10: Partial fix for textbox padding

Improved text position in ScrollTextBox. Still need to fix the extra
padding on the left.

Partially fixes #16890

Changed paths:
    engines/nancy/ui/scrolltextbox.cpp


diff --git a/engines/nancy/ui/scrolltextbox.cpp b/engines/nancy/ui/scrolltextbox.cpp
index 08ccfe21be7..45960a84d06 100644
--- a/engines/nancy/ui/scrolltextbox.cpp
+++ b/engines/nancy/ui/scrolltextbox.cpp
@@ -161,7 +161,7 @@ void ScrollTextBox::drawContent() {
 		// Fixed two-line strip at the top of the overlay. Drop the chrome's
 		// bottom margin so it's just tall enough for two lines.
 		const Font *font = g_nancy->_graphics->getFont(fontID);
-		const int lineStep = font->getFontHeight() + font->getFontHeight() / 9;
+		const int lineStep = font->getLineHeight() + font->getLineHeight() / 9;
 		const int twoLineContent = _tboxData->scrollbarDefaultPos.y + 2 * lineStep;
 		const int miniHeight = MIN(viewport.top + MAX(contentHeight, twoLineContent), fullHeight);
 		boxRect = Common::Rect(_fullPopupRect.left, _fullPopupRect.top,
@@ -173,6 +173,7 @@ void ScrollTextBox::drawContent() {
 	_drawSurface.create(boxRect.width(), boxRect.height(), g_nancy->_graphics->getInputPixelFormat());
 
 	int textLeft = viewport.left;
+	int textTop = viewport.top;
 	if (_expanded) {
 		setTransparent(false);
 		drawBackground();
@@ -181,15 +182,21 @@ void ScrollTextBox::drawContent() {
 		// and pull the text left into the unused scrollbar gap.
 		setTransparent(true);
 		_drawSurface.clear(transColor);
+		// Nudge the first line down one pixel so its top padding matches the
+		// original's mini strip.
+		textTop += 1;
 		const UISliderRecord &sl = _header->slider;
 		if (_header->sliderEnabled && !sl.destRect.isEmpty()) {
-			textLeft = toPopupLocal(sl.destRect, sl.destUsesGameFrameOffset != 0).left;
+			// Align the glyphs to the scrollbar's left edge, dropping the
+			// line-start inset that is baked into the text surface (the mini
+			// strip reclaims the whole gutter).
+			textLeft = toPopupLocal(sl.destRect, sl.destUsesGameFrameOffset != 0).left - _tboxData->lineStartXCursor;
 		}
 	}
 
 	_drawSurface.blitFrom(_fullSurface,
 		Common::Rect(0, scrollY, _fullSurface.w, scrollY + visibleTextHeight),
-		Common::Point(textLeft, viewport.top));
+		Common::Point(textLeft, textTop));
 
 	if (_expanded) {
 		drawScrollbar(_scrollbarDragging ? kUIButtonPressed : (_scrollbarHovered ? kUIButtonHover : kUIButtonIdle));




More information about the Scummvm-git-logs mailing list