[Scummvm-git-logs] scummvm master -> 95e82d6bbb7c60dcc12f85822e4643a260f96d9d
bluegr
noreply at scummvm.org
Sun May 24 13:31:39 UTC 2026
This automated email contains information about 10 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
769ce400e9 NANCY: Throw a warning when a terse caption text can't be found
328f5dcdec NANCY: Add information about some of the fields in SecondaryVideo
41b6c1d9ba NANCY: Add some more cursor enums for Nancy10+
709cee8136 NANCY: Set the hover cursor to "talk" instead of "eyeglass" in Nancy10+
12086db3a6 NANCY: Correct handling of default action records
5dad193549 NANCY: More work on the cell phone popup in Nancy10+
fed5429270 NANCY: Implement ARs related to the cellphone battery and directory
df3a4c95a1 NANCY: Persist cell phone data for Nancy10+
a42b87c336 NANCY: Map notification state for TASK buttons
95e82d6bbb NANCY: Fix regression in debug command scan_ar_type
Commit: 769ce400e97db8217551719cd12279da4dfcaa09
https://github.com/scummvm/scummvm/commit/769ce400e97db8217551719cd12279da4dfcaa09
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-05-24T16:31:23+03:00
Commit Message:
NANCY: Throw a warning when a terse caption text can't be found
Will help us identify the cases where this happens more easily
Changed paths:
engines/nancy/action/conversation.cpp
diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index d2622ddc3e0..6ece9bbf0ff 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -187,7 +187,12 @@ void ConversationSound::readTerseCaptionText(Common::SeekableReadStream &stream)
// WORKAROUND: Return an empty string for captions that aren't found.
// Happens with some conversations in Nancy10 (e.g. when calling the Rawleys).
- _text = convo->texts.getValOrDefault(key, "");
+ if (convo->texts.contains(key)) {
+ _text = convo->texts[key];
+ } else {
+ warning("Convo key not found: %s", key.c_str());
+ _text = "";
+ }
}
void ConversationSound::readTerseResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
Commit: 328f5dcdec3bd45767a685c9697803e17d820c85
https://github.com/scummvm/scummvm/commit/328f5dcdec3bd45767a685c9697803e17d820c85
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-05-24T16:31:23+03:00
Commit Message:
NANCY: Add information about some of the fields in SecondaryVideo
Changed paths:
engines/nancy/action/secondaryvideo.cpp
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index c6f7da9d583..808362db49e 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -178,6 +178,8 @@ void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
_sceneChange.readData(stream, ser.getVersion() == kGameTypeVampire);
ser.skip(1, kGameTypeNancy1);
+ // Count of extra 6-byte entries that sit between the header and the
+ // video descs in the original layout. Always 0 in practice.
ser.skip(2, kGameTypeNancy10);
uint16 numVideoDescs = 0;
@@ -186,6 +188,12 @@ void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
for (uint i = 0; i < numVideoDescs; ++i) {
_videoDescs[i].readData(stream);
}
+
+ // Nancy 10+ tail: 16 trailing bytes after the video descs. Purpose
+ // not yet identified.
+ if (g_nancy->getGameType() >= kGameTypeNancy10) {
+ stream.skip(16);
+ }
}
void PlaySecondaryVideo::execute() {
Commit: 41b6c1d9bab22d11fc86fe907f17f2a5643f723b
https://github.com/scummvm/scummvm/commit/41b6c1d9bab22d11fc86fe907f17f2a5643f723b
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-05-24T16:31:24+03:00
Commit Message:
NANCY: Add some more cursor enums for Nancy10+
Changed paths:
engines/nancy/cursor.cpp
engines/nancy/cursor.h
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 4e81f2d600a..772589a4c13 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -154,6 +154,7 @@ uint CursorManager::resolveNancy10CursorID(CursorType type, int16 itemID) {
switch (type) {
case kNormal: return kNewNormal;
case kHotspot: return kNewHotspot;
+ case kHotspotTalk: return kNewHotspotTalk;
case kNormalArrow: return kNewNormalArrow;
case kHotspotArrow: return kNewHotspotArrow;
case kExit: return kNewExit;
diff --git a/engines/nancy/cursor.h b/engines/nancy/cursor.h
index 690d362aa3f..fccbcf47f68 100644
--- a/engines/nancy/cursor.h
+++ b/engines/nancy/cursor.h
@@ -55,12 +55,19 @@ public:
kCustom2Hotspot = 19,
kNormalArrow = 20,
kHotspotArrow = 21,
+ kHotspotTalk = 22, // Speech-bubble hover cursor (Nancy 10+)
// Cursors in Nancy10 and newer games. Each cursor type stores
// two consecutive entries in the chunk: an idle slot at
// (type * 2) and a hotspot/highlighted slot at (type * 2 + 1).
kNewNormal = 0, // Type 0 idle â Eyeglass
kNewHotspot = 1, // Type 0 hotspot â Eyeglass highlighted (only "hotspot" variant we expose)
+ kNewUse = 2, // Type 1 idle â Used for interaction with characters and objects
+ kNewHotspotUse = 3, // Type 1 hotspot
+ kNewLockedUse = 4, // Type 2 idle
+ kNewHotspotLockedUse = 5, // Type 2 hotspot
+ kNewTalk = 6, // Type 3 idle â Speech-bubble (talking to characters)
+ kNewHotspotTalk = 7, // Type 3 hotspot
kNewNormalArrow = 8, // Type 4 idle â when the cursor is over the taskbar
kNewHotspotArrow = 9, // Type 4 hotspot
kNewExit = 10, // Type 5 idle â Used for movement and exiting puzzles
Commit: 709cee8136314d570f5d2180a4dcc9ee621a5f9f
https://github.com/scummvm/scummvm/commit/709cee8136314d570f5d2180a4dcc9ee621a5f9f
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-05-24T16:31:25+03:00
Commit Message:
NANCY: Set the hover cursor to "talk" instead of "eyeglass" in Nancy10+
Changed paths:
engines/nancy/action/secondaryvideo.h
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index aab64bf0f3b..9117dedb011 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -22,6 +22,8 @@
#ifndef NANCY_ACTION_SECONDARYVIDEO_H
#define NANCY_ACTION_SECONDARYVIDEO_H
+#include "engines/nancy/cursor.h"
+#include "engines/nancy/nancy.h"
#include "engines/nancy/video.h"
#include "engines/nancy/action/actionrecord.h"
@@ -71,6 +73,10 @@ public:
bool canHaveHotspot() const override { return true; }
bool isViewportRelative() const override { return true; }
+ CursorManager::CursorType getHoverCursor() const override {
+ return g_nancy->getGameType() >= kGameTypeNancy10 ? CursorManager::kHotspotTalk : CursorManager::kHotspot;
+ }
+
protected:
Common::String getRecordTypeName() const override { return "PlaySecondaryVideo"; }
Commit: 12086db3a69ada9865cd8b1fbe3cf3425fed7505
https://github.com/scummvm/scummvm/commit/12086db3a69ada9865cd8b1fbe3cf3425fed7505
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-05-24T16:31:26+03:00
Commit Message:
NANCY: Correct handling of default action records
Default action records are like "else" cases for the previously
executed action record. We were erroneously treating them as a
default switch case, so we were checking them against ALL the scene
action records, which was not right.
This fixes cases scenes that rely on the default AR
Fix #16783, #16795, #16796
Changed paths:
engines/nancy/action/actionmanager.cpp
engines/nancy/action/actionmanager.h
diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index cb161d77ba7..453179b3479 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -240,7 +240,6 @@ ActionRecord *ActionManager::createAndLoadNewRecord(Common::SeekableReadStream &
}
void ActionManager::processActionRecords() {
- _recordsWereExecuted = false;
_activatedRecordsThisFrame.clear();
for (auto record : _records) {
@@ -251,7 +250,7 @@ void ActionManager::processActionRecords() {
// Process dependencies every call. We make sure to ignore cursor dependencies,
// as they are only handled when calling from handleInput()
processDependency(record->_dependencies, *record, record->canHaveHotspot());
- record->_isActive = record->_dependencies.satisfied;
+ record->_isActive = _previousRecordWasExecuted = record->_dependencies.satisfied;
if (record->_isActive) {
if(record->_state == ActionRecord::kBegin) {
@@ -259,7 +258,6 @@ void ActionManager::processActionRecords() {
}
record->execute();
- _recordsWereExecuted = true;
}
if (g_nancy->getGameType() >= kGameTypeNancy4 && NancySceneState.getState() == State::Scene::kLoad) {
@@ -560,7 +558,7 @@ void ActionManager::processDependency(DependencyRecord &dep, ActionRecord &recor
break;
case DependencyType::kDefaultAR:
- dep.satisfied = !_recordsWereExecuted;
+ dep.satisfied = !_previousRecordWasExecuted;
break;
default:
warning("Unimplemented Dependency type %i", (int)dep.type);
@@ -575,7 +573,7 @@ void ActionManager::clearActionRecords() {
}
_records.clear();
_activatedRecordsThisFrame.clear();
- _recordsWereExecuted = false;
+ _previousRecordWasExecuted = false;
}
void ActionManager::onPause(bool pause) {
diff --git a/engines/nancy/action/actionmanager.h b/engines/nancy/action/actionmanager.h
index 4aa946963d7..23bafa7e4c7 100644
--- a/engines/nancy/action/actionmanager.h
+++ b/engines/nancy/action/actionmanager.h
@@ -79,7 +79,7 @@ protected:
void debugDrawHotspots();
- bool _recordsWereExecuted = false; // Used for kDefaultAR dependency
+ bool _previousRecordWasExecuted = false;
Common::Array<ActionRecord *> _activatedRecordsThisFrame;
};
Commit: 5dad193549de49b2207f2a959b8b0c3dfef4f8db
https://github.com/scummvm/scummvm/commit/5dad193549de49b2207f2a959b8b0c3dfef4f8db
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-05-24T16:31:26+03:00
Commit Message:
NANCY: More work on the cell phone popup in Nancy10+
- Implemented battery level and signal strength
- Changed contact list to be mutable
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 e8798a4f7a5..c40500bad0f 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -70,6 +70,8 @@ void CellPhonePopup::init() {
bounds.moveTo(0, 0);
_drawSurface.create(bounds.width(), bounds.height(), g_nancy->_graphics->getInputPixelFormat());
+ _contacts = _uiclData->contacts;
+
_screenState = kWelcome;
_dialedNumber.clear();
_resolvedContact = -1;
@@ -99,6 +101,36 @@ void CellPhonePopup::setNoSignal(bool noSignal) {
}
}
+void CellPhonePopup::setBatteryLow(bool low) {
+ if (_batteryLow == low) {
+ return;
+ }
+ _batteryLow = low;
+ if (_isVisible) {
+ drawScreenContent();
+ }
+}
+
+void CellPhonePopup::upsertContact(const UICL::Contact &c) {
+ // Match against the 11-byte dial pattern (prefix[2..12]). If an entry
+ // already carries that pattern, overwrite it; otherwise append.
+ for (uint i = 0; i < _contacts.size(); ++i) {
+ if (memcmp(_contacts[i].unknownPrefix + 2,
+ c.unknownPrefix + 2, 11) == 0) {
+ _contacts[i] = c;
+ if (_isVisible && _screenState == kDirectory) {
+ drawScreenContent();
+ }
+ return;
+ }
+ }
+
+ _contacts.push_back(c);
+ if (_isVisible && _screenState == kDirectory) {
+ drawScreenContent();
+ }
+}
+
void CellPhonePopup::open() {
if (_isVisible) {
return;
@@ -193,7 +225,7 @@ void CellPhonePopup::updateGraphics() {
// connecting sprite stays on screen for the duration of the
// conversation. AR 128 closes the popup when the call ends.
if (_resolvedContact >= 0 &&
- _resolvedContact < (int)_uiclData->contacts.size()) {
+ _resolvedContact < (int)_contacts.size()) {
triggerContactCallSceneChange((uint)_resolvedContact);
_resolvedContact = -1;
resetDialPad();
@@ -291,8 +323,11 @@ void CellPhonePopup::drawStatusIcons() {
_uiclData->signalSpriteDest.top - chunkOrigin.y));
}
- if (!_uiclData->batterySpriteSrc.isEmpty() && !_uiclData->batterySpriteDest.isEmpty()) {
- _drawSurface.blitFrom(_spritesImage, _uiclData->batterySpriteSrc,
+ const Common::Rect &batterySrc = _batteryLow && !_uiclData->batterySpriteSrcAlt.isEmpty()
+ ? _uiclData->batterySpriteSrcAlt
+ : _uiclData->batterySpriteSrc;
+ if (!batterySrc.isEmpty() && !_uiclData->batterySpriteDest.isEmpty()) {
+ _drawSurface.blitFrom(_spritesImage, batterySrc,
Common::Point(_uiclData->batterySpriteDest.left - chunkOrigin.x,
_uiclData->batterySpriteDest.top - chunkOrigin.y));
}
@@ -470,9 +505,9 @@ void CellPhonePopup::drawDirectoryList() {
uint visited = 0;
for (uint contactIdx = 0;
- contactIdx < _uiclData->contacts.size() && visibleRow < maxRows;
+ contactIdx < _contacts.size() && visibleRow < maxRows;
++contactIdx) {
- const UICL::Contact &c = _uiclData->contacts[contactIdx];
+ const UICL::Contact &c = _contacts[contactIdx];
if (c.name.empty() || !isContactVisible(c)) {
continue;
}
@@ -620,8 +655,8 @@ int CellPhonePopup::findContactByDialBuffer() const {
// Dial pattern lives in prefix[2..], terminated by '\n'.
const uint dialLen = _dialedNumber.size();
const uint kDialOffset = 2;
- for (uint i = 0; i < _uiclData->contacts.size(); ++i) {
- const UICL::Contact &c = _uiclData->contacts[i];
+ for (uint i = 0; i < _contacts.size(); ++i) {
+ const UICL::Contact &c = _contacts[i];
if (!isContactVisible(c)) {
continue;
}
@@ -643,11 +678,11 @@ int CellPhonePopup::findContactByDialBuffer() const {
}
void CellPhonePopup::triggerContactCallSceneChange(uint contactIndex) {
- if (contactIndex >= _uiclData->contacts.size()) {
+ if (contactIndex >= _contacts.size()) {
return;
}
- const UICL::Contact &c = _uiclData->contacts[contactIndex];
+ const UICL::Contact &c = _contacts[contactIndex];
const uint16 sceneID = (uint16)c.unknownSuffix[0] | ((uint16)c.unknownSuffix[1] << 8);
if (sceneID == kNoScene) {
@@ -763,8 +798,8 @@ int CellPhonePopup::contactIndexForVisibleRow(uint visibleRow) const {
Common::Array<Common::String> seenNames;
uint visited = 0;
uint visibleSoFar = 0;
- for (uint i = 0; i < _uiclData->contacts.size(); ++i) {
- const UICL::Contact &c = _uiclData->contacts[i];
+ for (uint i = 0; i < _contacts.size(); ++i) {
+ const UICL::Contact &c = _contacts[i];
if (c.name.empty() || !isContactVisible(c)) {
continue;
}
@@ -845,10 +880,10 @@ uint CellPhonePopup::directoryRowAt(const Common::Point &chunkMouse) const {
}
void CellPhonePopup::startCallToContact(uint contactIndex) {
- if (contactIndex >= _uiclData->contacts.size()) {
+ if (contactIndex >= _contacts.size()) {
return;
}
- const UICL::Contact &c = _uiclData->contacts[contactIndex];
+ const UICL::Contact &c = _contacts[contactIndex];
// Rebuild _dialedNumber so the call flow's lookup matches.
_dialedNumber.clear();
@@ -871,8 +906,8 @@ void CellPhonePopup::startCallToContact(uint contactIndex) {
uint CellPhonePopup::deduplicatedContactCount() const {
Common::Array<Common::String> seen;
- for (uint i = 0; i < _uiclData->contacts.size(); ++i) {
- const UICL::Contact &c = _uiclData->contacts[i];
+ for (uint i = 0; i < _contacts.size(); ++i) {
+ const UICL::Contact &c = _contacts[i];
if (c.name.empty() || !isContactVisible(c)) {
continue;
}
diff --git a/engines/nancy/ui/cellphonepopup.h b/engines/nancy/ui/cellphonepopup.h
index 80a09e83066..884795c0e9a 100644
--- a/engines/nancy/ui/cellphonepopup.h
+++ b/engines/nancy/ui/cellphonepopup.h
@@ -49,9 +49,16 @@ public:
void toggle() { if (_isVisible) close(); else open(); }
// Swaps the welcome graphic for the No Signal / No Access / Old Email
- // Only labels and blocks outgoing calls. TODO: hook to scene flag.
+ // Only labels and blocks outgoing calls.
void setNoSignal(bool noSignal);
+ // Swaps the battery sprite for the low/dead variant.
+ void setBatteryLow(bool low);
+
+ // Insert or replace a contact (matched by its 11-byte dial pattern).
+ // Used by AR 130 to add/modify entries at runtime.
+ void upsertContact(const UICL::Contact &c);
+
private:
enum ScreenState : int {
kWelcome = 0,
@@ -110,6 +117,10 @@ private:
const UICL *_uiclData;
+ // Runtime contact list, seeded from _uiclData->contacts and then
+ // mutable (AR 130 inserts/replaces entries).
+ Common::Array<UICL::Contact> _contacts;
+
// Chrome (header.imageName) and sprite atlas (overlayImageName).
Graphics::ManagedSurface _overlayImage;
Graphics::ManagedSurface _spritesImage;
@@ -134,6 +145,7 @@ private:
uint _directorySelection = 0;
bool _noSignal = false;
+ bool _batteryLow = false;
};
} // End of namespace UI
Commit: fed54292709060541061a5564c3a15d69d43b049
https://github.com/scummvm/scummvm/commit/fed54292709060541061a5564c3a15d69d43b049
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-05-24T16:31:27+03:00
Commit Message:
NANCY: Implement ARs related to the cellphone battery and directory
Implement the following ARs:
- SetCellPhoneBatteryAndSignal
- ChangeCellPhoneInfo
Will check if these need to persist in saved games during playthrough
and testing
Changed paths:
engines/nancy/action/arfactory.cpp
engines/nancy/action/miscrecords.cpp
engines/nancy/action/miscrecords.h
diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 0fb762b9254..9f3fe55fbf2 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -335,10 +335,12 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
case 128:
// Nancy 10+
return new CellPhonePopCellSceneFromStack();
+ case 129:
+ // Nancy 10+
+ return new SetCellPhoneBatteryAndSignal();
case 130:
// Nancy 10+
- warning("ChangeCellPhoneInfo - not implemented yet");
- return nullptr;
+ return new ChangeCellPhoneInfo();
case 131:
// Nancy 10+
return new AddSearchLink();
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index b0663a851cf..5c373c50fb5 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -238,6 +238,40 @@ void AddSearchLink::execute() {
finishExecution();
}
+void SetCellPhoneBatteryAndSignal::readData(Common::SeekableReadStream &stream) {
+ _mode = stream.readUint16LE();
+}
+
+void SetCellPhoneBatteryAndSignal::execute() {
+ UI::CellPhonePopup &popup = NancySceneState.getCellPhonePopup();
+ switch (_mode) {
+ case 0: popup.setBatteryLow(false); break;
+ case 1: popup.setBatteryLow(true); break;
+ case 2: popup.setNoSignal(false); break;
+ case 3: popup.setNoSignal(true); break;
+ default:
+ warning("SetCellPhoneBatteryAndSignal: unknown mode %u", _mode);
+ break;
+ }
+ finishExecution();
+}
+
+void ChangeCellPhoneInfo::readData(Common::SeekableReadStream &stream) {
+ stream.read(_contact.unknownPrefix, sizeof(_contact.unknownPrefix));
+
+ char nameBuf[21];
+ stream.read(nameBuf, 20);
+ nameBuf[20] = '\0';
+ _contact.name = nameBuf;
+
+ stream.read(_contact.unknownSuffix, sizeof(_contact.unknownSuffix));
+}
+
+void ChangeCellPhoneInfo::execute() {
+ NancySceneState.getCellPhonePopup().upsertContact(_contact);
+ finishExecution();
+}
+
void CellPhonePopCellSceneFromStack::readData(Common::SeekableReadStream &stream) {
_sceneChange.readData(stream);
}
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index 1bf6885b2b1..ed6cd179032 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -23,6 +23,7 @@
#define NANCY_ACTION_RECORDTYPES_H
#include "engines/nancy/action/actionrecord.h"
+#include "engines/nancy/enginedata.h"
namespace Nancy {
@@ -187,6 +188,33 @@ protected:
Common::String getRecordTypeName() const override { return "AddSearchLink"; }
};
+// Sets the cellphone's battery/signal indicators. Modes 0/1 toggle the
+// battery (normal / low) and 2/3 toggle the signal (normal / no signal).
+class SetCellPhoneBatteryAndSignal : public ActionRecord {
+public:
+ void readData(Common::SeekableReadStream &stream) override;
+ void execute() override;
+
+ uint16 _mode = 0;
+
+protected:
+ Common::String getRecordTypeName() const override { return "SetCellPhoneBatteryAndSignal"; }
+};
+
+// Adds a new entry to the cellphone directory, or overwrites an existing
+// one matched by dial pattern. Used to unlock contacts as the player
+// progresses (Nancy 10+).
+class ChangeCellPhoneInfo : public ActionRecord {
+public:
+ void readData(Common::SeekableReadStream &stream) override;
+ void execute() override;
+
+ UICL::Contact _contact;
+
+protected:
+ Common::String getRecordTypeName() const override { return "ChangeCellPhoneInfo"; }
+};
+
// Returns from a cellphone-driven conversation scene to the pre-call scene.
// sceneID == kNoScene pops the saved scene; any other sceneID overrides it.
class CellPhonePopCellSceneFromStack : public ActionRecord {
Commit: df3a4c95a1ad0151ae7be1667be8ac1e72463f8d
https://github.com/scummvm/scummvm/commit/df3a4c95a1ad0151ae7be1667be8ac1e72463f8d
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-05-24T16:31:27+03:00
Commit Message:
NANCY: Persist cell phone data for Nancy10+
Changed paths:
engines/nancy/puzzledata.cpp
engines/nancy/puzzledata.h
engines/nancy/ui/cellphonepopup.cpp
diff --git a/engines/nancy/puzzledata.cpp b/engines/nancy/puzzledata.cpp
index cd6076c71d8..5d1781dee78 100644
--- a/engines/nancy/puzzledata.cpp
+++ b/engines/nancy/puzzledata.cpp
@@ -277,6 +277,37 @@ float TableData::getComboValue(uint16 index) const {
return index < comboValues.size() ? comboValues[index] : kNoTableValue;
}
+void CellPhoneData::synchronize(Common::Serializer &ser) {
+ ser.syncAsByte(noSignal);
+ ser.syncAsByte(batteryLow);
+ ser.syncAsByte(seeded);
+
+ uint16 numContacts = (uint16)contacts.size();
+ ser.syncAsUint16LE(numContacts);
+
+ if (ser.isLoading()) {
+ contacts.resize(numContacts);
+ }
+
+ char nameBuf[21];
+ for (uint16 i = 0; i < numContacts; ++i) {
+ UICL::Contact &c = contacts[i];
+ ser.syncBytes(c.unknownPrefix, sizeof(c.unknownPrefix));
+
+ if (ser.isSaving()) {
+ memset(nameBuf, 0, sizeof(nameBuf));
+ Common::strlcpy(nameBuf, c.name.c_str(), sizeof(nameBuf));
+ }
+ ser.syncBytes((byte *)nameBuf, 20);
+ if (ser.isLoading()) {
+ nameBuf[20] = '\0';
+ c.name = nameBuf;
+ }
+
+ ser.syncBytes(c.unknownSuffix, sizeof(c.unknownSuffix));
+ }
+}
+
PuzzleData *makePuzzleData(const uint32 tag) {
switch(tag) {
case SliderPuzzleData::getTag():
@@ -297,6 +328,8 @@ PuzzleData *makePuzzleData(const uint32 tag) {
return new JournalData();
case TableData::getTag():
return new TableData();
+ case CellPhoneData::getTag():
+ return new CellPhoneData();
default:
return nullptr;
}
diff --git a/engines/nancy/puzzledata.h b/engines/nancy/puzzledata.h
index bf11859d3ea..d5df60db10c 100644
--- a/engines/nancy/puzzledata.h
+++ b/engines/nancy/puzzledata.h
@@ -24,6 +24,7 @@
#include "common/hashmap.h"
#include "engines/nancy/commontypes.h"
+#include "engines/nancy/enginedata.h"
#ifndef NANCY_PUZZLEDATA_H
#define NANCY_PUZZLEDATA_H
@@ -167,6 +168,23 @@ struct TableData : public PuzzleData {
Common::Array<float> comboValues;
};
+// Nancy 10+ cellphone state mutated by the ChangeCellPhoneInfo and
+// SetCellPhoneBatteryAndSignal action records, persisted between saves.
+struct CellPhoneData : public PuzzleData {
+ CellPhoneData() {}
+ virtual ~CellPhoneData() {}
+
+ static constexpr uint32 getTag() { return MKTAG('C', 'E', 'L', 'L'); }
+ virtual void synchronize(Common::Serializer &ser);
+
+ bool noSignal = false;
+ bool batteryLow = false;
+ // Loaded set to true once the popup has seeded the contact list from
+ // the UICL chunk; we then own it as runtime data.
+ bool seeded = false;
+ Common::Array<UICL::Contact> contacts;
+};
+
PuzzleData *makePuzzleData(const uint32 tag);
} // End of namespace Nancy
diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index c40500bad0f..0de5e463f4e 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -24,6 +24,7 @@
#include "engines/nancy/graphics.h"
#include "engines/nancy/input.h"
#include "engines/nancy/nancy.h"
+#include "engines/nancy/puzzledata.h"
#include "engines/nancy/resource.h"
#include "engines/nancy/sound.h"
@@ -70,7 +71,21 @@ void CellPhonePopup::init() {
bounds.moveTo(0, 0);
_drawSurface.create(bounds.width(), bounds.height(), g_nancy->_graphics->getInputPixelFormat());
- _contacts = _uiclData->contacts;
+ // Persistent state lives in CellPhoneData (saved across the game).
+ // First-time init seeds the runtime contact list from the chunk;
+ // subsequent inits (e.g. after a load) restore the saved state.
+ CellPhoneData *cellData = (CellPhoneData *)NancySceneState.getPuzzleData(CellPhoneData::getTag());
+ if (cellData) {
+ if (!cellData->seeded) {
+ cellData->contacts = _uiclData->contacts;
+ cellData->seeded = true;
+ }
+ _contacts = cellData->contacts;
+ _noSignal = cellData->noSignal;
+ _batteryLow = cellData->batteryLow;
+ } else {
+ _contacts = _uiclData->contacts;
+ }
_screenState = kWelcome;
_dialedNumber.clear();
@@ -96,6 +111,10 @@ void CellPhonePopup::setNoSignal(bool noSignal) {
return;
}
_noSignal = noSignal;
+ CellPhoneData *cellData = (CellPhoneData *)NancySceneState.getPuzzleData(CellPhoneData::getTag());
+ if (cellData) {
+ cellData->noSignal = noSignal;
+ }
if (_isVisible) {
drawScreenContent();
}
@@ -106,6 +125,10 @@ void CellPhonePopup::setBatteryLow(bool low) {
return;
}
_batteryLow = low;
+ CellPhoneData *cellData = (CellPhoneData *)NancySceneState.getPuzzleData(CellPhoneData::getTag());
+ if (cellData) {
+ cellData->batteryLow = low;
+ }
if (_isVisible) {
drawScreenContent();
}
@@ -114,18 +137,24 @@ void CellPhonePopup::setBatteryLow(bool low) {
void CellPhonePopup::upsertContact(const UICL::Contact &c) {
// Match against the 11-byte dial pattern (prefix[2..12]). If an entry
// already carries that pattern, overwrite it; otherwise append.
+ bool replaced = false;
for (uint i = 0; i < _contacts.size(); ++i) {
if (memcmp(_contacts[i].unknownPrefix + 2,
c.unknownPrefix + 2, 11) == 0) {
_contacts[i] = c;
- if (_isVisible && _screenState == kDirectory) {
- drawScreenContent();
- }
- return;
+ replaced = true;
+ break;
}
}
+ if (!replaced) {
+ _contacts.push_back(c);
+ }
+
+ CellPhoneData *cellData = (CellPhoneData *)NancySceneState.getPuzzleData(CellPhoneData::getTag());
+ if (cellData) {
+ cellData->contacts = _contacts;
+ }
- _contacts.push_back(c);
if (_isVisible && _screenState == kDirectory) {
drawScreenContent();
}
@@ -136,6 +165,14 @@ void CellPhonePopup::open() {
return;
}
+ // Re-pull persistent state in case a save was loaded after init().
+ CellPhoneData *cellData = (CellPhoneData *)NancySceneState.getPuzzleData(CellPhoneData::getTag());
+ if (cellData && cellData->seeded) {
+ _contacts = cellData->contacts;
+ _noSignal = cellData->noSignal;
+ _batteryLow = cellData->batteryLow;
+ }
+
_screenState = kWelcome;
_dialedNumber.clear();
_resolvedContact = -1;
Commit: a42b87c33626e0e0bee0019c883b9d19fb547a04
https://github.com/scummvm/scummvm/commit/a42b87c33626e0e0bee0019c883b9d19fb547a04
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-05-24T16:31:28+03:00
Commit Message:
NANCY: Map notification state for TASK buttons
Changed paths:
engines/nancy/enginedata.cpp
engines/nancy/enginedata.h
diff --git a/engines/nancy/enginedata.cpp b/engines/nancy/enginedata.cpp
index 749e49db028..2c029526ba6 100644
--- a/engines/nancy/enginedata.cpp
+++ b/engines/nancy/enginedata.cpp
@@ -895,7 +895,7 @@ TASK::TASK(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
char nameBuf[34];
for (uint i = 0; i < kNumButtons; ++i) {
readUIButton(*chunkStream, buttons[i].button);
- chunkStream->read(buttons[i].unknownPad, sizeof(buttons[i].unknownPad));
+ readRect(*chunkStream, buttons[i].notificationSrcRect);
for (uint s = 0; s < kNumAltSounds; ++s) {
chunkStream->read(nameBuf, 33);
nameBuf[33] = '\0';
diff --git a/engines/nancy/enginedata.h b/engines/nancy/enginedata.h
index cbac93d172a..c4310365ae0 100644
--- a/engines/nancy/enginedata.h
+++ b/engines/nancy/enginedata.h
@@ -526,7 +526,10 @@ enum TaskButton {
struct TASK : public EngineData {
struct ButtonRecord {
UIButtonRecord button;
- byte unknownPad[16];
+ // Source rect for the notification sprite (the badge shown on
+ // the middle three buttons when the popup has new content).
+ // Empty for buttons that don't carry a notification.
+ Common::Rect notificationSrcRect;
Common::String clickSoundName[3];
};
Commit: 95e82d6bbb7c60dcc12f85822e4643a260f96d9d
https://github.com/scummvm/scummvm/commit/95e82d6bbb7c60dcc12f85822e4643a260f96d9d
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-05-24T16:31:29+03:00
Commit Message:
NANCY: Fix regression in debug command scan_ar_type
Fix loading consecutive lists from ResourceManager::list().
A regression from 7eeff19.
Changed paths:
engines/nancy/resource.cpp
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 986f5127404..7c2bdedf21d 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -301,14 +301,16 @@ void ResourceManager::list(const Common::String &treeName, Common::Array<Common:
if (!tree) {
return;
}
- outList = tree->getPathsForType(type);
+ Common::Array<Common::Path> result = tree->getPathsForType(type);
+ outList.insert_at(outList.size(), result);
} else {
for (uint i = 0; i < _cifTreeNames.size(); ++i) {
// No provided tree name, check inside every loaded tree
Common::String upper = _cifTreeNames[i];
upper.toUppercase();
const CifTree *tree = (const CifTree *)SearchMan.getArchive(treePrefix + upper);
- outList = tree->getPathsForType(type);
+ Common::Array<Common::Path> result = tree->getPathsForType(type);
+ outList.insert_at(outList.size(), result);
}
}
}
More information about the Scummvm-git-logs
mailing list