[Scummvm-git-logs] scummvm branch-3-0 -> 2bfda36ae8bd8f4feb390cd4006424cb5c611628

sluicebox noreply at scummvm.org
Mon Nov 24 16:16:31 UTC 2025


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

Summary:
09cbe2433e PRIVATE: Implement LoseInventory()
b423565c05 PRIVATE: Implement all Leave Item sounds
96f943b6d7 PRIVATE: Implement all Take Item sounds
152dbe2d28 PRIVATE: Finish implementing PoliceBust and BustMovie
d791c0e023 PRIVATE: Fix addMemory crash when helping Mavis
810266ddc8 PRIVATE: Clear diary page exits
267e3c826b PRIVATE: Hide dossier arrows when unavailable
160ea57388 PRIVATE: Improve dossier navigation
2bfda36ae8 PRIVATE: Add mapping for Japanese Windows cursors


Commit: 09cbe2433e03d3b37da982619f8ef13300d28dbb
    https://github.com/scummvm/scummvm/commit/09cbe2433e03d3b37da982619f8ef13300d28dbb
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-11-24T08:14:18-08:00

Commit Message:
PRIVATE: Implement LoseInventory()

Changed paths:
    engines/private/funcs.cpp
    engines/private/private.cpp
    engines/private/private.h


diff --git a/engines/private/funcs.cpp b/engines/private/funcs.cpp
index 6a163abec6b..6ab508d1b78 100644
--- a/engines/private/funcs.cpp
+++ b/engines/private/funcs.cpp
@@ -359,8 +359,9 @@ static void fNoStopSounds(ArgArray args) {
 
 static void fLoseInventory(ArgArray args) {
 	assert(args.size() == 0);
-	debugC(1, kPrivateDebugScript, "LoveInventory()");
-	g_private->inventory.clear();
+	debugC(1, kPrivateDebugScript, "LoseInventory()");
+
+	g_private->removeRandomInventory();
 }
 
 static void fInventory(ArgArray args) {
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index 2374bd6fe7d..5c63b3a199b 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -1081,6 +1081,31 @@ bool PrivateEngine::inInventory(const Common::String &bmp) const {
 	return false;
 }
 
+void PrivateEngine::removeRandomInventory() {
+	// This logic was extracted from the executable.
+	// Examples:
+	//   0-3 items:  0 items removed
+	//   4-6 items:  1 item removed
+	//   7-10 items: 2 items removed
+	//
+	// TODO: Clear the inventory flag for the item.
+	// We are currently only removing items from the diary. We need to also
+	// remove them from Marlowe's inventory by clearing their item flag.
+	// We can do this once item flags are stored and included in save files.
+	uint numberOfItemsToRemove = (inventory.size() * 30) / 100;
+	for (uint i = 0; i < numberOfItemsToRemove; i++) {
+		uint indexToRemove = _rnd->getRandomNumber(inventory.size() - 1);
+		uint index = 0;
+		for (InvList::iterator it = inventory.begin(); it != inventory.end(); ++it) {
+			if (index == indexToRemove) {
+				inventory.erase(it);
+				break;
+			}
+			index++;
+		}
+	}
+}
+
 void PrivateEngine::selectAMRadioArea(Common::Point mousePos) {
 	if (_AMRadioArea.surf == nullptr)
 		return;
diff --git a/engines/private/private.h b/engines/private/private.h
index fb45db8b4d0..5259867229a 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -335,6 +335,7 @@ public:
 	// Diary
 	InvList inventory;
 	bool inInventory(const Common::String &bmp) const;
+	void removeRandomInventory();
 	Common::String _diaryLocPrefix;
 	void loadLocations(const Common::Rect &);
 	void loadInventory(uint32, const Common::Rect &, const Common::Rect &);


Commit: b423565c059bcd818a23653f7d98105200fc237d
    https://github.com/scummvm/scummvm/commit/b423565c059bcd818a23653f7d98105200fc237d
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-11-24T08:14:27-08:00

Commit Message:
PRIVATE: Implement all Leave Item sounds

Changed paths:
    engines/private/funcs.cpp
    engines/private/private.cpp
    engines/private/private.h


diff --git a/engines/private/funcs.cpp b/engines/private/funcs.cpp
index 6ab508d1b78..bade9e645a2 100644
--- a/engines/private/funcs.cpp
+++ b/engines/private/funcs.cpp
@@ -59,6 +59,14 @@ static void fChgMode(ArgArray args) {
 			int maxLocationValue = g_private->getMaxLocationValue();
 			setSymbol(location, maxLocationValue + 1);
 		}
+		// set a game flag when visiting the police station.
+		if (!g_private->isDemo()) {
+			if (*(args[2].u.sym->name) == g_private->getPoliceStationLocation()) {
+				Common::String beenDowntownName = g_private->getBeenDowntownVariable();
+				Symbol *beenDowntown = g_private->maps.lookupVariable(&beenDowntownName);
+				setSymbol(beenDowntown, 1);
+			}
+		}
 	}
 
 	if (g_private->_mode == 0) {
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index 5c63b3a199b..93e6742b0c6 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -746,6 +746,14 @@ Common::String PrivateEngine::getWallSafeValueVariable() {
 	return getSymbolName("kWallSafeValue", "k3");
 }
 
+Common::String PrivateEngine::getBeenDowntownVariable() {
+	return getSymbolName("kBeenDowntown", "k8");
+}
+
+Common::String PrivateEngine::getPoliceStationLocation() {
+	return getSymbolName("kLocationPO", "k12");
+}
+
 Common::String PrivateEngine::getExitCursor() {
 	return getSymbolName("kExit", "k5");
 }
@@ -2241,8 +2249,19 @@ Common::String PrivateEngine::getLeaveSound() {
 	if (isDemo())
 		return (_globalAudioPath + "mvo008.wav");
 
-	uint r = _rnd->getRandomNumber(4) + 1;
-	return Common::String::format("%sleft%d.wav", _globalAudioPath.c_str(), r);
+	// The last sound is only available after going to the police station.
+	const char *sounds[7] = {
+		"mvo008.wav",
+		"mvo004.wav",
+		"left1.wav",
+		"left2.wav",
+		"left3.wav",
+		"left4.wav",
+		"left5.wav" // "I've had enough trouble with the police"
+	};
+	Private::Symbol *beenDowntown = maps.variables.getVal(getBeenDowntownVariable());
+	uint r = _rnd->getRandomNumber(beenDowntown->u.val ? 6 : 5);
+	return _globalAudioPath + sounds[r];
 }
 
 Common::String PrivateEngine::getRandomPhoneClip(const char *clip, int i, int j) {
diff --git a/engines/private/private.h b/engines/private/private.h
index 5259867229a..8302e540bbe 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -302,6 +302,8 @@ public:
 	Common::String getAlternateGameVariable();
 	Common::String getPoliceIndexVariable();
 	Common::String getWallSafeValueVariable();
+	Common::String getBeenDowntownVariable();
+	Common::String getPoliceStationLocation();
 	const char *getSymbolName(const char *name, const char *strippedName, const char *demoName = nullptr);
 
 	// movies


Commit: 96f943b6d7a206659e242dfebfc89e5d8e9cde82
    https://github.com/scummvm/scummvm/commit/96f943b6d7a206659e242dfebfc89e5d8e9cde82
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-11-24T08:14:34-08:00

Commit Message:
PRIVATE: Implement all Take Item sounds

Changed paths:
    engines/private/private.cpp
    engines/private/private.h


diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index 93e6742b0c6..f2a693c5c44 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -70,6 +70,7 @@ PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
 	_modified = false;
 	_mode = -1;
 	_toTake = false;
+	_haveTakenItem = false;
 
 	// Movies
 	_nextMovie = "";
@@ -878,6 +879,7 @@ void PrivateEngine::selectMask(Common::Point mousePos) {
 					setSymbol(m.flag1, 1);
 					playSound(getTakeSound(), 1, false, false);
 					_toTake = false;
+					_haveTakenItem = true;
 				}
 			}
 
@@ -1392,6 +1394,8 @@ void PrivateEngine::restartGame() {
 		sym->u.val = 0;
 	}
 	inventory.clear();
+	_toTake = false;
+	_haveTakenItem = false;
 	_dossiers.clear();
 	_diaryPages.clear();
 
@@ -1444,6 +1448,7 @@ Common::Error PrivateEngine::loadGameStream(Common::SeekableReadStream *stream)
 	for (uint32 i = 0; i < size; ++i) {
 		inventory.push_back(stream->readString());
 	}
+	_haveTakenItem = (inventory.size() > 1); // TODO: include this in save format
 
 	// Diary pages
 	_diaryPages.clear();
@@ -2232,8 +2237,18 @@ Common::String PrivateEngine::getTakeSound() {
 	if (isDemo())
 		return (_globalAudioPath + "mvo007.wav");
 
-	uint r = _rnd->getRandomNumber(4) + 1;
-	return Common::String::format("%stook%d.wav", _globalAudioPath.c_str(), r);
+	// Only the first four sounds are available when taking the first item.
+	const char *sounds[7] = {
+		"mvo007.wav",
+		"mvo003.wav",
+		"took1.wav",
+		"took2.wav",
+		"took3.wav",
+		"took4.wav",
+		"took5.wav"
+	};
+	uint r = _rnd->getRandomNumber(_haveTakenItem ? 6 : 3);
+	return _globalAudioPath + sounds[r];
 }
 
 Common::String PrivateEngine::getTakeLeaveSound() {
diff --git a/engines/private/private.h b/engines/private/private.h
index 8302e540bbe..806c17fe43c 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -342,6 +342,7 @@ public:
 	void loadLocations(const Common::Rect &);
 	void loadInventory(uint32, const Common::Rect &, const Common::Rect &);
 	bool _toTake;
+	bool _haveTakenItem;
 	DiaryPages _diaryPages;
 	int _currentDiaryPage;
 	ExitInfo _diaryNextPageExit;


Commit: 152dbe2d28a7bb328994e988eaab7184a6b299b1
    https://github.com/scummvm/scummvm/commit/152dbe2d28a7bb328994e988eaab7184a6b299b1
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-11-24T08:14:51-08:00

Commit Message:
PRIVATE: Finish implementing PoliceBust and BustMovie

Changed paths:
    engines/private/funcs.cpp
    engines/private/private.cpp
    engines/private/private.h


diff --git a/engines/private/funcs.cpp b/engines/private/funcs.cpp
index bade9e645a2..d9ae5259809 100644
--- a/engines/private/funcs.cpp
+++ b/engines/private/funcs.cpp
@@ -232,50 +232,39 @@ static void fRestartGame(ArgArray args) {
 static void fPoliceBust(ArgArray args) {
 	// assert types
 	assert(args.size() == 1 || args.size() == 2);
-	g_private->_policeBustEnabled = args[0].u.val;
-	//debug("Number of clicks %d", g_private->computePoliceIndex());
+	int mode = (args.size() == 2) ? args[1].u.val : 0;
+	debugC(1, kPrivateDebugScript, "PoliceBust(%d, %d)", args[0].u.val, mode);
+	
+	if (mode == 3) {
+		g_private->completePoliceBust();
+		return;
+	}
+	if (mode == 2) {
+		g_private->wallSafeAlarm();
+		return;
+	}
+	if (mode == 1) {
+		// Not implemented: a special mode for police busts
+		// in Marlowe's office that was removed from the game.
+		return;
+	}
 
-	if (g_private->_policeBustEnabled)
+	if (args[0].u.val) {
 		g_private->startPoliceBust();
-
-	if (args.size() == 2) {
-		if (args[1].u.val == 2) {
-			// Unclear what it means
-		} else if (args[1].u.val == 3) {
-			g_private->_nextSetting = g_private->getMainDesktopSetting();
-			g_private->_mode = 0;
-			g_private->_origin = Common::Point(kOriginZero[0], kOriginZero[1]);
-		} else
-			assert(0);
+	} else {
+		g_private->stopPoliceBust();
 	}
-	debugC(1, kPrivateDebugScript, "PoliceBust(%d, ..)", args[0].u.val);
-	debugC(1, kPrivateDebugScript, "WARNING: PoliceBust partially implemented");
 }
 
 static void fBustMovie(ArgArray args) {
 	// assert types
 	assert(args.size() == 1);
 	debugC(1, kPrivateDebugScript, "BustMovie(%s)", args[0].u.sym->name->c_str());
-	uint policeIndex = g_private->maps.variables.getVal(g_private->getPoliceIndexVariable())->u.val;
-	int videoIndex = policeIndex / 2 - 1;
-	if (videoIndex < 0)
-		videoIndex = 0;
-	assert(videoIndex <= 5);
-	Common::String pv =
-		Common::String::format("po/animatio/spoc%02dxs.smk",
-							   kPoliceBustVideos[videoIndex]);
-
-	if (kPoliceBustVideos[videoIndex] == 2) {
-		Common::String s("global/transiti/audio/spoc02VO.wav");
-		g_private->playSound(s, 1, false, false);
-		g_private->changeCursor("default");
-		g_private->waitForSoundToStop();
-	}
 
-	g_private->_nextMovie = pv;
+	g_private->_nextMovie = g_private->_policeBustMovie;
 	g_private->_nextSetting = args[0].u.sym->name->c_str();
 
-	Common::String memoryPath = pv;
+	Common::String memoryPath = g_private->_policeBustMovie;
 	memoryPath.replace('/', '\\');
 	g_private->addMemory(memoryPath);
 }
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index f2a693c5c44..bdbcccce9d0 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -53,7 +53,7 @@ extern int parse(const char *);
 PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
 	: Engine(syst), _gameDescription(gd), _image(nullptr), _videoDecoder(nullptr),
 	  _compositeSurface(nullptr), _transparentColor(0), _frameImage(nullptr),
-	  _framePalette(nullptr), _maxNumberClicks(0), _sirenWarning(0),
+	  _framePalette(nullptr),
 	  _subtitles(nullptr), _sfxSubtitles(false), _useSubtitles(false),
 	  _defaultCursor(nullptr),
 	  _screenW(640), _screenH(480) {
@@ -86,9 +86,7 @@ PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
 	_framePath = "inface/general/inface2.bmp";
 
 	// Police
-	_policeBustEnabled = false;
-	_policeBustSetting = "";
-	_numberClicks = 0;
+	resetPoliceBust();
 	_sirenSound = "po/audio/posfx002.wav";
 
 	// General sounds
@@ -542,43 +540,125 @@ void PrivateEngine::clearAreas() {
 	}
 }
 
+void PrivateEngine::resetPoliceBust() {
+	_policeBustEnabled = false;
+	_policeSirenPlayed = false;
+	_numberOfClicks = 0;
+	_numberClicksAfterSiren = 0;
+	_policeBustMovieIndex = 0;
+	_policeBustMovie = "";
+	_policeBustPreviousSetting = "";
+}
+
 void PrivateEngine::startPoliceBust() {
-	// This logic was extracted from the binary
+	_policeBustEnabled = true;
+	_policeSirenPlayed = false;
+
+	// Calculate two click counts:
+	// 1. the number of clicks until the siren warning
+	// 2. the number of clicks after the siren warning until the bust
+	// This logic was extracted from the executable.
 	int policeIndex = maps.variables.getVal(getPoliceIndexVariable())->u.val;
-	int r = _rnd->getRandomNumber(0xc);
-	if (policeIndex > 0x14) {
-		policeIndex = 0x15;
+	if (policeIndex > 20) {
+		policeIndex = 21;
+	}
+	int r = _rnd->getRandomNumber(11);
+	int numberOfClicks = r + ((policeIndex * 14) / -21) + 16;
+	_numberClicksAfterSiren = _rnd->getRandomNumber(6) + 3;
+	if ((numberOfClicks - _numberClicksAfterSiren) <= 2) {
+		_numberOfClicks = 2;
+	} else {
+		_numberOfClicks = numberOfClicks - _numberClicksAfterSiren;
 	}
-	_maxNumberClicks = r + 0x10 + (policeIndex * 0xe) / -0x15;
-	_sirenWarning = _rnd->getRandomNumber(0x7) + 3;
-	_numberClicks = 0;
-	if (_sirenWarning >= _maxNumberClicks)
-		_sirenWarning = _maxNumberClicks - 1;
 }
 
-void PrivateEngine::checkPoliceBust() {
-	if (!_policeBustEnabled)
+void PrivateEngine::stopPoliceBust() {
+	_policeBustEnabled = false;
+}
+
+void PrivateEngine::wallSafeAlarm() {
+	// This logic was extracted from the executable.
+	// It looks like the developers' intended to randomly reduce
+	// the number of clicks until the police arrive.
+	// But instead of using their low random number, they generated
+	// a new random number, so the safe alarm may increase the
+	// number of clicks until the police arrive.
+
+	int r1 = _rnd->getRandomNumber(3);
+	int r2 = _rnd->getRandomNumber(3);
+	if (r1 + r2 + 1 <= _numberOfClicks) {
+		r1 = _rnd->getRandomNumber(3);
+		r2 = _rnd->getRandomNumber(3);
+		_numberOfClicks = r1 + r2 + 1;
+	}
+}
+
+void PrivateEngine::completePoliceBust() {
+	if (!_policeBustPreviousSetting.empty()) {
+		_nextSetting = _policeBustPreviousSetting;
+	}
+
+	int policeIndex = maps.variables.getVal(getPoliceIndexVariable())->u.val;
+	if (policeIndex > 13) {
 		return;
+	}
+
+	// Set kPoliceArrived. This flag is cleared by the wall safe alarm.
+	Symbol *policeArrived = maps.variables.getVal(getPoliceArrivedVariable());
+	setSymbol(policeArrived, 1);
+
+	// Select the movie for BustMovie() to play
+	_policeBustMovie =
+		Common::String::format("po/animatio/spoc%02dxs.smk",
+			kPoliceBustVideos[g_private->_policeBustMovieIndex]);
+
+	// Play audio on the second bust movie
+	if (kPoliceBustVideos[_policeBustMovieIndex] == 2) {
+		Common::String s("global/transiti/audio/spoc02VO.wav");
+		g_private->playSound(s, 1, false, false);
+		g_private->changeCursor("default");
+		g_private->waitForSoundToStop();
+	}
 
-	if (_numberClicks < _sirenWarning)
+	// Cycle to the next movie and wrap around
+	_policeBustMovieIndex = (_policeBustMovieIndex + 1) % ARRAYSIZE(kPoliceBustVideos);
+
+	_nextSetting = getPOGoBustMovieSetting();
+}
+
+void PrivateEngine::checkPoliceBust() {
+	if (!_policeBustEnabled) {
 		return;
+	}
 
-	if (_numberClicks == _sirenWarning) {
-		stopSound(true);
-		playSound(_sirenSound, 0, false, false);
-		_numberClicks++; // Won't execute again
+	if (_numberOfClicks >= 0) {
 		return;
 	}
 
-	if (_numberClicks == _maxNumberClicks + 1) {
-		uint policeIndex = maps.variables.getVal(getPoliceIndexVariable())->u.val;
-		_policeBustSetting = _currentSetting;
-		if (policeIndex <= 13) {
-			_nextSetting = getPOGoBustMovieSetting();
+	if (!_policeSirenPlayed) {
+		// Play siren
+		stopSound(true);
+		playSound(_sirenSound, 1, false, false);
+
+		_policeSirenPlayed = true;
+		_numberOfClicks = _numberClicksAfterSiren;
+	} else {
+		// Bust Marlowe.
+		// The original seems to record _currentSetting instead of
+		// _nextSetting, but that causes a click to do nothing if it
+		// triggers a police bust that doesn't do anything except for
+		// restoring the current scene.
+		if (!_nextSetting.empty()) {
+			_policeBustPreviousSetting = _nextSetting;
 		} else {
-			_nextSetting = getPoliceBustFromMOSetting();
+			_policeBustPreviousSetting = _currentSetting;
 		}
-		clearAreas();
+		// The next setting is indeed kPoliceBustFromMO, even though it
+		// occurs from all locations and is unrelated to Marlowe's office.
+		// According to comments in the game script, Marlowe's office
+		// originally required a special mode but it was later removed.
+		// Apparently the developers didn't rename the setting.
+		_nextSetting = getPoliceBustFromMOSetting();
 		_policeBustEnabled = false;
 	}
 }
@@ -747,6 +827,10 @@ Common::String PrivateEngine::getWallSafeValueVariable() {
 	return getSymbolName("kWallSafeValue", "k3");
 }
 
+Common::String PrivateEngine::getPoliceArrivedVariable() {
+	return getSymbolName("kPoliceArrived", "k7");
+}
+
 Common::String PrivateEngine::getBeenDowntownVariable() {
 	return getSymbolName("kBeenDowntown", "k8");
 }
@@ -853,7 +937,7 @@ void PrivateEngine::selectExit(Common::Point mousePos) {
 		}
 	}
 	if (!ns.empty()) {
-		_numberClicks++; // count click only if it hits a hotspot
+		_numberOfClicks--; // count click only if it hits a hotspot
 		_nextSetting = ns;
 		_highlightMasks = false;
 	}
@@ -890,7 +974,7 @@ void PrivateEngine::selectMask(Common::Point mousePos) {
 		}
 	}
 	if (!ns.empty()) {
-		_numberClicks++; // count click only if it hits a hotspot
+		_numberOfClicks--; // count click only if it hits a hotspot
 		_nextSetting = ns;
 		_highlightMasks = false;
 	}
@@ -916,8 +1000,6 @@ bool PrivateEngine::selectLocation(const Common::Point &mousePos) {
 					}
 				}
 
-				_numberClicks++;
-
 				// Prevent crash if there are no memories for this location
 				if (!diaryPageSet) {
 					return true;
@@ -1388,6 +1470,9 @@ void PrivateEngine::restartGame() {
 			sym->u.val = 0;
 	}
 
+	// Police Bust
+	resetPoliceBust();
+
 	// Diary
 	for (NameList::iterator it = maps.locationList.begin(); it != maps.locationList.end(); ++it) {
 		Private::Symbol *sym = maps.locations.getVal(*it);
diff --git a/engines/private/private.h b/engines/private/private.h
index 806c17fe43c..21b6b97e2c1 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -302,6 +302,7 @@ public:
 	Common::String getAlternateGameVariable();
 	Common::String getPoliceIndexVariable();
 	Common::String getWallSafeValueVariable();
+	Common::String getPoliceArrivedVariable();
 	Common::String getBeenDowntownVariable();
 	Common::String getPoliceStationLocation();
 	const char *getSymbolName(const char *name, const char *strippedName, const char *demoName = nullptr);
@@ -327,12 +328,18 @@ public:
 
 	// Police Bust
 	bool _policeBustEnabled;
+	bool _policeSirenPlayed;
+	int _numberOfClicks;
+	int _numberClicksAfterSiren;
+	int _policeBustMovieIndex;
+	Common::String _policeBustMovie;
+	Common::String _policeBustPreviousSetting;
+	void resetPoliceBust();
 	void startPoliceBust();
+	void stopPoliceBust();
+	void wallSafeAlarm();
+	void completePoliceBust();
 	void checkPoliceBust();
-	int _numberClicks;
-	int _maxNumberClicks;
-	int _sirenWarning;
-	Common::String _policeBustSetting;
 
 	// Diary
 	InvList inventory;


Commit: d791c0e0234c7ba49fe950fede371f0a40307d10
    https://github.com/scummvm/scummvm/commit/d791c0e0234c7ba49fe950fede371f0a40307d10
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-11-24T08:14:58-08:00

Commit Message:
PRIVATE: Fix addMemory crash when helping Mavis

Changed paths:
    engines/private/funcs.cpp
    engines/private/private.cpp
    engines/private/private.h


diff --git a/engines/private/funcs.cpp b/engines/private/funcs.cpp
index d9ae5259809..d79c4f53603 100644
--- a/engines/private/funcs.cpp
+++ b/engines/private/funcs.cpp
@@ -53,12 +53,8 @@ static void fChgMode(ArgArray args) {
 
 	if (args.size() == 3) {
 		Symbol *location = g_private->maps.lookupLocation(args[2].u.sym->name);
-		if (location->u.val == 0) {
-			// visited locations have non-zero values.
-			// set to an incrementing value to record the order visited.
-			int maxLocationValue = g_private->getMaxLocationValue();
-			setSymbol(location, maxLocationValue + 1);
-		}
+		g_private->setLocationAsVisited(location);
+
 		// set a game flag when visiting the police station.
 		if (!g_private->isDemo()) {
 			if (*(args[2].u.sym->name) == g_private->getPoliceStationLocation()) {
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index bdbcccce9d0..6db85c1e491 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -1108,7 +1108,7 @@ void PrivateEngine::addMemory(const Common::String &path) {
 
 	uint locationIndex = 0;
 	for (auto &it : maps.locationList) {
-		const Private::Symbol *sym = maps.locations.getVal(it);
+		Private::Symbol *sym = maps.locations.getVal(it);
 		locationIndex++;
 
 		Common::String currentLocation = it.substr(9);
@@ -1146,7 +1146,13 @@ void PrivateEngine::addMemory(const Common::String &path) {
 		}
 
 		currentLocation.toLowercase();
-		if (sym->u.val && currentLocation == location) {
+		if (currentLocation == location) {
+			// Ensure that the location is marked as visited.
+			// Police station video spoc00xs can be played before the
+			// police station has been visited if the player has not
+			// been busted by the police yet.
+			setLocationAsVisited(sym);
+
 			diaryPage.locationID = locationIndex;
 			break;
 		}
@@ -2474,6 +2480,15 @@ void PrivateEngine::loadMemories(const Common::Rect &rect, uint rightPageOffset,
 	}
 }
 
+void PrivateEngine::setLocationAsVisited(Symbol *location) {
+	if (location->u.val == 0) {
+		// visited locations have non-zero values.
+		// set to an incrementing value to record the order visited.
+		int maxLocationValue = getMaxLocationValue();
+		setSymbol(location, maxLocationValue + 1);
+	}
+}
+
 int PrivateEngine::getMaxLocationValue() {
 	int maxValue = 0;
 	for (SymbolMap::iterator it = maps.locations.begin(); it != maps.locations.end(); ++it) {
diff --git a/engines/private/private.h b/engines/private/private.h
index 21b6b97e2c1..78a99c05c6a 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -362,6 +362,7 @@ public:
 	Common::Array<MaskInfo> _locationMasks;
 	Common::Array<MaskInfo> _memoryMasks;
 	bool selectMemory(const Common::Point &mousePos);
+	void setLocationAsVisited(Symbol *location);
 	int getMaxLocationValue();
 
 	// Save/Load games


Commit: 810266ddc818db1a59efc8059e62716f4152fcaf
    https://github.com/scummvm/scummvm/commit/810266ddc818db1a59efc8059e62716f4152fcaf
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-11-24T08:15:06-08:00

Commit Message:
PRIVATE: Clear diary page exits

Fixes diary page exits staying active during game

Bug ##16341

Changed paths:
    engines/private/private.cpp
    engines/private/private.h


diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index 6db85c1e491..3c00ff9bce8 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -529,6 +529,8 @@ void PrivateEngine::clearAreas() {
 	_dossierPrevSuspectMask.clear();
 	_dossierNextSheetMask.clear();
 	_dossierPrevSheetMask.clear();
+	_diaryNextPageExit.clear();
+	_diaryPrevPageExit.clear();
 
 	for (uint d = 0 ; d < 3; d++) {
 		if (_safeDigitArea[d].surf) {
diff --git a/engines/private/private.h b/engines/private/private.h
index 78a99c05c6a..24ecc4eddb2 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -82,6 +82,12 @@ typedef struct ExitInfo {
 	Common::String nextSetting;
 	Common::Rect   rect;
 	Common::String cursor;
+
+	void clear() {
+		nextSetting.clear();
+		rect.setEmpty();
+		cursor.clear();
+	}
 } ExitInfo;
 
 typedef struct MaskInfo {


Commit: 267e3c826ba2090edacef1b7f11dceb7a02c56b0
    https://github.com/scummvm/scummvm/commit/267e3c826ba2090edacef1b7f11dceb7a02c56b0
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-11-24T08:15:19-08:00

Commit Message:
PRIVATE: Hide dossier arrows when unavailable

Changed paths:
    engines/private/funcs.cpp
    engines/private/private.cpp


diff --git a/engines/private/funcs.cpp b/engines/private/funcs.cpp
index d79c4f53603..1b648f7de5b 100644
--- a/engines/private/funcs.cpp
+++ b/engines/private/funcs.cpp
@@ -315,6 +315,10 @@ static void fDossierPrevSuspect(ArgArray args) {
 	Common::String s(args[0].u.str);
 	MaskInfo m;
 
+	if (g_private->_dossierSuspect == 0) {
+		return;
+	}
+
 	int x = args[1].u.val;
 	int y = args[2].u.val;
 
@@ -332,6 +336,10 @@ static void fDossierNextSuspect(ArgArray args) {
 	Common::String s(args[0].u.str);
 	MaskInfo m;
 
+	if ((g_private->_dossierSuspect + 1) >= g_private->_dossiers.size()) {
+		return;
+	}
+
 	int x = args[1].u.val;
 	int y = args[2].u.val;
 
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index 3c00ff9bce8..e2274672107 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -1306,10 +1306,9 @@ bool PrivateEngine::selectDossierNextSuspect(Common::Point mousePos) {
 			playSound(getPaperShuffleSound(), 1, false, false);
 			_dossierSuspect++;
 			_dossierPage = 0;
-			loadDossier();
-			drawMask(_dossierNextSuspectMask.surf);
-			drawMask(_dossierPrevSuspectMask.surf);
-			drawScreen();
+			
+			// reload kDossierOpen
+			_nextSetting = _currentSetting;
 		}
 		return true;
 	}
@@ -1324,10 +1323,9 @@ bool PrivateEngine::selectDossierPrevSheet(Common::Point mousePos) {
 		if (_dossierPage == 1) {
 			playSound(getPaperShuffleSound(), 1, false, false);
 			_dossierPage = 0;
-			loadDossier();
-			drawMask(_dossierNextSuspectMask.surf);
-			drawMask(_dossierPrevSuspectMask.surf);
-			drawScreen();
+			
+			// reload kDossierOpen
+			_nextSetting = _currentSetting;
 		}
 		return true;
 	}
@@ -1343,10 +1341,9 @@ bool PrivateEngine::selectDossierNextSheet(Common::Point mousePos) {
 		if (_dossierPage == 0 && !m.page2.empty()) {
 			playSound(getPaperShuffleSound(), 1, false, false);
 			_dossierPage = 1;
-			loadDossier();
-			drawMask(_dossierNextSuspectMask.surf);
-			drawMask(_dossierPrevSuspectMask.surf);
-			drawScreen();
+			
+			// reload kDossierOpen
+			_nextSetting = _currentSetting;
 		}
 		return true;
 	}
@@ -1362,10 +1359,9 @@ bool PrivateEngine::selectDossierPrevSuspect(Common::Point mousePos) {
 			playSound(getPaperShuffleSound(), 1, false, false);
 			_dossierSuspect--;
 			_dossierPage = 0;
-			loadDossier();
-			drawMask(_dossierNextSuspectMask.surf);
-			drawMask(_dossierPrevSuspectMask.surf);
-			drawScreen();
+			
+			// reload kDossierOpen
+			_nextSetting = _currentSetting;
 		}
 		return true;
 	}


Commit: 160ea573885c3a47dc813d4da4515bbde3ed90b1
    https://github.com/scummvm/scummvm/commit/160ea573885c3a47dc813d4da4515bbde3ed90b1
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-11-24T08:15:35-08:00

Commit Message:
PRIVATE: Improve dossier navigation

Fixes two small issues:
- The "next sheet" cursor appeared even when there was no next sheet
- The dossier page acted like an exit hotspot

Changed paths:
    engines/private/funcs.cpp
    engines/private/private.cpp
    engines/private/private.h


diff --git a/engines/private/funcs.cpp b/engines/private/funcs.cpp
index 1b648f7de5b..6a1178e1d55 100644
--- a/engines/private/funcs.cpp
+++ b/engines/private/funcs.cpp
@@ -291,6 +291,11 @@ static void fDossierChgSheet(ArgArray args) {
 	Common::String s(args[0].u.str);
 	MaskInfo m;
 
+	// do nothing if suspect only has one sheet
+	if (g_private->_dossiers[g_private->_dossierSuspect].page2.empty()) {
+		return;
+	}
+
 	int p = args[1].u.val;
 	int x = args[2].u.val;
 	int y = args[3].u.val;
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index e2274672107..1267e0a87ab 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -105,6 +105,7 @@ PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
 	// Dossiers
 	_dossierPage = 0;
 	_dossierSuspect = 0;
+	_dossierPageMask.clear();
 	_dossierNextSuspectMask.clear();
 	_dossierPrevSuspectMask.clear();
 	_dossierNextSheetMask.clear();
@@ -380,6 +381,8 @@ Common::Error PrivateEngine::run() {
 					break;
 				else if (selectDossierPrevSheet(mousePos))
 					break;
+				else if (selectDossierPage(mousePos))
+					break;
 				else if (selectSafeDigit(mousePos))
 					break;
 				else if (selectDiaryNextPage(mousePos))
@@ -525,6 +528,7 @@ void PrivateEngine::clearAreas() {
 	_policeRadioArea.clear();
 	_AMRadioArea.clear();
 	_phoneArea.clear();
+	_dossierPageMask.clear();
 	_dossierNextSuspectMask.clear();
 	_dossierPrevSuspectMask.clear();
 	_dossierNextSheetMask.clear();
@@ -1286,15 +1290,31 @@ void PrivateEngine::loadDossier() {
 	int x = 40;
 	int y = 30;
 
-	DossierInfo m = _dossiers[_dossierSuspect];
+	MaskInfo m;
+	DossierInfo d = _dossiers[_dossierSuspect];
 
 	if (_dossierPage == 0) {
-		loadImage(m.page1, x, y);
+		m.surf = loadMask(d.page1, x, y, true);
 	} else if (_dossierPage == 1) {
-		loadImage(m.page2, x, y);
+		m.surf = loadMask(d.page2, x, y, true);
 	} else {
 		error("Invalid page");
 	}
+
+	m.cursor = "default";
+	_dossierPageMask = m;
+	_masks.push_back(m); // not push_front, as this occurs after DossierChgSheet
+}
+
+bool PrivateEngine::selectDossierPage(Common::Point mousePos) {
+	if (_dossierPageMask.surf == nullptr) {
+		return false;
+	}
+
+	if (inMask(_dossierPageMask.surf, mousePos)) {
+		return true;
+	}
+	return false;
 }
 
 bool PrivateEngine::selectDossierNextSuspect(Common::Point mousePos) {
diff --git a/engines/private/private.h b/engines/private/private.h
index 24ecc4eddb2..cbe9678d03f 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -321,10 +321,12 @@ public:
 	DossierArray _dossiers;
 	uint _dossierSuspect;
 	uint _dossierPage;
+	MaskInfo _dossierPageMask;
 	MaskInfo _dossierNextSuspectMask;
 	MaskInfo _dossierPrevSuspectMask;
 	MaskInfo _dossierNextSheetMask;
 	MaskInfo _dossierPrevSheetMask;
+	bool selectDossierPage(Common::Point);
 	bool selectDossierNextSuspect(Common::Point);
 	bool selectDossierPrevSuspect(Common::Point);
 	bool selectDossierNextSheet(Common::Point);


Commit: 2bfda36ae8bd8f4feb390cd4006424cb5c611628
    https://github.com/scummvm/scummvm/commit/2bfda36ae8bd8f4feb390cd4006424cb5c611628
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-11-24T08:15:55-08:00

Commit Message:
PRIVATE: Add mapping for Japanese Windows cursors

Changed paths:
    engines/private/cursors.cpp


diff --git a/engines/private/cursors.cpp b/engines/private/cursors.cpp
index a74c9d47ea1..7d739d2a407 100644
--- a/engines/private/cursors.cpp
+++ b/engines/private/cursors.cpp
@@ -42,6 +42,7 @@ struct CursorEntry {
 	const char *name;
  	const char *aname;
 	uint id;
+	uint japaneseId;
 };
 
 void PrivateEngine::loadCursors() {
@@ -49,14 +50,14 @@ void PrivateEngine::loadCursors() {
 
 	if (_platform == Common::kPlatformWindows) {
 		const CursorEntry cursorIDReference[] = {
-			{ "kTurnLeft",  "k1", 23 },
-			{ "kTurnRight", "k2", 9  },
-			{ "kZoomIn",    "k3", 17 },
-			{ "kZoomOut",   "k4", 11 },
-			{ "kExit",      "k5", 7  },
-			{ "kPhone",     "k6", 25 },
-			{ "kInventory", "k7", 19 },
-			{ nullptr, nullptr,   0  }
+			{ "kTurnLeft",  "k1", 23, 17 },
+			{ "kTurnRight", "k2", 9,  3  },
+			{ "kZoomIn",    "k3", 17, 11 },
+			{ "kZoomOut",   "k4", 11, 5  },
+			{ "kExit",      "k5", 7,  1  },
+			{ "kPhone",     "k6", 25, 19 },
+			{ "kInventory", "k7", 19, 13 },
+			{ nullptr, nullptr,   0,  0  }
 		};
 
 		Common::WinResources *exe = nullptr;
@@ -98,7 +99,8 @@ void PrivateEngine::loadCursors() {
 
 			const CursorEntry *entry = cursorIDReference;
 			while (entry->name != nullptr) {
-				if (entry->id == _cursors[i].winCursorGroup->cursors[0].id.getID()) {
+				uint entryId = (_language == Common::JA_JPN) ? entry->japaneseId : entry->id;
+				if (entryId == _cursors[i].winCursorGroup->cursors[0].id.getID()) {
 					_cursors[i].name = entry->name;
 					_cursors[i].aname = entry->aname;
 					break;
@@ -111,14 +113,14 @@ void PrivateEngine::loadCursors() {
 		delete exeStream;
 	} else {
 		const CursorEntry cursorIDReference[] = {
-			{ "kTurnLeft",  "k1", 133 },
-			{ "kTurnRight", "k2", 132 },
-			{ "kZoomIn",    "k3", 138 },
-			{ "kZoomOut",   "k4", 135 },
-			{ "kExit",      "k5", 130 },
-			{ "kPhone",     "k6", 141 },
-			{ "kInventory", "k7", 139 },
-			{ nullptr, nullptr,   0   }
+			{ "kTurnLeft",  "k1", 133, 0 },
+			{ "kTurnRight", "k2", 132, 0 },
+			{ "kZoomIn",    "k3", 138, 0 },
+			{ "kZoomOut",   "k4", 135, 0 },
+			{ "kExit",      "k5", 130, 0 },
+			{ "kPhone",     "k6", 141, 0 },
+			{ "kInventory", "k7", 139, 0 },
+			{ nullptr, nullptr,   0,   0 }
 		};
 
 		Common::MacResManager resMan;




More information about the Scummvm-git-logs mailing list