[Scummvm-git-logs] scummvm master -> f84c8aec71f2f215e1512dec28f544e693898bf3

neuromancer noreply at scummvm.org
Sun Nov 23 07:15:06 UTC 2025


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:
f84c8aec71 PRIVATE: Finish implementing PoliceBust and BustMovie


Commit: f84c8aec71f2f215e1512dec28f544e693898bf3
    https://github.com/scummvm/scummvm/commit/f84c8aec71f2f215e1512dec28f544e693898bf3
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-11-23T08:15:01+01: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;




More information about the Scummvm-git-logs mailing list