[Scummvm-git-logs] scummvm master -> 09764a18ed56986582f47a929b3477f988f97827

bluegr noreply at scummvm.org
Sat Jun 6 09:41:35 UTC 2026


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

Summary:
07a5fb57b8 NANCY: Use a helper method for case insensitive key matching in Nancy10
09764a18ed NANCY: Fix making phone calls within a closeup scene in Nancy10


Commit: 07a5fb57b8740ac3197b91217c243772801bbc55
    https://github.com/scummvm/scummvm/commit/07a5fb57b8740ac3197b91217c243772801bbc55
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-06T12:41:24+03:00

Commit Message:
NANCY: Use a helper method for case insensitive key matching in Nancy10

Allows us to remove relevant hacks. Fixes viewing the trunk in Mary's
Gifts.

Changed paths:
    engines/nancy/action/conversation.cpp
    engines/nancy/action/miscrecords.cpp
    engines/nancy/action/soundrecords.cpp
    engines/nancy/ui/cellphonepopup.cpp
    engines/nancy/ui/notebookpopup.cpp
    engines/nancy/util.cpp
    engines/nancy/util.h


diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index 5989b549e1f..4c4a4a609e8 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -185,24 +185,7 @@ void ConversationSound::readTerseCaptionText(Common::SeekableReadStream &stream)
 	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
 	assert(convo);
 
-	if (convo->texts.contains(key)) {
-		_text = convo->texts[key];
-	} else {
-		// Nancy10+ searched convo texts in a key insensitive way, but
-		// the possible permutations involve mainly the last character
-		// being upper or lower case, so just try that before giving up.
-		if (key[key.size() - 1] == toupper(key[key.size() - 1]))
-			key[key.size() - 1] = tolower(key[key.size() - 1]);
-		else
-			key[key.size() - 1] = toupper(key[key.size() - 1]);
-
-		if (convo->texts.contains(key)) {
-			_text = convo->texts[key];
-		} else {
-			warning("Convo key not found: %s", key.c_str());
-			_text = "";
-		}
-	}
+	_text = getTextFromCaseInsensitiveKey(convo->texts, key);
 }
 
 void ConversationSound::readTerseResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
@@ -212,7 +195,7 @@ void ConversationSound::readTerseResponseText(Common::SeekableReadStream &stream
 	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
 	assert(convo);
 
-	response.text = convo->texts[key];
+	response.text = getTextFromCaseInsensitiveKey(convo->texts, key);
 }
 
 void ConversationSound::execute() {
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index cacb72796aa..259b1320083 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -116,7 +116,7 @@ void TextBoxWrite::readData(Common::SeekableReadStream &stream) {
 		const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
 		assert(autotext);
 
-		_text = autotext->texts[stringID];
+		_text = getTextFromCaseInsensitiveKey(autotext->texts, stringID);
 	} else {
 		char *buf = new char[size];
 		stream.read(buf, size);
@@ -159,10 +159,7 @@ void FrameTextBox::readData(Common::SeekableReadStream &stream) {
 		const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
 		assert(autotext);
 
-		// TODO: we probably ought to be doing something more robust here
-		// to detect missing keys, but for now just return an empty string
-		// if the key isn't found.
-		_text = autotext->texts.getValOrDefault(stringID, "");
+		_text = getTextFromCaseInsensitiveKey(autotext->texts, stringID);
 	} else if (size > 0) {
 		char *buf = new char[size];
 		stream.read(buf, size);
diff --git a/engines/nancy/action/soundrecords.cpp b/engines/nancy/action/soundrecords.cpp
index c65cfd4ede6..c27435c3a5d 100644
--- a/engines/nancy/action/soundrecords.cpp
+++ b/engines/nancy/action/soundrecords.cpp
@@ -138,7 +138,7 @@ void PlaySoundCC::readCCText(Common::SeekableReadStream &stream, Common::String
 		const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
 		assert(autotext);
 
-		out = autotext->texts[key];
+		out = getTextFromCaseInsensitiveKey(autotext->texts, key);
 	}
 }
 
diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index 60e3f429414..577068cbb6f 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -728,10 +728,8 @@ void CellPhonePopup::drawLinkList() {
 		}
 
 		Common::String lookupKey = list[absolute].key;
-		lookupKey.toUppercase();
-		Common::String rowText = (autotext && autotext->texts.contains(lookupKey))
-			? autotext->texts[lookupKey]
-			: lookupKey;
+		Common::String rowText = getTextFromCaseInsensitiveKey(autotext->texts, lookupKey);
+
 		// Single-line draw — drop every <n> markup so they don't render as
 		// literal "<n>" glyphs and crowd the row.
 		while (rowText.contains("<n>")) {
@@ -765,9 +763,6 @@ void CellPhonePopup::drawContentView() {
 	}
 
 	const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
-	if (!autotext || !autotext->texts.contains(_contentKey)) {
-		return;
-	}
 
 	const Font *font = g_nancy->_graphics->getFont(_uiclData->fontId2);
 	if (!font) {
@@ -792,7 +787,7 @@ void CellPhonePopup::drawContentView() {
 
 	// Render the engine's hypertext markup into a tall scratch surface,
 	// then blit a vertically-scrolled window of it into the LCD.
-	const Common::String renderText = autotext->texts[_contentKey];
+	const Common::String renderText = getTextFromCaseInsensitiveKey(autotext->texts, _contentKey);
 
 	// Find this page in the UIBW chunk (browser pages only); its hotspot
 	// records are the per-page image table the article references.
diff --git a/engines/nancy/ui/notebookpopup.cpp b/engines/nancy/ui/notebookpopup.cpp
index 50d79fb3842..73037a51983 100644
--- a/engines/nancy/ui/notebookpopup.cpp
+++ b/engines/nancy/ui/notebookpopup.cpp
@@ -441,8 +441,6 @@ void NotebookPopup::buildTextLines() {
 		return;
 
 	const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
-	if (!autotext)
-		return;
 
 	// Senior-detective Tasks page: hide the to-do list and show the
 	// AUTOTEXT placeholder body instead.
@@ -459,13 +457,8 @@ void NotebookPopup::buildTextLines() {
 
 	const Common::Array<JournalData::Entry> &entries = journalData->journalEntries[surfaceID];
 	for (uint i = 0; i < entries.size(); ++i) {
-		const Common::String &stringID = entries[i].stringID;
-		if (!autotext->texts.contains(stringID))
-			continue;
-
-		Common::String body = autotext->texts[stringID];
-		if (body.empty())
-			continue;
+		Common::String stringID = entries[i].stringID;
+		Common::String body = getTextFromCaseInsensitiveKey(autotext->texts, stringID);
 
 		// Tasks are prefixed with a checkbox showing completion state.
 		// mark % 10 == 8 means "complete".
diff --git a/engines/nancy/util.cpp b/engines/nancy/util.cpp
index 971d90a6df4..6c9518635f1 100644
--- a/engines/nancy/util.cpp
+++ b/engines/nancy/util.cpp
@@ -344,6 +344,31 @@ void assembleTextLine(char *rawCaption, Common::String &output, uint size) {
 	}
 }
 
+Common::String getTextFromCaseInsensitiveKey(Common::HashMap<Common::String, Common::String> texts, Common::String &key) {
+	if (texts.contains(key)) {
+		return texts[key];
+	} else {
+		// Nancy10+ searched keyed texts in a key insensitive way, but
+		// the possible permutations involve mainly the last character
+		// being upper or lower case, so just try that before giving up.
+		if (key[key.size() - 1] == toupper(key[key.size() - 1]))
+			key[key.size() - 1] = tolower(key[key.size() - 1]);
+		else
+			key[key.size() - 1] = toupper(key[key.size() - 1]);
+
+		if (texts.contains(key))
+			return texts[key];
+
+		key.toUppercase();
+
+		if (texts.contains(key))
+			return texts[key];
+	}
+
+	warning("Key not found: %s", key.c_str());
+	return "";
+}
+
 bool DeferredLoader::load(uint32 endTime) {
 	uint32 loopStartTime = g_system->getMillis();
 	uint32 loopTime = 0; // Stores the loop that took the longest time to complete
diff --git a/engines/nancy/util.h b/engines/nancy/util.h
index 3dc6ab1eaf5..0977792e16f 100644
--- a/engines/nancy/util.h
+++ b/engines/nancy/util.h
@@ -22,6 +22,7 @@
 #define NANCY_UTIL_H
 
 #include "common/array.h"
+#include "common/hashmap.h"
 #include "common/path.h"
 #include "common/rect.h"
 #include "common/serializer.h"
@@ -64,6 +65,8 @@ void readUISlider(Common::SeekableReadStream &stream, UISliderRecord &dst);
 void readUIPopupHeader(Common::SeekableReadStream &stream, UIPopupHeader &dst);
 void readUIButtonSlot(Common::SeekableReadStream &stream, UIButtonSlot &dst);
 
+Common::String getTextFromCaseInsensitiveKey(Common::HashMap<Common::String, Common::String> texts, Common::String &key);
+
 // Abstract base class used for loading data that would take too much time in a single frame
 class DeferredLoader {
 public:


Commit: 09764a18ed56986582f47a929b3477f988f97827
    https://github.com/scummvm/scummvm/commit/09764a18ed56986582f47a929b3477f988f97827
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-06T12:41:26+03:00

Commit Message:
NANCY: Fix making phone calls within a closeup scene in Nancy10

The call phone's scene pushing/popping mechanism could clobber the
global scene stack. Add safeguards, so that doesn't happen.

Fixes making phone calls within a closeup scene, e.g. when calling
Charleena Purcell while looking at a closeup of the trunk in Mary's
Gifts.

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


diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 259b1320083..f3054b3afbe 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -219,8 +219,9 @@ void ControlUIItems::execute() {
 		case kUITypeNotebook:
 			NancySceneState.getNotebookPopup().open();
 			break;
-		case kUITypeCellphone:
-			NancySceneState.getCellPhonePopup().open();
+		case kUITypeCellphone: {
+			UI::CellPhonePopup &phone = NancySceneState.getCellPhonePopup();
+			phone.open();
 
 			if (_startScene != (int16)kNoScene) {
 				SceneChangeDescription scene;
@@ -230,11 +231,13 @@ void ControlUIItems::execute() {
 				// The destination scene's sound carries the conversation audio.
 				scene.continueSceneSound = kLoadSceneSound;
 
-				// Pushed so AR 128 in the conversation scene can return here.
-				NancySceneState.pushScene();
+				// Save the pre-call scene on the popup so AR 128 returns
+				// there without touching the global push slot.
+				phone.setReturnScene(NancySceneState.getSceneInfo());
 				NancySceneState.changeScene(scene);
 			}
 			break;
+		}
 		default:
 			break;
 		}
@@ -346,14 +349,22 @@ void CellPhonePopCellSceneFromStack::readData(Common::SeekableReadStream &stream
 }
 
 void CellPhonePopCellSceneFromStack::execute() {
-	if (_sceneChange.sceneID == kNoScene) {
-		NancySceneState.popScene(false);
-	} else {
+	UI::CellPhonePopup &phone = NancySceneState.getCellPhonePopup();
+
+	if (_sceneChange.sceneID != kNoScene) {
 		NancySceneState.changeScene(_sceneChange);
+	} else {
+		// Restore the pre-call scene if one was saved. If there's no saved
+		// scene (e.g. the conversation was entered without a phone call),
+		// do nothing — popping the global scene stack here would clobber
+		// closeup / inventory pushes that have nothing to do with the phone.
+		SceneChangeDescription returnScene;
+		if (phone.consumeReturnScene(returnScene))
+			NancySceneState.changeScene(returnScene);
 	}
 
 	// Conversation is over; take the phone down.
-	NancySceneState.getCellPhonePopup().close();
+	phone.close();
 
 	finishExecution();
 }
diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index 577068cbb6f..d9aab409861 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -1116,14 +1116,28 @@ void CellPhonePopup::triggerContactCallSceneChange(uint contactIndex) {
 										eventFlagValue ? g_nancy->_true : g_nancy->_false);
 	}
 
-	// Pushed so AR 128 in the conversation scene can return the player here.
-	NancySceneState.pushScene();
+	// Save the pre-call scene on the popup so AR 128 can return there
+	// without clobbering the global push slot (used by closeups, etc).
+	setReturnScene(NancySceneState.getSceneInfo());
 
 	NancySceneState.changeScene(scene);
 
 	// Phone stays on screen through the conversation; AR 128 closes it.
 }
 
+void CellPhonePopup::setReturnScene(const SceneChangeDescription &scene) {
+	_returnScene = scene;
+	_hasReturnScene = true;
+}
+
+bool CellPhonePopup::consumeReturnScene(SceneChangeDescription &out) {
+	if (!_hasReturnScene)
+		return false;
+	out = _returnScene;
+	_hasReturnScene = false;
+	return true;
+}
+
 // --------------------------------------------------------------------
 // Directory helpers
 // --------------------------------------------------------------------
diff --git a/engines/nancy/ui/cellphonepopup.h b/engines/nancy/ui/cellphonepopup.h
index 9a0353003bd..0536c135a7f 100644
--- a/engines/nancy/ui/cellphonepopup.h
+++ b/engines/nancy/ui/cellphonepopup.h
@@ -65,6 +65,12 @@ public:
 						const Common::String &value, int16 extra,
 						int16 flag, int16 eventFlag);
 
+	// Phone-call return scene. Set before jumping into a conversation scene
+	// so AR 128 (CellPhonePopCellSceneFromStack) can return there without
+	// trampling the global push slot used by closeups/inventory views.
+	void setReturnScene(const SceneChangeDescription &scene);
+	bool consumeReturnScene(SceneChangeDescription &out);
+
 private:
 	enum ScreenState : int {
 		kWelcome          = 0,
@@ -212,6 +218,9 @@ private:
 
 	bool _noSignal = false;
 	bool _batteryLow = false;
+
+	SceneChangeDescription _returnScene;
+	bool _hasReturnScene = false;
 };
 
 } // End of namespace UI




More information about the Scummvm-git-logs mailing list