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

bluegr noreply at scummvm.org
Wed Jul 29 06:05:03 UTC 2026


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

Summary:
b920158649 NANCY: NANCY10: Close active popups when auto-opening a new one
bd0c9859db NANCY: NANCY10: Fix slide arrow cursors used in CollisionPuzzle types
b18ea39073 NANCY: NANCY10: Fix cellphone UI issues in no signal mode


Commit: b9201586493df4d3d96c9f76ff745b43939ab792
    https://github.com/scummvm/scummvm/commit/b9201586493df4d3d96c9f76ff745b43939ab792
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-29T09:04:49+03:00

Commit Message:
NANCY: NANCY10: Close active popups when auto-opening a new one

Fixes lingering open popups when answering a call from the cellphone,
e.g. when a death scene starts.

Fix #17031

Changed paths:
    engines/nancy/action/miscrecords.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 614f32f7bbb..036e130bb69 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -243,6 +243,11 @@ void ControlUIItems::execute() {
 	// phone, _startScene (when set) is the scene to jump to once it opens,
 	// which places a call that starts a conversation there.
 	if (_autoOpenOrBadgeSound == 1) {
+		// Only one popup shows at a time. If the player left one open (e.g. the
+		// inventory), close it before this AR auto-opens its target, so the
+		// game-over incoming call doesn't stack the phone over another popup.
+		NancySceneState.closeActivePopups();
+
 		switch (_uiButton) {
 		case kUITypeInventory:
 			NancySceneState.getInventoryPopup().open();
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index ce44d36c172..40f82f276fb 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -1525,6 +1525,13 @@ Common::Rect Scene::activePopupConfinement() const {
 	return Common::Rect();
 }
 
+void Scene::closeActivePopups() {
+	if (_conversationPopup.isVisible()) _conversationPopup.close();
+	if (_inventoryPopup.isOpen())       _inventoryPopup.close();
+	if (_notebookPopup.isVisible())     _notebookPopup.close();
+	if (_cellPhonePopup.isVisible())    _cellPhonePopup.close();
+}
+
 void Scene::handleInput() {
 	// While a UI prep scene is running the player shouldn't be able to interact
 	// with the (hidden, videoless) prep scenes. Swallow all input until the
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 11d6e67e412..29cd250b325 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -249,6 +249,10 @@ public:
 	State getState() const { return _state; }
 	void setState(State state) { _state = state; }
 
+	// Close every open Nancy 10+ popup. Used before a script auto-opens one
+	// (e.g. the incoming game-over call) so two popups can't stack.
+	void closeActivePopups();
+
 	struct Timers {
 		Time pushedPlayTime;
 		Time lastTotalTime;


Commit: bd0c9859db907d94589ec57de0168a4c2f46d720
    https://github.com/scummvm/scummvm/commit/bd0c9859db907d94589ec57de0168a4c2f46d720
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-29T09:04:51+03:00

Commit Message:
NANCY: NANCY10: Fix slide arrow cursors used in CollisionPuzzle types

Fix #17023

Changed paths:
    engines/nancy/action/puzzle/collisionpuzzle.cpp
    engines/nancy/cursor.cpp
    engines/nancy/cursor.h


diff --git a/engines/nancy/action/puzzle/collisionpuzzle.cpp b/engines/nancy/action/puzzle/collisionpuzzle.cpp
index 967e5ed99dc..053e9b8ba95 100644
--- a/engines/nancy/action/puzzle/collisionpuzzle.cpp
+++ b/engines/nancy/action/puzzle/collisionpuzzle.cpp
@@ -668,6 +668,15 @@ void CollisionPuzzle::handleInput(NancyInput &input) {
 		return;
 	}
 
+	// Nancy 10 gave both puzzle variants dedicated slide-arrow cursors (system cursor types 23-26);
+	// earlier games reuse the generic scene-movement arrows.
+	const bool useSlideCursors =
+		g_nancy->getGameType() >= kGameTypeNancy10 && g_nancy->getGameType() <= kGameTypeNancy12;
+	const CursorManager::CursorType leftCursor = useSlideCursors ? CursorManager::kNewPuzzleSlideLeft : CursorManager::kMoveLeft;
+	const CursorManager::CursorType rightCursor = useSlideCursors ? CursorManager::kNewPuzzleSlideRight : CursorManager::kMoveRight;
+	const CursorManager::CursorType upCursor = useSlideCursors ? CursorManager::kNewPuzzleSlideUp : CursorManager::kMoveUp;
+	const CursorManager::CursorType downCursor = useSlideCursors ? CursorManager::kNewPuzzleSlideDown : CursorManager::kMoveDown;
+
 	for (uint i = 0; i < _pieces.size(); ++i) {
 		Common::Point checkPos;
 		Common::Rect left, right, up, down;
@@ -706,7 +715,7 @@ void CollisionPuzzle::handleInput(NancyInput &input) {
 			if (left.contains(input.mousePos)) {
 				checkPos = movePiece(i, kWallLeft);
 				if (checkPos != _pieces[i]._gridPos) {
-					g_nancy->_cursor->setCursorType(CursorManager::kMoveLeft);
+					g_nancy->_cursor->setCursorType(leftCursor);
 
 					if (input.input & NancyInput::kLeftMouseButtonUp) {
 						_lastPosition = _pieces[i]._gridPos;
@@ -724,7 +733,7 @@ void CollisionPuzzle::handleInput(NancyInput &input) {
 			if (right.contains(input.mousePos)) {
 				checkPos = movePiece(i, kWallRight);
 				if (checkPos != _pieces[i]._gridPos) {
-					g_nancy->_cursor->setCursorType(CursorManager::kMoveRight);
+					g_nancy->_cursor->setCursorType(rightCursor);
 
 					if (input.input & NancyInput::kLeftMouseButtonUp) {
 						_lastPosition = _pieces[i]._gridPos;
@@ -742,7 +751,7 @@ void CollisionPuzzle::handleInput(NancyInput &input) {
 			if (up.contains(input.mousePos)) {
 				checkPos = movePiece(i, kWallUp);
 				if (checkPos != _pieces[i]._gridPos) {
-					g_nancy->_cursor->setCursorType(CursorManager::kMoveUp);
+					g_nancy->_cursor->setCursorType(upCursor);
 
 					if (input.input & NancyInput::kLeftMouseButtonUp) {
 						_lastPosition = _pieces[i]._gridPos;
@@ -760,7 +769,7 @@ void CollisionPuzzle::handleInput(NancyInput &input) {
 			if (down.contains(input.mousePos)) {
 				checkPos = movePiece(i, kWallDown);
 				if (checkPos != _pieces[i]._gridPos) {
-					g_nancy->_cursor->setCursorType(CursorManager::kMoveDown);
+					g_nancy->_cursor->setCursorType(downCursor);
 
 					if (input.input & NancyInput::kLeftMouseButtonUp) {
 						_lastPosition = _pieces[i]._gridPos;
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index a5e8e410896..e628451c762 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -183,6 +183,10 @@ uint CursorManager::resolveNancy10CursorID(CursorType type, int16 itemID, bool s
 	case kInvertedRotateLeft:   return kNewInvertedRotateLeft;
 	case kDragHand:             return kNewDragHand;
 	case kPuzzleArrow:          return kNewPuzzleArrow;
+	case kNewPuzzleSlideUp:     return kNewPuzzleSlideUp;
+	case kNewPuzzleSlideDown:   return kNewPuzzleSlideDown;
+	case kNewPuzzleSlideLeft:   return kNewPuzzleSlideLeft;
+	case kNewPuzzleSlideRight:  return kNewPuzzleSlideRight;
 	case kDropHand:             return kNewDropHand;
 	default:
 		return kNewNormal;
diff --git a/engines/nancy/cursor.h b/engines/nancy/cursor.h
index a0d54a49131..5f00f938b29 100644
--- a/engines/nancy/cursor.h
+++ b/engines/nancy/cursor.h
@@ -91,6 +91,10 @@ public:
 		kNewUseHand				= 36,	// Type 18 — Hand used while using items
 		kNewDragHand			= 38,	// Type 19 — Hand used while dragging puzzle pieces (e.g. SortPuzzle pickup action sets this)
 		kNewPuzzleArrow			= 45,	// Type 22 hotspot — Arrow cursor shown when hovering a clickable puzzle hotspot
+		kNewPuzzleSlideUp		= 47,	// Type 23 hotspot — Slide-arrow shown over a movable tile in CollisionPuzzle/TileMovePuzzle
+		kNewPuzzleSlideDown		= 49,	// Type 24 hotspot
+		kNewPuzzleSlideLeft		= 51,	// Type 25 hotspot
+		kNewPuzzleSlideRight	= 53,	// Type 26 hotspot
 		kNewDropHand			= 64,	// Type 32 — Hand shown when a held piece is dropped (briefly set on the drop action)
 
 		// Cursor types in Nancy13 and newer games. Nancy13 rebuilt the CURS


Commit: b18ea3907332333834ed5afc5ad88fa9d6a15a5e
    https://github.com/scummvm/scummvm/commit/b18ea3907332333834ed5afc5ad88fa9d6a15a5e
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-29T09:04:54+03:00

Commit Message:
NANCY: NANCY10: Fix cellphone UI issues in no signal mode

In no signal mode:
- The Internet browser menu should not be visible
- Position the "No signal" text correctly on screen
- Hide the help button
- The dir and dialing buttons should be disabled
- Also, fix an issue with the original: also disable the dir and
  dialing buttons from the web screen, to disallow placing calls in
  no signal mode

Fix #17028

Changed paths:
    engines/nancy/ui/cellphonepopup.cpp
    engines/nancy/ui/cellphonepopup.h


diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index 6b9614b9291..2436a588ce0 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -542,8 +542,9 @@ void CellPhonePopup::drawChrome() {
 	drawCloseButton(_closeButtonHovered);
 	// The help "?" button lives on the dialer face only. The original hides
 	// it once a call is being placed (the connecting / "We're sorry" screens)
-	// and on every sub-screen that shows its own heading.
-	if (_screenState == kWelcome || _screenState == kDialing) {
+	// and on every sub-screen that shows its own heading. With no signal the
+	// online help is unreachable, so the button is gone entirely.
+	if ((_screenState == kWelcome || _screenState == kDialing) && !_noSignal) {
 		drawHelpButton(_helpButtonHovered ? 1 : 0);
 	}
 	_needsRedraw = true;
@@ -683,7 +684,11 @@ void CellPhonePopup::drawScreenContent() {
 			drawHubButton(kN13SubViewPics);
 		} else {
 			drawHubButton(kSubEmail);
-			drawHubButton(kSubWeb);
+			// No cellular signal locks the phone to "Old Email Only", so the
+			// Internet Browser option is removed from the hub.
+			if (!_noSignal) {
+				drawHubButton(kSubWeb);
+			}
 		}
 		break;
 	}
@@ -943,8 +948,13 @@ void CellPhonePopup::drawStatusLabels() {
 		return;
 	}
 
-	const int x = _uiclData->statusTextX - _screenPosition.left;
-	const int yBase = _uiclData->statusTextY - _screenPosition.top;
+	// The status labels sit one pixel right of the dialed-number baseline.
+	const int x = _uiclData->statusTextX + 1 - _screenPosition.left;
+	// statusTextY + offset is the text baseline in the original, which draws
+	// upward from it; ScummVM's drawString anchors at the top of the line
+	// (baseline = y + getFontHeight()), so subtract the font height to land the
+	// block where the original centers it on the LCD.
+	const int yBase = _uiclData->statusTextY - _screenPosition.top - font->getFontHeight();
 	const int kLineYOffsets[UICL::kNumStatusLabels] = { -10, 20, 50 };
 
 	for (uint i = 0; i < UICL::kNumStatusLabels; ++i) {
@@ -1687,6 +1697,14 @@ void CellPhonePopup::enterScreenState(ScreenState newState) {
 	drawScreenContent();
 }
 
+bool CellPhonePopup::isDialKeyActive(uint slot) const {
+	if (_noSignal && (_screenState == kWelcome || _screenState == kDialing ||
+			_screenState == kOnlineHub)) {
+		return slot == 13;
+	}
+	return true;
+}
+
 void CellPhonePopup::cancelCall() {
 	if (!_callSound.name.empty()) {
 		g_nancy->_sound->stopSound(_callSound);
@@ -2248,7 +2266,7 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 
 	// Green-arrow highlight for the captioned "> HELP" and "< BACK" / HOME
 	// buttons: swap to the pressed sprite while the cursor is over them.
-	const bool helpVisible = (_screenState == kWelcome || _screenState == kDialing);
+	const bool helpVisible = (_screenState == kWelcome || _screenState == kDialing) && !_noSignal;
 	const bool overHelp = helpVisible &&
 			!_uiclData->helpButton.destRect.isEmpty() &&
 			_uiclData->helpButton.destRect.contains(chunkMouse);
@@ -2280,7 +2298,7 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 			(input.input & (NancyInput::kLeftMouseButtonDown | NancyInput::kLeftMouseButtonHeld)) &&
 			!(input.input & NancyInput::kLeftMouseButtonUp)) {
 		for (uint i = 0; i < UICL::kNumDialPadSlots; ++i) {
-			if (_uiclData->dialPadSlots[i].destRect.contains(chunkMouse)) {
+			if (_uiclData->dialPadSlots[i].destRect.contains(chunkMouse) && isDialKeyActive(i)) {
 				newPressed = (int)i;
 				break;
 			}
@@ -2293,7 +2311,7 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 
 	// Help "?" button: opens the help page in the content view. Hidden
 	// (and unclickable) on sub-screens that already show their own heading.
-	if (!isSubScreenState() &&
+	if (!isSubScreenState() && !_noSignal &&
 			!_uiclData->helpButton.destRect.isEmpty() && !_uiclData->helpTextKey.empty() &&
 			!(_screenState == kContentView && _contentKey == _uiclData->helpTextKey) &&
 			_uiclData->helpButton.destRect.contains(chunkMouse)) {
@@ -2386,10 +2404,13 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 
 		// Highlight whichever option button the cursor is over.
 		const bool n13Hub = g_nancy->getGameType() >= kGameTypeNancy13;
+		// No signal removes the Internet Browser option (the Nancy 13 "view
+		// pictures" option in the same slot is not signal-gated).
+		const bool webDisabled = _noSignal && !n13Hub;
 		const int emailSlot = n13Hub ? kN13SubEmail : kSubEmail;
 		const int webSlot = n13Hub ? kN13SubViewPics : kSubWeb;
 		const int newHubHover = emailR.contains(popupMouse) ? emailSlot
-								: webR.contains(popupMouse) ? webSlot : -1;
+								: (!webDisabled && webR.contains(popupMouse)) ? webSlot : -1;
 		if (newHubHover != _hoveredHubButton) {
 			_hoveredHubButton = newHubHover;
 			drawScreenContent();
@@ -2404,7 +2425,7 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 				input.eatMouseInput();
 				return;
 			}
-		} else if (webR.contains(popupMouse)) {
+		} else if (!webDisabled && webR.contains(popupMouse)) {
 			g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
 			if (input.input & NancyInput::kLeftMouseButtonUp) {
 				_directoryScroll = 0;
@@ -2761,7 +2782,8 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 	// Call/talk button. Checked before the dial-pad loop so an overlapping
 	// slot can't eat it. The Talk key is dial-pad slot 12. Only live while the
 	// keypad is on screen (skipped in the zoomed web / email / browser views).
-	if (keypadVisible && _uiclData->dialPadSlots[12].destRect.contains(chunkMouse)) {
+	if (keypadVisible && isDialKeyActive(12) &&
+			_uiclData->dialPadSlots[12].destRect.contains(chunkMouse)) {
 		g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
 
 		if (input.input & NancyInput::kLeftMouseButtonUp) {
@@ -2822,7 +2844,7 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 	if (keypadVisible) {
 		for (uint i = 0; i < UICL::kNumDialPadSlots; ++i) {
 			const UICL::DialPadSlot &slot = _uiclData->dialPadSlots[i];
-			if (slot.destRect.contains(chunkMouse)) {
+			if (slot.destRect.contains(chunkMouse) && isDialKeyActive(i)) {
 				newHovered = (int)i;
 				break;
 			}
diff --git a/engines/nancy/ui/cellphonepopup.h b/engines/nancy/ui/cellphonepopup.h
index c3395fc6130..3c9003533d1 100644
--- a/engines/nancy/ui/cellphonepopup.h
+++ b/engines/nancy/ui/cellphonepopup.h
@@ -220,6 +220,12 @@ private:
 
 	void resetDialPad();
 	void enterScreenState(ScreenState newState);
+	// With no signal the phone locks to "Old Email Only": on the welcome,
+	// dialing and online-hub screens every keypad key is dead except Menu
+	// (slot 13), which still reaches the e-mail list. Digits, *, #, Talk, Dir
+	// and the Help "?" go inert. The directory keeps its keys so the reachable
+	// e-mail path still works.
+	bool isDialKeyActive(uint slot) const;
 	// True while a player-placed call is ringing / waiting for pickup, so the
 	// connecting strip shows a Back button (subButtons[0]) that cancels it.
 	// Incoming calls have no Back button.




More information about the Scummvm-git-logs mailing list