[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