[Scummvm-git-logs] scummvm master -> 566aed00f3d8aefec314008e0744e8977e1ef837

bluegr noreply at scummvm.org
Fri Sep 11 05:56:27 UTC 2026


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

Summary:
baf8a6ac72 NANCY: NANCY14: Implement foreground masks for in-game secondary movies
a9f8e182ca NANCY: NANCY14: Fixes for conversation subtitles
4a0f687bcb NANCY: Remove the unneeded GF_COMPRESSED flag
8d230b12fe NANCY: NANCY14: Fix making phone calls
5b32023837 NANCY: NANCY10: Fix making calls using numbers
566aed00f3 NANCY: NANCY1-9: Cellphone fixes


Commit: baf8a6ac72d51a35aa6dc5561cad3c2c6d4d2ab3
    https://github.com/scummvm/scummvm/commit/baf8a6ac72d51a35aa6dc5561cad3c2c6d4d2ab3
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-09-11T08:56:06+03:00

Commit Message:
NANCY: NANCY14: Implement foreground masks for in-game secondary movies

Now, the table is drawn correctly in front of Dieter von Schwesterkrank

Changed paths:
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondarymovie.h


diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index c9defb662bb..2c78e7a7159 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -23,6 +23,7 @@
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
 #include "engines/nancy/sound.h"
 #include "engines/nancy/util.h"
 #include "engines/nancy/video.h"
@@ -244,8 +245,8 @@ void PlaySecondaryMovie::readRandomMovieData(Common::Serializer &ser, Common::Se
 // The header grew to mirror the non-random AR (videoFormat / visibility / cursor
 // / sceneID / frameID, plus two currently unmapped u16s and a per-movie volume
 // byte). The sequence records are unchanged. The tail is a blt-descriptor list
-// for the main movie, then the recognition ("secondary") movie's name and its
-// own blt-descriptor list, in place of Nancy13's secondaryMovie record + hotspot
+// for the main movie, then the name of a foreground mask image and its own
+// blt-descriptor list, in place of Nancy13's secondaryMovie record + hotspot
 // list.
 void PlaySecondaryMovie::readRandomMovieDataNancy14(Common::Serializer &ser, Common::SeekableReadStream &stream) {
 	readFilename(ser, _startingSequenceName);
@@ -276,15 +277,14 @@ void PlaySecondaryMovie::readRandomMovieDataNancy14(Common::Serializer &ser, Com
 		_videoDescs[i].readData(stream);
 	}
 
-	// Recognition ("secondary") movie: its name followed by its own blt
-	// descriptors. The descriptors are consumed to keep the stream aligned
-	// (no home in the struct yet).
-	readFilename(ser, _secondaryMovie.name);
-	uint16 numSecondaryDescs = 0;
-	ser.syncAsUint16LE(numSecondaryDescs);
-	for (uint i = 0; i < numSecondaryDescs; ++i) {
-		SecondaryVideoDescription unused;
-		unused.readData(stream);
+	// Foreground mask: the name of an image, followed by the blt descriptors
+	// that place it over the movie for each background frame.
+	readFilename(ser, _maskName);
+	uint16 numMaskDescs = 0;
+	ser.syncAsUint16LE(numMaskDescs);
+	_maskDescs.resize(numMaskDescs);
+	for (uint i = 0; i < numMaskDescs; ++i) {
+		_maskDescs[i].readData(stream);
 	}
 
 	applyStartingRandomSequence();
@@ -454,6 +454,7 @@ int PlaySecondaryMovie::beginRandomPause(const RandomSequence &seq) {
 	_randomPauseEndTime = g_system->getMillis() + (uint32)MAX<int32>(0, pauseMs);
 	_randomChainState = kRandomPaused;
 	setVisible(false);
+	_mask.setVisible(false);
 	_decoder.pauseVideo(true);
 	return -1;
 }
@@ -845,11 +846,43 @@ void PlaySecondaryMovie::init() {
 		resolveSentinelFrames();
 	}
 
+	if (!_maskName.empty() && _maskImage.empty()) {
+		g_nancy->_resource->loadImage(_maskName, _maskImage);
+		_mask.setVisible(false);
+		_mask.init();
+	}
+
 	_screenPosition = _drawSurface.getBounds();
 
 	RenderObject::init();
 }
 
+void PlaySecondaryMovie::registerGraphics() {
+	if (!_maskImage.empty()) {
+		_mask.registerGraphics();
+	}
+
+	RenderActionRecord::registerGraphics();
+}
+
+void PlaySecondaryMovie::updateMask(int viewportFrame) {
+	if (_maskImage.empty()) {
+		return;
+	}
+
+	for (const SecondaryVideoDescription &desc : _maskDescs) {
+		if (desc.frameID == viewportFrame) {
+			_mask._drawSurface.create(_maskImage, desc.srcRect);
+			_mask.setTransparent(true);
+			_mask.moveTo(desc.destRect);
+			_mask.setVisible(_isVisible);
+			return;
+		}
+	}
+
+	_mask.setVisible(false);
+}
+
 void PlaySecondaryMovie::onPause(bool pause) {
 	_decoder.pauseVideo(pause);
 	RenderActionRecord::onPause(pause);
@@ -972,6 +1005,8 @@ void PlaySecondaryMovie::execute() {
 				setVisible(false);
 				_hasHotspot = false;
 			}
+
+			updateMask(newFrame);
 		}
 
 		// We update the decoder here instead of in updateGraphics() to avoid an
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index 6ac166051f8..d9cb365d71b 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -95,6 +95,7 @@ public:
 
 	void init() override;
 	void onPause(bool pause) override;
+	void registerGraphics() override;
 
 	void readData(Common::SeekableReadStream &stream) override;
 	void execute() override;
@@ -176,6 +177,18 @@ public:
 	// character's recognition animation, played while the mouse hovers it.
 	RandomSequence _secondaryMovie;
 
+	// Nancy14 replaced that slot with a foreground mask: a still image blitted
+	// over the movie, so scenery standing in front of the character (a table,
+	// a counter) covers the lower part of it.
+	class ForegroundMask : public RenderObject {
+	public:
+		ForegroundMask() : RenderObject(9) {}
+		bool isViewportRelative() const override { return true; }
+	};
+
+	Common::Path _maskName;
+	Common::Array<SecondaryVideoDescription> _maskDescs;
+
 	// Nancy13 talkable characters: the scene to open when the character is
 	// clicked (its conversation). kNoScene means the character isn't clickable.
 	uint16 _talkSceneID = kNoScene;
@@ -305,7 +318,13 @@ protected:
 	// against the loaded decoder's frame count. Random sequences only.
 	void resolveSentinelFrames();
 
+	// Show the foreground mask blit belonging to the given background frame,
+	// or hide it when the record doesn't describe one for that frame.
+	void updateMask(int viewportFrame);
+
 	Graphics::ManagedSurface _fullFrame;
+	Graphics::ManagedSurface _maskImage;
+	ForegroundMask _mask;
 	int _curViewportFrame = -1;
 	bool _isFinished = false;
 };


Commit: a9f8e182ca179ff9ff6b7af43f24545905a0e833
    https://github.com/scummvm/scummvm/commit/a9f8e182ca179ff9ff6b7af43f24545905a0e833
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-09-11T08:56:08+03:00

Commit Message:
NANCY: NANCY14: Fixes for conversation subtitles

- Captions are placed in either the CONVO for conversations or AUTOTEXT
  for sound records. Lookup both
- Show captions for group concatenated text (new in Nancy14)
- Don't erase the textbox text for sounds with empty captions

Changed paths:
    engines/nancy/action/conversation.cpp
    engines/nancy/action/soundrecords.cpp
    engines/nancy/action/soundrecords.h


diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index 8e238e99177..19faca812af 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -172,6 +172,13 @@ void ConversationSound::readTerseData(Common::SeekableReadStream &stream) {
 	}
 }
 
+// Conversation captions live in the CONVO text chunk, but Nancy14 keeps a few of
+// them in AUTOTEXT instead. A key that CONVO does have, but maps to no text, is a
+// deliberately silent line, so only fall back when the key is missing altogether.
+static Common::String resolveConversationText(const Common::String &key) {
+	return resolveSubtitleText(key, resolveSubtitleText(key), "CONVO");
+}
+
 void ConversationSound::readDataNancy13(Common::SeekableReadStream &stream) {
 	readFilename(stream, _sound.name);
 	_sound.channelID = 12;	// hardcoded, as in the terse variants
@@ -200,16 +207,14 @@ void ConversationSound::readDataNancy13(Common::SeekableReadStream &stream) {
 	_sceneChange.frameID = stream.readUint16LE();
 	_sceneChange.continueSceneSound = kContinueSceneSound;
 
-	// Caption and response texts are external, keyed by sound name in CONVO.
+	// Caption and response texts are external, keyed by sound name.
 	// Each part of a concatenated line has its own caption; they make up one
 	// exchange, so they are shown together.
-	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
-	assert(convo);
 	if (_concatSounds.empty()) {
-		_text = convo->texts.getValOrDefault(_sound.name, "");
+		_text = resolveConversationText(_sound.name);
 	} else {
 		for (uint i = 0; i < _concatSounds.size(); ++i) {
-			_text += convo->texts.getValOrDefault(_concatSounds[i], "");
+			_text += resolveConversationText(_concatSounds[i]);
 		}
 	}
 
@@ -221,7 +226,7 @@ void ConversationSound::readDataNancy13(Common::SeekableReadStream &stream) {
 		response.sceneChange.sceneID = stream.readUint16LE();
 		response.sceneChange.continueSceneSound = kContinueSceneSound;
 		response.conditionFlags.read(stream);
-		response.text = convo->texts.getValOrDefault(response.soundName, "");
+		response.text = resolveConversationText(response.soundName);
 	}
 
 	uint16 numFlagsStructs = stream.readUint16LE();
diff --git a/engines/nancy/action/soundrecords.cpp b/engines/nancy/action/soundrecords.cpp
index 7e28108a140..f637369226f 100644
--- a/engines/nancy/action/soundrecords.cpp
+++ b/engines/nancy/action/soundrecords.cpp
@@ -49,6 +49,25 @@ static uint selectRandomSound(Common::Array<Common::String> &soundNames) {
 	return g_nancy->_randomSource->getRandomNumber(soundNames.size() - 1);
 }
 
+// Some entries hold nothing but markup: "silence", which scenes play as a
+// placeholder, is just "<n>". Showing one would clear the textbox and put a
+// blank line in it, wiping whatever caption is up.
+static bool hasVisibleText(const Common::String &text) {
+	bool inToken = false;
+
+	for (uint i = 0; i < text.size(); ++i) {
+		if (text[i] == '<') {
+			inToken = true;
+		} else if (text[i] == '>') {
+			inToken = false;
+		} else if (!inToken && !Common::isSpace(text[i])) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
 // Nancy13+ subtitles are no longer stored inside the sound record. Instead, the
 // engine looks the played sound's name up in the CVTX text chunks when the sound
 // starts and, if a matching entry exists, shows it in the game textbox. The
@@ -61,14 +80,17 @@ static Common::String resolveSoundSubtitle(const Common::String &soundName) {
 	const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
 	if (autotext) {
 		Common::String text = autotext->texts.getValOrDefault(soundName, "");
-		if (!text.empty()) {
+		if (hasVisibleText(text)) {
 			return text;
 		}
 	}
 
 	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
 	if (convo) {
-		return convo->texts.getValOrDefault(soundName, "");
+		Common::String text = convo->texts.getValOrDefault(soundName, "");
+		if (hasVisibleText(text)) {
+			return text;
+		}
 	}
 
 	return Common::String();
@@ -545,7 +567,7 @@ void ConcatMultiSound::readData(Common::SeekableReadStream &stream) {
 	_sound.numLoops = (uint16)stream.readSint32LE();	// stored as an int32 on disk
 	_sound.volume = stream.readUint16LE();
 	_exitSceneID = stream.readSint16LE();
-	_field35 = stream.readByte();
+	_subtitleMode = stream.readByte();
 
 	// MultiSound: one shared set of flag pairs.
 	if (!perGroupFlags()) {
@@ -560,6 +582,32 @@ void ConcatMultiSound::readData(Common::SeekableReadStream &stream) {
 	_sound.name = "NO SOUND";
 }
 
+void ConcatMultiSound::showGroupSubtitle() {
+	if (_subtitleMode == kSubtitleModeNone) {
+		return;
+	}
+
+	// A group is captioned as a single block: the text of each of its sounds, in
+	// playback order, with a line break after every sound whose flag is set.
+	// Sounds with no text of their own contribute nothing.
+	Common::String text;
+	for (const SequencedSound &sound : _groups[_currentGroup].sounds) {
+		Common::String part = resolveSoundSubtitle(sound.name);
+		if (part.empty()) {
+			continue;
+		}
+
+		text += part;
+		if (sound.flag) {
+			text += "<n>";
+		}
+	}
+
+	if (!text.empty()) {
+		showSubtitle(text + "<e>");
+	}
+}
+
 void ConcatMultiSound::startCurrentSound() {
 	SoundGroup &group = _groups[_currentGroup];
 	SequencedSound &sound = group.sounds[_currentSound];
@@ -571,6 +619,11 @@ void ConcatMultiSound::startCurrentSound() {
 		}
 	}
 
+	// The whole group is subtitled at once, as its first sound starts.
+	if (_currentSound == 0) {
+		showGroupSubtitle();
+	}
+
 	_sound.name = sound.name;
 	if (!_sound.name.empty() && _sound.name != "NO SOUND") {
 		g_nancy->_sound->loadSound(_sound);
diff --git a/engines/nancy/action/soundrecords.h b/engines/nancy/action/soundrecords.h
index 51ee7332152..513dd4f67c3 100644
--- a/engines/nancy/action/soundrecords.h
+++ b/engines/nancy/action/soundrecords.h
@@ -283,7 +283,7 @@ public:
 protected:
 	struct SequencedSound {
 		Common::String name;
-		byte flag = 0;
+		byte flag = 0;		// when set, this sound's subtitle ends its line
 		int16 delay = 0;	// seconds to hold after the sound starts
 	};
 
@@ -292,16 +292,22 @@ protected:
 		Common::Array<FlagDescription> flags;	// ConcatSound only
 	};
 
+	// Selects how a group's subtitle is presented. The original picks between two
+	// textbox surfaces, which are the same textbox here, so only the value that
+	// suppresses the subtitle entirely is acted on.
+	static const byte kSubtitleModeNone = 3;
+
 	// Flags stored per group (ConcatSound) or as one shared set (MultiSound).
 	virtual bool perGroupFlags() const = 0;
 
+	void showGroupSubtitle();
 	void startCurrentSound();
 
 	Common::Array<SoundGroup> _groups;
 	Common::Array<FlagDescription> _sharedFlags;	// MultiSound only
 	SoundDescription _sound;
 	int16 _exitSceneID = kNoScene;
-	byte _field35 = 0;
+	byte _subtitleMode = 0;
 
 	// Runtime state
 	uint _currentGroup = 0;


Commit: 4a0f687bcbcc87c4cc3ef04c95f9ba1444ba57af
    https://github.com/scummvm/scummvm/commit/4a0f687bcbcc87c4cc3ef04c95f9ba1444ba57af
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-09-11T08:56:10+03:00

Commit Message:
NANCY: Remove the unneeded GF_COMPRESSED flag

Check for files compressed inside CAB files through the common
InstallShield code instead.

Fix #17156

Changed paths:
    engines/nancy/detection.h
    engines/nancy/detection_tables.h
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h


diff --git a/engines/nancy/detection.h b/engines/nancy/detection.h
index 1f954665307..a7977106844 100644
--- a/engines/nancy/detection.h
+++ b/engines/nancy/detection.h
@@ -66,10 +66,6 @@ enum GameType {
 	// Mystery of the Seven Keys was made with Unity
 };
 
-enum NancyGameFlags {
-	GF_COMPRESSED 		= 1 << 0
-};
-
 struct NancyGameDescription {
 	AD_GAME_DESCRIPTION_HELPERS(desc);
 
diff --git a/engines/nancy/detection_tables.h b/engines/nancy/detection_tables.h
index 810faf74f40..bce2f1d18dd 100644
--- a/engines/nancy/detection_tables.h
+++ b/engines/nancy/detection_tables.h
@@ -119,7 +119,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY_GUIOPTIONS
 		},
 		kGameTypeNancy1
@@ -158,7 +158,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY2_GUIOPTIONS
 		},
 		kGameTypeNancy2
@@ -197,7 +197,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY_GUIOPTIONS
 		},
 		kGameTypeNancy3
@@ -214,7 +214,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY_GUIOPTIONS
 		},
 		kGameTypeNancy3
@@ -242,7 +242,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY_GUIOPTIONS
 		},
 		kGameTypeNancy3
@@ -281,7 +281,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY_GUIOPTIONS
 		},
 		kGameTypeNancy4
@@ -298,7 +298,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY_GUIOPTIONS
 		},
 		kGameTypeNancy4
@@ -315,7 +315,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY_GUIOPTIONS
 		},
 		kGameTypeNancy4
@@ -344,7 +344,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY_GUIOPTIONS
 		},
 		kGameTypeNancy4
@@ -372,7 +372,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY5_GUIOPTIONS
 		},
 		kGameTypeNancy5
@@ -389,7 +389,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY5_GUIOPTIONS
 		},
 		kGameTypeNancy5
@@ -406,7 +406,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY5_GUIOPTIONS
 		},
 		kGameTypeNancy5
@@ -435,7 +435,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY5_GUIOPTIONS
 		},
 		kGameTypeNancy5
@@ -467,7 +467,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY6_7_GUIOPTIONS
 		},
 		kGameTypeNancy6
@@ -484,7 +484,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY6_7_GUIOPTIONS
 		},
 		kGameTypeNancy6
@@ -501,7 +501,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY6_7_GUIOPTIONS
 		},
 		kGameTypeNancy6
@@ -529,7 +529,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY6_7_GUIOPTIONS
 		},
 		kGameTypeNancy6
@@ -557,7 +557,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY6_7_GUIOPTIONS
 		},
 		kGameTypeNancy7
@@ -574,7 +574,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY6_7_GUIOPTIONS
 		},
 		kGameTypeNancy7
@@ -602,7 +602,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY6_7_GUIOPTIONS
 		},
 		kGameTypeNancy7
@@ -630,7 +630,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY8_GUIOPTIONS
 		},
 		kGameTypeNancy8
@@ -658,7 +658,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY8_GUIOPTIONS
 		},
 		kGameTypeNancy8
@@ -690,7 +690,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_DROPPLATFORM,
 			NANCY8_GUIOPTIONS
 		},
 		kGameTypeNancy9
@@ -737,7 +737,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_TESTING | ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_TESTING | ADGF_DROPPLATFORM,
 			NANCY8_GUIOPTIONS
 		},
 		kGameTypeNancy10
@@ -754,7 +754,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_TESTING | ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_TESTING | ADGF_DROPPLATFORM,
 			NANCY8_GUIOPTIONS
 		},
 		kGameTypeNancy10
@@ -770,7 +770,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_TESTING | ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_TESTING | ADGF_DROPPLATFORM,
 			NANCY8_GUIOPTIONS
 		},
 		kGameTypeNancy10
@@ -817,7 +817,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_TESTING | ADGF_DROPPLATFORM | GF_COMPRESSED,
+			ADGF_TESTING | ADGF_DROPPLATFORM,
 			NANCY8_GUIOPTIONS
 		},
 		kGameTypeNancy11
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 085b449a151..9bd2ebef9b4 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -579,11 +579,9 @@ void NancyEngine::bootGameEngine() {
 	SearchMan.addSubDirectoryMatching(gameDataDir, "font");
 
 	// Load archive if running a compressed variant
-	if (isCompressed()) {
-		Common::Archive *cabinet = Common::makeInstallShieldArchive("data");
-		if (cabinet) {
-			SearchMan.add("data1.cab", cabinet);
-		}
+	Common::Archive *cabinet = Common::makeInstallShieldArchive("data");
+	if (cabinet) {
+		SearchMan.add("data1.cab", cabinet);
 	}
 
 	_resource->readCifTree("ciftree", "dat", 1);
@@ -978,8 +976,4 @@ Common::Error NancyEngine::synchronize(Common::Serializer &ser) {
 	return Common::kNoError;
 }
 
-bool NancyEngine::isCompressed() {
-	return getGameFlags() & GF_COMPRESSED;
-}
-
 } // End of namespace Nancy
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index e3ec6af9737..e422e32d99f 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -181,8 +181,6 @@ private:
 
 	Common::Error synchronize(Common::Serializer &serializer);
 
-	bool isCompressed();
-
 	StaticData _staticData;
 	Common::HashMap<Common::String, EngineData *> _engineData;
 


Commit: 8d230b12fe8562532e350c4bf6539f907b17f6af
    https://github.com/scummvm/scummvm/commit/8d230b12fe8562532e350c4bf6539f907b17f6af
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-09-11T08:56:12+03:00

Commit Message:
NANCY: NANCY14: Fix making phone calls

The length of local and long distance calls is now stored in the
Telephone AR data

Changed paths:
    engines/nancy/action/puzzle/telephone.cpp
    engines/nancy/action/puzzle/telephone.h


diff --git a/engines/nancy/action/puzzle/telephone.cpp b/engines/nancy/action/puzzle/telephone.cpp
index a8953a17398..746cb3c1a3b 100644
--- a/engines/nancy/action/puzzle/telephone.cpp
+++ b/engines/nancy/action/puzzle/telephone.cpp
@@ -128,6 +128,11 @@ void Telephone::readData(Common::SeekableReadStream &stream) {
 	stream.skip(1);
 	readRect(stream, _exitHotspot);
 
+	if (_phoneType == kTelephone && g_nancy->getGameType() >= kGameTypeNancy14) {
+		_numberLength = stream.readUint16LE();
+		_longDistanceNumberLength = stream.readUint16LE();
+	}
+
 	uint numCalls = stream.readUint16LE();
 
 	_calls.resize(numCalls);
@@ -193,6 +198,9 @@ void Telephone::execute() {
 				// Pressed a new button, check all numbers for match
 				// We do this before going to the ringing state to support nancy4's voice mail system,
 				// where call numbers can be 1 digit long
+				uint numberLength = (_calledNumber.size() && _calledNumber[0] == 1) ? _longDistanceNumberLength : _numberLength;
+				bool isNumberComplete = _calledNumber.size() >= numberLength;
+
 				for (uint i = 0; i < _calls.size(); ++i) {
 					auto &call = _calls[i];
 					bool invalid = false;
@@ -206,13 +214,8 @@ void Telephone::execute() {
 					}
 
 					// We do not want to check for a terminator if the dialed number is of
-					// appropriate size (7 digits, or 11 when the number starts with '1')
-					bool checkNextDigit = true;
-					if (_calledNumber.size() >= 11 || (_calledNumber.size() >= 7 && (_calledNumber[0] != 1))) {
-						checkNextDigit = false;
-					}
-
-					if (!invalid && checkNextDigit) {
+					// appropriate size
+					if (!invalid && !isNumberComplete) {
 						// Check if the next digit in the phone number is '10' (star). Presumably, that will never
 						// be contained in a valid phone number
 						if (_calls[i].phoneNumber[_calledNumber.size()] != 10) {
@@ -230,7 +233,7 @@ void Telephone::execute() {
 
 				if (_selected == -1) {
 					// Did not find a suitable match, check if the dialed number is above allowed size
-					if (_calledNumber.size() >= 11 || (_calledNumber.size() >= 7 && (_calledNumber[0] != 1))) {
+					if (isNumberComplete) {
 						shouldRing = true;
 					}
 				} else {
diff --git a/engines/nancy/action/puzzle/telephone.h b/engines/nancy/action/puzzle/telephone.h
index b1655fb1139..44944d6ea40 100644
--- a/engines/nancy/action/puzzle/telephone.h
+++ b/engines/nancy/action/puzzle/telephone.h
@@ -85,6 +85,11 @@ protected:
 	Common::Rect _exitHotspot;
 	Common::Array<PhoneCall> _calls;
 
+	// Number of digits a number needs before the phone starts dialing. Numbers
+	// beginning with a '1' are long distance and have their own length
+	uint16 _numberLength = 7;
+	uint16 _longDistanceNumberLength = 11;
+
 	// NewPhone properties
 	bool _hasDisplay = false;
 	uint16 _displayFont = 0;


Commit: 5b32023837544e719614a8a6eb770d876f23c2af
    https://github.com/scummvm/scummvm/commit/5b32023837544e719614a8a6eb770d876f23c2af
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-09-11T08:56:14+03:00

Commit Message:
NANCY: NANCY10: Fix making calls using numbers

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


diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index 3bf1a964ff1..ad46d4be08e 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -1799,23 +1799,24 @@ int CellPhonePopup::findContactByDialBuffer() const {
 		return -1;
 	}
 
-	// Dial pattern lives in prefix[2..], terminated by '\n'.
+	// The whole dial pattern is compared against the dialed digits, with any
+	// digit not yet entered counting as a zero. Contacts carry one entry per
+	// form of their number (full, without the leading '1', local only), each
+	// padded out with zeroes, so a shorter number pressed with Talk matches
+	// its own entry. Entries hidden from the directory are still callable, so
+	// the visibility flag plays no part here.
 	const uint dialLen = _dialedNumber.size();
 	for (uint i = 0; i < _contacts.size(); ++i) {
 		const UICL::Contact &c = _contacts[i];
-		if (!isContactVisible(c)) {
-			continue;
-		}
 		bool match = true;
-		for (uint b = 0; b < dialLen; ++b) {
-			const byte slotIdx = (byte)(_dialedNumber[b] - '0');
-			if (b >= sizeof(c.dialPattern) || slotIdx != c.dialPattern[b]) {
+		for (uint b = 0; b < sizeof(c.dialPattern); ++b) {
+			const byte slotIdx = b < dialLen ? (byte)(_dialedNumber[b] - '0') : 0;
+			if (slotIdx != c.dialPattern[b]) {
 				match = false;
 				break;
 			}
 		}
-		if (match && dialLen < sizeof(c.dialPattern) &&
-				c.dialPattern[dialLen] == '\n') {
+		if (match) {
 			return (int)i;
 		}
 	}


Commit: 566aed00f3d8aefec314008e0744e8977e1ef837
    https://github.com/scummvm/scummvm/commit/566aed00f3d8aefec314008e0744e8977e1ef837
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-09-11T08:56:15+03:00

Commit Message:
NANCY: NANCY1-9: Cellphone fixes

- Backing away using the left hotspot no longer produces a beep sound
- During the "We're sorry" message after a bad dialed number, both the
  left anr right hotspots are now active

Fix #16684

Changed paths:
    engines/nancy/action/puzzle/telephone.cpp


diff --git a/engines/nancy/action/puzzle/telephone.cpp b/engines/nancy/action/puzzle/telephone.cpp
index 746cb3c1a3b..f44efbf7b6d 100644
--- a/engines/nancy/action/puzzle/telephone.cpp
+++ b/engines/nancy/action/puzzle/telephone.cpp
@@ -198,15 +198,22 @@ void Telephone::execute() {
 				// Pressed a new button, check all numbers for match
 				// We do this before going to the ringing state to support nancy4's voice mail system,
 				// where call numbers can be 1 digit long
+				// Phones without automatic dialing leave it to the dial button to decide when
+				// to place a call, so the whole number gets matched, with digits that were
+				// never entered counting as zeroes
+				bool matchWholeNumber = !_dialAutomatically;
 				uint numberLength = (_calledNumber.size() && _calledNumber[0] == 1) ? _longDistanceNumberLength : _numberLength;
-				bool isNumberComplete = _calledNumber.size() >= numberLength;
+				bool isNumberComplete = matchWholeNumber || _calledNumber.size() >= numberLength;
 
 				for (uint i = 0; i < _calls.size(); ++i) {
 					auto &call = _calls[i];
 					bool invalid = false;
+					uint numDigits = matchWholeNumber ? call.phoneNumber.size() : _calledNumber.size();
 
-					for (uint j = 0; j < _calledNumber.size(); ++j) {
-						if (_calledNumber[j] != call.phoneNumber[j]) {
+					for (uint j = 0; j < numDigits; ++j) {
+						byte dialedDigit = j < _calledNumber.size() ? _calledNumber[j] : 0;
+
+						if (dialedDigit != call.phoneNumber[j]) {
 							// Invalid number, move onto next
 							invalid = true;
 							break;
@@ -414,6 +421,11 @@ void Telephone::handleInput(NancyInput &input) {
 			continue;
 		}
 
+		// So are the directory buttons when there is only a single entry to show
+		if ((i == _upDirButtonID || i == _downDirButtonID) && _calls.size() == 1) {
+			continue;
+		}
+
 		if (NancySceneState.getViewport().convertViewportToScreen(_destRects[i]).contains(input.mousePos)) {
 			g_nancy->_cursor->setCursorType(CursorManager::kHotspot);
 			buttonNr = i;
@@ -421,16 +433,21 @@ void Telephone::handleInput(NancyInput &input) {
 		}
 	}
 
-	if (_callState != kWaiting && _callState != kRinging) {
-		return;
-	}
-
+	// The exit hotspot stays active for as long as the record is running, even
+	// while ringing, talking, or playing the bad number message. Only the
+	// buttons are limited to the states where the phone accepts input.
 	if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
 		g_nancy->_cursor->setCursorType(g_nancy->_cursor->_puzzleExitCursor);
 
 		if (input.input & NancyInput::kLeftMouseButtonUp) {
-			g_nancy->_sound->loadSound(_hangUpSound);
-			g_nancy->_sound->playSound(_hangUpSound);
+			if (_phoneType == kTelephone) {
+				g_nancy->_sound->loadSound(_hangUpSound);
+				g_nancy->_sound->playSound(_hangUpSound);
+			} else {
+				// The new phone hangs up without a sound, and without waiting for
+				// whatever is currently playing to finish
+				_state = kActionTrigger;
+			}
 
 			_callState = kHangUp;
 		}




More information about the Scummvm-git-logs mailing list