[Scummvm-git-logs] scummvm master -> 4c4a37a61ed23e9a8b3b06c4a93619cb7016e748
bluegr
noreply at scummvm.org
Mon Jul 27 01:30:57 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
4c4a37a61e NANCY: NANCY10: Preserve secondary videos across NO_ART scene changes
Commit: 4c4a37a61ed23e9a8b3b06c4a93619cb7016e748
https://github.com/scummvm/scummvm/commit/4c4a37a61ed23e9a8b3b06c4a93619cb7016e748
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-07-27T04:30:50+03:00
Commit Message:
NANCY: NANCY10: Preserve secondary videos across NO_ART scene changes
A NO_ART_SCENE is a videoless scene that keeps the previous scene's
frame on screen and only overlays new logic (used, for example, by the
phone-call conversations in Nancy10 or newer). The original engine
clears such a scene, which wipes only the conversation/sound/phone
records and leaves the previous scene's ambient character videos
playing.
Scene::load now checks if the incoming scene is a NO_ART_SCENE, so that
current SecondaryMovie AR records are preserved. Older games do not
feature "no art" scenes, so they're unaffected.
Fixes SecondaryMovie characters disappearing during phone calls
Fix #17022
Changed paths:
engines/nancy/action/actionmanager.cpp
engines/nancy/action/actionmanager.h
engines/nancy/action/actionrecord.h
engines/nancy/action/secondarymovie.cpp
engines/nancy/action/secondarymovie.h
engines/nancy/action/secondaryvideo.h
engines/nancy/state/scene.cpp
engines/nancy/state/scene.h
diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 1c56956ae17..816b43b0c0a 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -604,9 +604,9 @@ void ActionManager::processDependency(DependencyRecord &dep, ActionRecord &recor
}
}
-void ActionManager::clearActionRecords() {
+void ActionManager::clearActionRecords(bool nextIsNoArt) {
for (auto it = _records.begin(); it != _records.end(); ) {
- if ((*it)->isPersistentAcrossScenes()) {
+ if ((*it)->survivesSceneChange(nextIsNoArt)) {
++it;
continue;
}
diff --git a/engines/nancy/action/actionmanager.h b/engines/nancy/action/actionmanager.h
index 23bafa7e4c7..171c01ec9db 100644
--- a/engines/nancy/action/actionmanager.h
+++ b/engines/nancy/action/actionmanager.h
@@ -62,7 +62,7 @@ public:
void addNewActionRecord(Common::SeekableReadStream &inputData);
Common::Array<ActionRecord *> &getActionRecords() { return _records; }
ActionRecord *getActionRecord(uint id) { if (id < _records.size()) return _records[id]; else return nullptr;}
- void clearActionRecords();
+ void clearActionRecords(bool nextIsNoArt = false);
void onPause(bool pause);
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index ca9bdaf0390..43d54e3c3e7 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -125,8 +125,15 @@ public:
// Used for handling kCursorType dependency
virtual bool canHaveHotspot() const { return false; }
- // Records returning true survive Scene::clearSceneData / ActionManager::clearActionRecords.
- virtual bool isPersistentAcrossScenes() const { return false; }
+ // Records returning true survive the upcoming scene change (i.e. are not
+ // deleted by Scene::clearSceneData / ActionManager::clearActionRecords).
+ // `nextSceneIsNoArt` is true when the incoming scene is a "NO_ART_SCENE": a
+ // videoless scene that keeps the previous scene's frame on screen and only
+ // overlays new logic. The original engine's ClearNoArtSceneData clears the
+ // conversation / sound / phone records but leaves the ambient character
+ // videos playing, so e.g. a character stays visible while a phone-call
+ // conversation runs in front of them.
+ virtual bool survivesSceneChange(bool nextSceneIsNoArt) const { return false; }
protected:
void finishExecution();
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 2f418ec4d23..3d907a4c677 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -57,11 +57,13 @@ PlaySecondaryMovie::~PlaySecondaryMovie() {
}
}
-bool PlaySecondaryMovie::isPersistentAcrossScenes() const {
+bool PlaySecondaryMovie::survivesSceneChange(bool nextSceneIsNoArt) const {
// Nancy11's random movies can be ambient loops that intentionally keep
// playing across scene changes. Nancy13's per-character reaction movies
// (AR 42) are scene-local: they must stop when their scene is left, and are
- // reloaded if it's re-entered.
+ // reloaded if it's re-entered. A plain (non-random) cinematic movie is
+ // self-contained and does not persist, not even into a NO_ART_SCENE â so the
+ // NO_ART flag is deliberately ignored here.
return _isRandom && g_nancy->getGameType() < kGameTypeNancy13 && !_isDone && !_randomStopRequested;
}
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index 29eccf57c52..0ff4af69121 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -169,7 +169,7 @@ public:
bool isViewportRelative() const override { return true; }
- bool isPersistentAcrossScenes() const override;
+ bool survivesSceneChange(bool nextSceneIsNoArt) const override;
// Nancy13 talkable characters expose the character's on-screen box as a
// clickable hotspot with a talk cursor; clicking opens _talkSceneID, and
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index 8137060baaf..77a9c187c85 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -82,6 +82,9 @@ public:
bool canHaveHotspot() const override { return true; }
bool isViewportRelative() const override { return true; }
+ // Ambient character animations stay on screen across a NO_ART_SCENE change
+ // (e.g. while a phone-call conversation is overlaid in front of them).
+ bool survivesSceneChange(bool nextSceneIsNoArt) const override { return nextSceneIsNoArt; }
CursorManager::CursorType getHoverCursor() const override {
return g_nancy->getGameType() >= kGameTypeNancy10 ? CursorManager::kHotspotTalk : CursorManager::kHotspot;
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 7e6bc40a142..ce44d36c172 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -1182,9 +1182,6 @@ void Scene::load(bool fromSaveFile) {
_specialEffects.front().onSceneChange();
}
- clearSceneData();
- g_nancy->_graphics->suppressNextDraw();
-
// Scene IDs are prefixed with S inside the cif tree; e.g 100 -> S100
Common::Path sceneName(Common::String::format("S%u", _sceneState.nextScene.sceneID));
IFF *sceneIFF = g_nancy->_resource->loadIFF(sceneName);
@@ -1213,6 +1210,16 @@ void Scene::load(bool fromSaveFile) {
delete sceneSummaryChunk;
+ // A "NO_ART_SCENE" carries no viewport art: it keeps the previous scene's
+ // frame on screen and only overlays new logic (used, for example, by
+ // phone-call conversations). Clearing it must preserve the previous scene's
+ // ambient character videos, so the scene type has to be known before the
+ // scene data is wiped.
+ const bool nextIsNoArt = _sceneState.summary.videoFile == "NO_ART_SCENE";
+
+ clearSceneData(nextIsNoArt);
+ g_nancy->_graphics->suppressNextDraw();
+
debugC(0, kDebugScene, "Loading new scene %i: description \"%s\", frame %i, vertical scroll %i, %s",
_sceneState.nextScene.sceneID,
_sceneState.summary.description.c_str(),
@@ -1821,7 +1828,7 @@ void Scene::initStaticData() {
_state = kLoad;
}
-void Scene::clearSceneData() {
+void Scene::clearSceneData(bool nextIsNoArt) {
// Clear generic flags only
for (uint16 id : g_nancy->getStaticData().genericEventFlags) {
_flags.eventFlags[id] = g_nancy->_false;
@@ -1831,14 +1838,19 @@ void Scene::clearSceneData() {
// Stop a leftover random movie if the outgoing scene didn't include
// its own PSM(isRandom) AR (so it doesn't bleed into the next scene).
- if (_activeMovie && _activeMovie->isPersistentAcrossScenes() && !_hadRandomMovieARThisScene) {
+ // A NO_ART_SCENE keeps the previous scene's ambient videos playing, so
+ // leave the active movie running in that case.
+ if (!nextIsNoArt && _activeMovie && _activeMovie->survivesSceneChange(false) && !_hadRandomMovieARThisScene) {
_activeMovie->stopRandom();
}
_hadRandomMovieARThisScene = false;
- bool clearActiveMovie = _activeMovie && !_activeMovie->isPersistentAcrossScenes();
+ // The active movie is dropped unless it survives this change (a persistent
+ // ambient loop). When it survives, clearActionRecords keeps the record alive,
+ // so the pointer must be kept too; otherwise it is cleared to avoid dangling.
+ bool clearActiveMovie = _activeMovie && !_activeMovie->survivesSceneChange(nextIsNoArt);
- _actionManager.clearActionRecords();
+ _actionManager.clearActionRecords(nextIsNoArt);
if (_lightning) {
_lightning->endLightning();
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 1c9a7129cba..11d6e67e412 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -280,7 +280,7 @@ private:
void initStaticData();
- void clearSceneData();
+ void clearSceneData(bool nextIsNoArt = false);
void clearPuzzleData();
// Maps an event flag label to its index in the eventFlags array
More information about the Scummvm-git-logs
mailing list