[Scummvm-git-logs] scummvm master -> 7a5326ad82f3a5991d4435d1f2003641481132e5

sev- noreply at scummvm.org
Sun Mar 1 12:20:24 UTC 2026


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

Summary:
5f4f2857b5 ALG: Refactoring, moved game-specific fields
7444a45dd9 ALG: Bugfixes for Graphics, script bugfix for SP
51a5e63af7 ALG: Refactoring, improve support for LBH
3d5a98788b ALG: Refactoring, further implementations for LBH
8ef78e5e6d ALG: Bugfixes for LBH and SP, refactoring
90a83d7322 ALG: minor style cleanup
597d22e7f0 ALG: fix clang warnings of unused fields
854c9aa33b ALG: bugfix, do not assign int to string
8e49de7a26 ALG: add GUIO docs
d3b5625ea2 ALG: enable build-by-default
7a5326ad82 NEWS: add ALG games


Commit: 5f4f2857b57088226d720fb177500b0eae711aa2
    https://github.com/scummvm/scummvm/commit/5f4f2857b57088226d720fb177500b0eae711aa2
Author: loki (loki at localhost)
Date: 2026-03-01T13:20:13+01:00

Commit Message:
ALG: Refactoring, moved game-specific fields

Changed paths:
    engines/alg/game.cpp
    engines/alg/game.h
    engines/alg/logic/game_bountyhunter.cpp
    engines/alg/logic/game_bountyhunter.h
    engines/alg/logic/game_crimepatrol.cpp
    engines/alg/logic/game_crimepatrol.h
    engines/alg/logic/game_drugwars.cpp
    engines/alg/logic/game_drugwars.h
    engines/alg/logic/game_johnnyrock.cpp
    engines/alg/logic/game_johnnyrock.h
    engines/alg/logic/game_maddog.cpp
    engines/alg/logic/game_maddog.h
    engines/alg/logic/game_maddog2.cpp
    engines/alg/logic/game_maddog2.h
    engines/alg/logic/game_spacepirates.cpp
    engines/alg/logic/game_spacepirates.h


diff --git a/engines/alg/game.cpp b/engines/alg/game.cpp
index bee89e52426..8b9c0e4bbd4 100644
--- a/engines/alg/game.cpp
+++ b/engines/alg/game.cpp
@@ -23,7 +23,6 @@
 #include "audio/decoders/raw.h"
 #include "common/events.h"
 #include "common/substream.h"
-#include "common/timer.h"
 #include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
 
@@ -65,14 +64,6 @@ Game::~Game() {
 			delete item;
 		}
 	}
-	delete _saveSound;
-	delete _loadSound;
-	delete _easySound;
-	delete _avgSound;
-	delete _hardSound;
-	delete _skullSound;
-	delete _shotSound;
-	delete _emptySound;
 }
 
 void Game::init() {
@@ -224,10 +215,10 @@ Zone *Game::checkZonesV1(Scene *scene, Rect *&hitRect, Common::Point *point) {
 }
 
 // This is used by later games
-Zone *Game::checkZonesV2(Scene *scene, Rect *&hitRect, Common::Point *point) {
+Zone *Game::checkZonesV2(Scene *scene, Rect *&hitRect, Common::Point *point, uint8 difficulty) {
 	for (auto &zone : scene->_zones) {
-		uint32 startFrame = zone->_startFrame - (_videoFrameSkip + 1) + ((_difficulty - 1) * _videoFrameSkip);
-		uint32 endFrame = zone->_endFrame + (_videoFrameSkip - 1) - ((_difficulty - 1) * _videoFrameSkip);
+		uint32 startFrame = zone->_startFrame - (_videoFrameSkip + 1) + ((difficulty - 1) * _videoFrameSkip);
+		uint32 endFrame = zone->_endFrame + (_videoFrameSkip - 1) - ((difficulty - 1) * _videoFrameSkip);
 		if (_currentFrame >= startFrame && _currentFrame <= endFrame) {
 			hitRect = checkZone(zone, point);
 			if (hitRect != nullptr) {
@@ -289,30 +280,6 @@ int8 Game::skipToNewScene(Scene *scene) {
 	return 0;
 }
 
-uint16 Game::randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude) {
-	if (max == 1) {
-		return 0;
-	}
-	// reset mask if full
-	uint16 fullMask = 0xFFFF >> (16 - max);
-	if (*mask == fullMask) {
-		*mask = 0;
-	}
-	uint16 randomNum = 0;
-	// find an unused random number
-	while (true) {
-		randomNum = _rnd->getRandomNumber(max - 1);
-		// check if bit is already used
-		uint16 bit = 1 << randomNum;
-		if (!((*mask & bit) || randomNum == exclude)) {
-			// set the bit in mask
-			*mask |= bit;
-			break;
-		}
-	}
-	return randomNum;
-}
-
 // Sound
 Audio::SeekableAudioStream *Game::loadSoundFile(const Common::Path &path) {
 	Common::File *file = new Common::File();
@@ -332,73 +299,6 @@ void Game::playSound(Audio::SeekableAudioStream *stream) {
 	}
 }
 
-void Game::doDiffSound(uint8 difficulty) {
-	switch (difficulty) {
-	case 1:
-		return playSound(_easySound);
-	case 2:
-		return playSound(_avgSound);
-	case 3:
-		return playSound(_hardSound);
-	}
-}
-
-void Game::doSaveSound() {
-	playSound(_saveSound);
-}
-
-void Game::doLoadSound() {
-	playSound(_loadSound);
-}
-
-void Game::doSkullSound() {
-	playSound(_skullSound);
-}
-
-void Game::doShot() {
-	playSound(_shotSound);
-}
-
-// Timer
-static void cursorTimerCallback(void *refCon) {
-	Game *game = static_cast<Game *>(refCon);
-	game->runCursorTimer();
-}
-
-void Game::setupCursorTimer() {
-	g_system->getTimerManager()->installTimerProc(&cursorTimerCallback, 1000000 / 50, (void *)this, "cursortimer");
-}
-
-void Game::removeCursorTimer() {
-	g_system->getTimerManager()->removeTimerProc(&cursorTimerCallback);
-}
-
-void Game::runCursorTimer() {
-	_thisGameTimer += 2;
-	if (_whichGun == 9) {
-		if (_emptyCount > 0) {
-			_emptyCount--;
-		} else {
-			_whichGun = 0;
-		}
-	} else {
-		if (_shotFired) {
-			_whichGun++;
-			if (_whichGun > 5) {
-				_whichGun = 0;
-				_shotFired = false;
-			}
-		} else {
-			if (_inHolster > 0) {
-				_inHolster--;
-				if (_inHolster == 0 && _whichGun == 7) {
-					_whichGun = 6;
-				}
-			}
-		}
-	}
-}
-
 // Script functions: Zone
 void Game::zoneGlobalHit(Common::Point *point) {
 	// do nothing
@@ -409,32 +309,6 @@ void Game::rectHitDoNothing(Rect *rect) {
 	// do nothing
 }
 
-void Game::rectNewScene(Rect *rect) {
-	_score += rect->_score;
-	if (!rect->_scene.empty()) {
-		_curScene = rect->_scene;
-	}
-}
-
-void Game::rectEasy(Rect *rect) {
-	doDiffSound(1);
-	_difficulty = 1;
-}
-
-void Game::rectAverage(Rect *rect) {
-	doDiffSound(2);
-	_difficulty = 2;
-}
-
-void Game::rectHard(Rect *rect) {
-	doDiffSound(3);
-	_difficulty = 3;
-}
-
-void Game::rectExit(Rect *rect) {
-	shutdown();
-}
-
 // Script functions: Scene PreOps
 void Game::scenePsoDrawRct(Scene *scene) {
 }
@@ -519,13 +393,6 @@ void Game::sceneSmDonothing(Scene *scene) {
 	// do nothing
 }
 
-// Script functions: ScnScr
-void Game::sceneDefaultScore(Scene *scene) {
-	if (scene->_scnscrParam > 0) {
-		_score += scene->_scnscrParam;
-	}
-}
-
 // Script functions: ScnNxtFrm
 void Game::sceneNxtfrm(Scene *scene) {
 }
@@ -534,7 +401,7 @@ void Game::sceneNxtfrm(Scene *scene) {
 void Game::debug_drawZoneRects() {
 	if (_debug_drawRects || debugChannelSet(1, Alg::kAlgDebugGraphics)) {
 		if (_inMenu) {
-			for (auto rect : _submenzone->_rects) {
+			for (auto rect : _subMenuZone->_rects) {
 				_screen->drawLine(rect->left, rect->top, rect->right, rect->top, 1);
 				_screen->drawLine(rect->left, rect->top, rect->left, rect->bottom, 1);
 				_screen->drawLine(rect->right, rect->bottom, rect->right, rect->top, 1);
diff --git a/engines/alg/game.h b/engines/alg/game.h
index 783c770e111..25667054121 100644
--- a/engines/alg/game.h
+++ b/engines/alg/game.h
@@ -41,7 +41,6 @@ public:
 	virtual void init();
 	virtual Common::Error run();
 	bool debug_dumpLibFile();
-	void runCursorTimer();
 	bool _debug_drawRects = false;
 	bool _debug_godMode = false;
 	bool _debug_unlimitedAmmo = false;
@@ -63,19 +62,10 @@ protected:
 	Common::Array<Graphics::Surface *> *_gun;
 	Common::Array<Graphics::Surface *> *_numbers;
 
-	Audio::SeekableAudioStream *_saveSound = nullptr;
-	Audio::SeekableAudioStream *_loadSound = nullptr;
-	Audio::SeekableAudioStream *_easySound = nullptr;
-	Audio::SeekableAudioStream *_avgSound = nullptr;
-	Audio::SeekableAudioStream *_hardSound = nullptr;
-	Audio::SeekableAudioStream *_skullSound = nullptr;
-	Audio::SeekableAudioStream *_shotSound = nullptr;
-	Audio::SeekableAudioStream *_emptySound = nullptr;
-
 	Audio::SoundHandle _sfxAudioHandle;
 
-	Zone *_menuzone;
-	Zone *_submenzone;
+	Zone *_menuZone;
+	Zone *_subMenuZone;
 
 	bool _leftDown = false;
 	bool _rightDown = false;
@@ -95,33 +85,17 @@ protected:
 	bool fired(Common::Point *point);
 	Rect *checkZone(Zone *zone, Common::Point *point);
 	Zone *checkZonesV1(Scene *scene, Rect *&hitRect, Common::Point *point);
-	Zone *checkZonesV2(Scene *scene, Rect *&hitRect, Common::Point *point);
+	Zone *checkZonesV2(Scene *scene, Rect *&hitRect, Common::Point *point, uint8 difficulty);
 	uint32 getFrame(Scene *scene);
 	void adjustDifficulty(uint8 newDifficulty, uint8 oldDifficulty);
 	int8 skipToNewScene(Scene *scene);
-	uint16 randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude);
 	void debug_drawZoneRects();
 
-	// Sounds
-	void doDiffSound(uint8 difficulty);
-	void doSaveSound();
-	void doLoadSound();
-	void doSkullSound();
-	void doShot();
-
-	// Timer
-	void setupCursorTimer();
-	void removeCursorTimer();
-
 	// Script functions: Zone
 	void zoneGlobalHit(Common::Point *point);
 	// Script functions: RectHit
 	void rectHitDoNothing(Rect *rect);
 	void rectNewScene(Rect *rect);
-	void rectExit(Rect *rect);
-	void rectEasy(Rect *rect);
-	void rectAverage(Rect *rect);
-	void rectHard(Rect *rect);
 	// Script functions: Scene PreOps
 	void scenePsoDrawRct(Scene *scene);
 	void scenePsoPause(Scene *scene);
@@ -139,45 +113,23 @@ protected:
 	void sceneDefaultNxtscn(Scene *scene);
 	// Script functions: ShowMsg
 	void sceneSmDonothing(Scene *scene);
-	// Script functions: ScnScr
-	void sceneDefaultScore(Scene *scene);
 	// Script functions: ScnNxtFrm
 	void sceneNxtfrm(Scene *scene);
 
 	bool _buttonDown = false;
-	uint8 _difficulty = 1;
-	uint8 _emptyCount = 0;
-	bool _fired = false;
+	bool _fired = 0;
 	uint32 _currentFrame;
 	bool _gameInProgress = false;
-	uint32 _thisGameTimer = 0;
 	bool _hadPause = false;
-	bool _holster = false;
 	bool _inMenu = false;
-	uint8 _inHolster = 0;
-	int8 _lives = 0;
-	long int _minF;
-	long int _maxF;
-	uint8 _oldWhichGun = 0xFF;
-	uint8 _oldDifficulty = 1;
-	int8 _oldLives = 0;
-	int32 _oldScore = -1;
-	uint8 _oldShots = 0;
 	uint32 _pauseTime = 0;
 	bool _sceneSkipped = false;
-	int32 _score = 0;
-	bool _shotFired = false;
-	uint16 _shots = 0;
 	uint32 _videoFrameSkip = 3;
 	uint32 _nextFrameTime = 0;
 	uint16 _videoPosX;
 	uint16 _videoPosY;
-	uint8 _whichGun = 0;
 
 	Common::String _curScene;
-	Common::String _subScene;
-	Common::String _retScene;
-	Common::String _lastScene;
 	Common::String _startScene;
 };
 
diff --git a/engines/alg/logic/game_bountyhunter.cpp b/engines/alg/logic/game_bountyhunter.cpp
index c30f9c9f26f..e57f584523b 100644
--- a/engines/alg/logic/game_bountyhunter.cpp
+++ b/engines/alg/logic/game_bountyhunter.cpp
@@ -88,7 +88,12 @@ GameBountyHunter::~GameBountyHunter() {
 		item->free();
 		delete item;
 	}
+	delete _saveSound;
+	delete _loadSound;
+	delete _skullSound;
+	delete _shotSound;
 	delete _shotgunSound;
+	delete _emptySound;
 }
 
 void GameBountyHunter::init() {
@@ -109,23 +114,23 @@ void GameBountyHunter::init() {
 	registerScriptFunctions();
 	verifyScriptFunctions();
 
-	_menuzone = new Zone("MainMenu", "GLOBALHIT");
-	_menuzone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
+	_menuZone = new Zone("MainMenu", "GLOBALHIT");
+	_menuZone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
 
-	_submenzone = new Zone("SubMenu", "GLOBALHIT");
-	_submenzone->addRect(0, 0, 0x78, 0x3C, nullptr, 0, "STARTMENU", "0");
-	_submenzone->addRect(0xC8, 0, 0x0140, 0x3C, nullptr, 0, "RECTLOAD", "0");
-	_submenzone->addRect(0xC8, 0x3C, 0x0140, 0x78, nullptr, 0, "RECTSAVE", "0");
-	_submenzone->addRect(0, 0x3C, 0x78, 0x78, nullptr, 0, "CONTMENU", "0");
-	_submenzone->addRect(0, 0x78, 0x78, 0xB4, nullptr, 0, "EXITMENU", "0");
-	_submenzone->addRect(0xC8, 0x78, 0x0140, 0xB4, nullptr, 0, "TOGGLEPLAYERS", "0");
+	_subMenuZone = new Zone("SubMenu", "GLOBALHIT");
+	_subMenuZone->addRect(0, 0, 0x78, 0x3C, nullptr, 0, "STARTMENU", "0");
+	_subMenuZone->addRect(0xC8, 0, 0x0140, 0x3C, nullptr, 0, "RECTLOAD", "0");
+	_subMenuZone->addRect(0xC8, 0x3C, 0x0140, 0x78, nullptr, 0, "RECTSAVE", "0");
+	_subMenuZone->addRect(0, 0x3C, 0x78, 0x78, nullptr, 0, "CONTMENU", "0");
+	_subMenuZone->addRect(0, 0x78, 0x78, 0xB4, nullptr, 0, "EXITMENU", "0");
+	_subMenuZone->addRect(0xC8, 0x78, 0x0140, 0xB4, nullptr, 0, "TOGGLEPLAYERS", "0");
 
 	_shotSound = loadSoundFile("blow.8b");
+	_shotgunSound = loadSoundFile("shotgun.8b");
 	_emptySound = loadSoundFile("empty.8b");
 	_saveSound = loadSoundFile("saved.8b");
 	_loadSound = loadSoundFile("loaded.8b");
 	_skullSound = loadSoundFile("skull.8b");
-	_shotgunSound = loadSoundFile("shotgun.8b");
 
 	_gun = AlgGraphics::loadScreenCoordAniImage("bh_gun.ani", _palette);
 	_shotgun = AlgGraphics::loadScreenCoordAniImage("bh_buck.ani", _palette);
@@ -390,7 +395,7 @@ Common::Error GameBountyHunter::run() {
 			Common::Point firedCoords;
 			if (fired(&firedCoords)) {
 				if (!_holster) {
-					Rect *hitGlobalRect = checkZone(_menuzone, &firedCoords);
+					Rect *hitGlobalRect = checkZone(_menuZone, &firedCoords);
 					if (hitGlobalRect != nullptr) {
 						callScriptFunctionRectHit(hitGlobalRect->_rectHit, hitGlobalRect);
 					} else {
@@ -399,9 +404,9 @@ Common::Error GameBountyHunter::run() {
 								_playerShots[_player]--;
 							}
 							displayShotFiredImage(&firedCoords);
-							doShot();
+							playSound(_shotSound);
 							Rect *hitRect = nullptr;
-							Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords);
+							Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords, _difficulty);
 							if (hitSceneZone != nullptr) {
 								callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
 							} else {
@@ -487,7 +492,7 @@ void GameBountyHunter::doMenu() {
 	while (_inMenu && !_vm->shouldQuit()) {
 		Common::Point firedCoords;
 		if (fired(&firedCoords)) {
-			Rect *hitMenuRect = checkZone(_submenzone, &firedCoords);
+			Rect *hitMenuRect = checkZone(_subMenuZone, &firedCoords);
 			if (hitMenuRect != nullptr) {
 				callScriptFunctionRectHit(hitMenuRect->_rectHit, hitMenuRect);
 			}
@@ -703,6 +708,30 @@ uint16 GameBountyHunter::beginLevel(uint8 levelNumber) {
 	return sceneNum;
 }
 
+uint16 GameBountyHunter::randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude) {
+	if (max == 1) {
+		return 0;
+	}
+	// reset mask if full
+	uint16 fullMask = 0xFFFF >> (16 - max);
+	if (*mask == fullMask) {
+		*mask = 0;
+	}
+	uint16 randomNum = 0;
+	// find an unused random number
+	while (1) {
+		randomNum = _rnd->getRandomNumber(max - 1);
+		// check if bit is already used
+		uint16 bit = 1 << randomNum;
+		if (!((*mask & bit) || randomNum == exclude)) {
+			// set the bit in mask
+			*mask |= bit;
+			break;
+		}
+	}
+	return randomNum;
+}
+
 uint16 GameBountyHunter::pickRandomScene(uint16 *sceneList, uint8 max) {
 	if (max == 0) {
 		return 0;
@@ -802,19 +831,26 @@ void GameBountyHunter::doShotgunSound() {
 }
 
 // Script functions: RectHit
+void GameBountyHunter::rectNewScene(Rect *rect) {
+	_score += rect->_score;
+	if (!rect->_scene.empty()) {
+		_curScene = rect->_scene;
+	}
+}
+
 void GameBountyHunter::rectShotMenu(Rect *rect) {
 	doMenu();
 }
 
 void GameBountyHunter::rectSave(Rect *rect) {
 	if (saveState()) {
-		doSaveSound();
+		playSound(_saveSound);
 	}
 }
 
 void GameBountyHunter::rectLoad(Rect *rect) {
 	if (loadState()) {
-		doLoadSound();
+		playSound(_loadSound);
 	}
 	setNextScene(_restartScene);
 	_restartScene = 0;
@@ -852,6 +888,22 @@ void GameBountyHunter::rectStart(Rect *rect) {
 	newGame();
 }
 
+void GameBountyHunter::rectEasy(Rect *rect) {
+	_difficulty = 1;
+}
+
+void GameBountyHunter::rectAverage(Rect *rect) {
+	_difficulty = 2;
+}
+
+void GameBountyHunter::rectHard(Rect *rect) {
+	_difficulty = 3;
+}
+
+void GameBountyHunter::rectExit(Rect *rect) {
+	shutdown();
+}
+
 void GameBountyHunter::rectTogglePlayers(Rect *rect) {
 	if (_numPlayers == 1) {
 		_numPlayers = 2;
@@ -867,7 +919,7 @@ void GameBountyHunter::rectTogglePlayers(Rect *rect) {
 		AlgGraphics::drawImage(_screen, _textMenuIcon, 0x0C, 0xBF);
 		AlgGraphics::drawImage(_screen, _textBlackBarIcon, 0x50, 0xBE);
 	}
-	doSkullSound();
+	playSound(_skullSound);
 	_screen->copyRectToSurface(_background->getBasePtr(_videoPosX, _videoPosY), _background->pitch, _videoPosX, _videoPosY, _videoDecoder->getWidth(), _videoDecoder->getHeight());
 }
 
@@ -1313,6 +1365,13 @@ void GameBountyHunter::sceneDefaultWepdwn(Scene *scene) {
 	}
 }
 
+// Script functions: ScnScr
+void GameBountyHunter::sceneDefaultScore(Scene *scene) {
+	if (scene->_scnscrParam > 0) {
+		_score += scene->_scnscrParam;
+	}
+}
+
 // Debug methods
 void GameBountyHunter::debugWarpTo(int val) {
 	// TODO implement
diff --git a/engines/alg/logic/game_bountyhunter.h b/engines/alg/logic/game_bountyhunter.h
index 98ecc7c2673..55d1ac769f1 100644
--- a/engines/alg/logic/game_bountyhunter.h
+++ b/engines/alg/logic/game_bountyhunter.h
@@ -91,7 +91,12 @@ private:
 	Common::Array<Graphics::Surface *> *_shotgun;
 
 	// sounds
+	Audio::SeekableAudioStream *_saveSound = nullptr;
+	Audio::SeekableAudioStream *_loadSound = nullptr;
+	Audio::SeekableAudioStream *_skullSound = nullptr;
+	Audio::SeekableAudioStream *_shotSound = nullptr;
 	Audio::SeekableAudioStream *_shotgunSound = nullptr;
+	Audio::SeekableAudioStream *_emptySound = nullptr;
 
 	// constants
 	uint16 _randomHarry1[7] = {0x01B9, 0x01B7, 0x01B5, 0x01B3, 0x01AF, 0x01AD, 0};
@@ -128,6 +133,18 @@ private:
 	const uint16 _allPlayersDead = 0x108;
 
 	// gamestate
+	uint8 _difficulty = 1;
+	uint8 _oldDifficulty = 1;
+	bool _holster = false;
+	int8 _lives = 0;
+	int8 _oldLives = 0;
+	int32 _score = 0;
+	int32 _oldScore = -1;
+	uint16 _shots = 0;
+	uint8 _oldShots = 0;
+	uint8 _whichGun = 0;
+	uint8 _oldWhichGun = 0xFF;
+
 	uint16 _restartScene = 0;
 	uint8 _numPlayers = 1;
 	uint8 _player = 0;
@@ -142,6 +159,7 @@ private:
 	uint8 _levelDoneMask = 0;
 	uint8 _numSubLevelsDone = 0;
 
+	// TODO remove?
 	// uint16 _usedScenes = 0;
 	// int16 _lastPick = -1;
 	// int16 _initted = 0;
@@ -175,6 +193,8 @@ private:
 
 	uint8 _unk_2ADA6 = 0;
 
+	Common::String _subScene;
+
 	// base functions
 	void newGame();
 	void doMenu();
@@ -195,6 +215,7 @@ private:
 	void iconShotgun();
 	void iconReset();
 	uint16 beginLevel(uint8 levelNumber);
+	uint16 randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude);
 	uint16 pickRandomScene(uint16 *sceneList, uint8 max);
 	uint16 pickGunfightScene();
 	uint16 pickInnocentScene();
@@ -204,11 +225,16 @@ private:
 	void doShotgunSound();
 
 	// Script functions: RectHit
+	void rectNewScene(Rect *rect);
 	void rectShotMenu(Rect *rect);
 	void rectSave(Rect *rect);
 	void rectLoad(Rect *rect);
 	void rectContinue(Rect *rect);
 	void rectStart(Rect *rect);
+	void rectEasy(Rect *rect);
+	void rectAverage(Rect *rect);
+	void rectHard(Rect *rect);
+	void rectExit(Rect *rect);
 	void rectTogglePlayers(Rect *rect);
 	void rectHitIconJug(Rect *rect);
 	void rectHitIconLantern(Rect *rect);
@@ -270,6 +296,9 @@ private:
 
 	// Script functions: Scene WepDwn
 	void sceneDefaultWepdwn(Scene *scene);
+
+	// Script functions: ScnScr
+	void sceneDefaultScore(Scene *scene);
 };
 
 class DebuggerBountyHunter : public GUI::Debugger {
diff --git a/engines/alg/logic/game_crimepatrol.cpp b/engines/alg/logic/game_crimepatrol.cpp
index d77abace7e8..800d1b90f36 100644
--- a/engines/alg/logic/game_crimepatrol.cpp
+++ b/engines/alg/logic/game_crimepatrol.cpp
@@ -60,6 +60,11 @@ GameCrimePatrol::~GameCrimePatrol() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	delete _saveSound;
+	delete _loadSound;
+	delete _skullSound;
+	delete _shotSound;
+	delete _emptySound;
 }
 
 void GameCrimePatrol::init() {
@@ -82,18 +87,18 @@ void GameCrimePatrol::init() {
 	registerScriptFunctions();
 	verifyScriptFunctions();
 
-	_menuzone = new Zone("MainMenu", "GLOBALHIT");
-	_menuzone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
+	_menuZone = new Zone("MainMenu", "GLOBALHIT");
+	_menuZone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
 
-	_submenzone = new Zone("SubMenu", "GLOBALHIT");
-	_submenzone->addRect(0x1C, 0x11, 0x5D, 0x20, nullptr, 0, "STARTMENU", "0");
-	_submenzone->addRect(0x1C, 0x31, 0x5D, 0x40, nullptr, 0, "RECTLOAD", "0");
-	_submenzone->addRect(0x1C, 0x51, 0x5D, 0x60, nullptr, 0, "RECTSAVE", "0");
-	_submenzone->addRect(0x1C, 0x71, 0x5D, 0x80, nullptr, 0, "CONTMENU", "0");
-	_submenzone->addRect(0x1C, 0x91, 0x5D, 0xA0, nullptr, 0, "EXITMENU", "0");
-	_submenzone->addRect(0xDD, 0x3C, 0x010A, 0x4B, nullptr, 0, "RECTEASY", "0");
-	_submenzone->addRect(0xDD, 0x5C, 0x010A, 0x6B, nullptr, 0, "RECTAVG", "0");
-	_submenzone->addRect(0xDD, 0x7C, 0x010A, 0x8B, nullptr, 0, "RECTHARD", "0");
+	_subMenuZone = new Zone("SubMenu", "GLOBALHIT");
+	_subMenuZone->addRect(0x1C, 0x11, 0x5D, 0x20, nullptr, 0, "STARTMENU", "0");
+	_subMenuZone->addRect(0x1C, 0x31, 0x5D, 0x40, nullptr, 0, "RECTLOAD", "0");
+	_subMenuZone->addRect(0x1C, 0x51, 0x5D, 0x60, nullptr, 0, "RECTSAVE", "0");
+	_subMenuZone->addRect(0x1C, 0x71, 0x5D, 0x80, nullptr, 0, "CONTMENU", "0");
+	_subMenuZone->addRect(0x1C, 0x91, 0x5D, 0xA0, nullptr, 0, "EXITMENU", "0");
+	_subMenuZone->addRect(0xDD, 0x3C, 0x010A, 0x4B, nullptr, 0, "RECTEASY", "0");
+	_subMenuZone->addRect(0xDD, 0x5C, 0x010A, 0x6B, nullptr, 0, "RECTAVG", "0");
+	_subMenuZone->addRect(0xDD, 0x7C, 0x010A, 0x8B, nullptr, 0, "RECTHARD", "0");
 
 	_shotSound = loadSoundFile("blow.8b");
 	_emptySound = loadSoundFile("empty.8b");
@@ -339,7 +344,7 @@ Common::Error GameCrimePatrol::run() {
 			Common::Point firedCoords;
 			if (fired(&firedCoords)) {
 				if (!_holster) {
-					Rect *hitGlobalRect = checkZone(_menuzone, &firedCoords);
+					Rect *hitGlobalRect = checkZone(_menuZone, &firedCoords);
 					if (hitGlobalRect != nullptr) {
 						callScriptFunctionRectHit(hitGlobalRect->_rectHit, hitGlobalRect);
 					} else if (_shots > 0) {
@@ -347,9 +352,9 @@ Common::Error GameCrimePatrol::run() {
 							_shots--;
 						}
 						displayShotFiredImage(&firedCoords);
-						doShot();
+						playSound(_shotSound);
 						Rect *hitRect = nullptr;
-						Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords);
+						Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords, _difficulty);
 						if (hitSceneZone != nullptr) {
 							callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
 						} else {
@@ -441,7 +446,7 @@ void GameCrimePatrol::doMenu() {
 	while (_inMenu && !_vm->shouldQuit()) {
 		Common::Point firedCoords;
 		if (fired(&firedCoords)) {
-			Rect *hitMenuRect = checkZone(_submenzone, &firedCoords);
+			Rect *hitMenuRect = checkZone(_subMenuZone, &firedCoords);
 			if (hitMenuRect != nullptr) {
 				callScriptFunctionRectHit(hitMenuRect->_rectHit, hitMenuRect);
 			}
@@ -648,6 +653,30 @@ uint16 GameCrimePatrol::sceneToNumber(Scene *scene) {
 	return atoi(scene->_name.substr(5).c_str());
 }
 
+uint16 GameCrimePatrol::randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude) {
+	if (max == 1) {
+		return 0;
+	}
+	// reset mask if full
+	uint16 fullMask = 0xFFFF >> (16 - max);
+	if (*mask == fullMask) {
+		*mask = 0;
+	}
+	uint16 randomNum = 0;
+	// find an unused random number
+	while (1) {
+		randomNum = _rnd->getRandomNumber(max - 1);
+		// check if bit is already used
+		uint16 bit = 1 << randomNum;
+		if (!((*mask & bit) || randomNum == exclude)) {
+			// set the bit in mask
+			*mask |= bit;
+			break;
+		}
+	}
+	return randomNum;
+}
+
 uint16 GameCrimePatrol::pickRandomScene(uint8 index, uint8 max) {
 	if (max != 0) {
 		_randomMax = max;
@@ -726,19 +755,26 @@ void GameCrimePatrol::sceneIsoGotToGeneric(uint8 index, uint16 sceneId) {
 }
 
 // Script functions: RectHit
+void GameCrimePatrol::rectNewScene(Rect *rect) {
+	_score += rect->_score;
+	if (!rect->_scene.empty()) {
+		_curScene = rect->_scene;
+	}
+}
+
 void GameCrimePatrol::rectShotMenu(Rect *rect) {
 	doMenu();
 }
 
 void GameCrimePatrol::rectSave(Rect *rect) {
 	if (saveState()) {
-		doSaveSound();
+		playSound(_saveSound);
 	}
 }
 
 void GameCrimePatrol::rectLoad(Rect *rect) {
 	if (loadState()) {
-		doLoadSound();
+		playSound(_loadSound);
 	}
 }
 
@@ -767,6 +803,22 @@ void GameCrimePatrol::rectStart(Rect *rect) {
 	newGame();
 }
 
+void GameCrimePatrol::rectEasy(Rect *rect) {
+	_difficulty = 1;
+}
+
+void GameCrimePatrol::rectAverage(Rect *rect) {
+	_difficulty = 2;
+}
+
+void GameCrimePatrol::rectHard(Rect *rect) {
+	_difficulty = 3;
+}
+
+void GameCrimePatrol::rectExit(Rect *rect) {
+	shutdown();
+}
+
 void GameCrimePatrol::rectTargetPractice(Rect *rect) {
 	uint16 nextScene = 0;
 	Scene *scene = _sceneInfo->findScene(_curScene);
@@ -1281,6 +1333,13 @@ void GameCrimePatrol::sceneDefaultWepdwn(Scene *scene) {
 	_shots = 10;
 }
 
+// Script functions: ScnScr
+void GameCrimePatrol::sceneDefaultScore(Scene *scene) {
+	if (scene->_scnscrParam > 0) {
+		_score += scene->_scnscrParam;
+	}
+}
+
 // Debug methods
 void GameCrimePatrol::debugWarpTo(int val) {
 	// TODO implement
diff --git a/engines/alg/logic/game_crimepatrol.h b/engines/alg/logic/game_crimepatrol.h
index 373a2d821d0..bdf1aaadae7 100644
--- a/engines/alg/logic/game_crimepatrol.h
+++ b/engines/alg/logic/game_crimepatrol.h
@@ -81,6 +81,13 @@ private:
 	Graphics::Surface *_difficultyIcon;
 	Graphics::Surface *_bulletholeIcon;
 
+	// sounds
+	Audio::SeekableAudioStream *_saveSound = nullptr;
+	Audio::SeekableAudioStream *_loadSound = nullptr;
+	Audio::SeekableAudioStream *_skullSound = nullptr;
+	Audio::SeekableAudioStream *_shotSound = nullptr;
+	Audio::SeekableAudioStream *_emptySound = nullptr;
+	
 	// constants
 	const int16 _scenesLevel0[2] = {0x0191, 0};
 	const int16 _scenesLevel1[3] = {0x27, 0x01B7, 0};
@@ -133,6 +140,18 @@ private:
 	const int16 _practiceTargetBottom[5] = {0x3D, 0x79, 0x7B, 0xA1, 0xA1};
 
 	// gamestate
+	uint8 _difficulty = 1;
+	uint8 _oldDifficulty = 1;
+	bool _holster = false;
+	int8 _lives = 0;
+	int8 _oldLives = 0;
+	int32 _score = 0;
+	int32 _oldScore = -1;
+	uint16 _shots = 0;
+	uint8 _oldShots = 0;
+	uint8 _whichGun = 0;
+	uint8 _oldWhichGun = 0xFF;
+
 	uint16 _gotTo[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 	int8 _stage = 0;
 	int8 _oldStage = -1;
@@ -166,6 +185,7 @@ private:
 	void displayShotFiredImage(Common::Point *point);
 	void enableVideoFadeIn();
 	uint16 sceneToNumber(Scene *scene);
+	uint16 randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude);
 	uint16 pickRandomScene(uint8 index, uint8 max);
 	uint16 pickDeathScene();
 	void sceneNxtscnGeneric(uint8 index);
@@ -173,11 +193,16 @@ private:
 	void sceneIsoGotToGeneric(uint8 index, uint16 sceneId);
 
 	// Script functions: RectHit
+	void rectNewScene(Rect *rect);
 	void rectShotMenu(Rect *rect);
 	void rectSave(Rect *rect);
 	void rectLoad(Rect *rect);
 	void rectContinue(Rect *rect);
 	void rectStart(Rect *rect);
+	void rectEasy(Rect *rect);
+	void rectAverage(Rect *rect);
+	void rectHard(Rect *rect);
+	void rectExit(Rect *rect);
 	void rectTargetPractice(Rect *rect);
 	void rectSelectTargetPractice(Rect *rect);
 	void rectSelectGangFight(Rect *rect);
@@ -261,7 +286,11 @@ private:
 
 	// Script functions: Scene WepDwn
 	void sceneDefaultWepdwn(Scene *scene);
+
 	void debugDrawPracticeRects();
+
+	// Script functions: ScnScr
+	void sceneDefaultScore(Scene *scene);
 };
 
 class DebuggerCrimePatrol : public GUI::Debugger {
diff --git a/engines/alg/logic/game_drugwars.cpp b/engines/alg/logic/game_drugwars.cpp
index 15b45ef7bfa..eccc18345f6 100644
--- a/engines/alg/logic/game_drugwars.cpp
+++ b/engines/alg/logic/game_drugwars.cpp
@@ -60,6 +60,11 @@ GameDrugWars::~GameDrugWars() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	delete _saveSound;
+	delete _loadSound;
+	delete _skullSound;
+	delete _shotSound;
+	delete _emptySound;
 }
 
 void GameDrugWars::init() {
@@ -82,18 +87,18 @@ void GameDrugWars::init() {
 	registerScriptFunctions();
 	verifyScriptFunctions();
 
-	_menuzone = new Zone("MainMenu", "GLOBALHIT");
-	_menuzone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
+	_menuZone = new Zone("MainMenu", "GLOBALHIT");
+	_menuZone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
 
-	_submenzone = new Zone("SubMenu", "GLOBALHIT");
-	_submenzone->addRect(0x1C, 0x13, 0x5D, 0x22, nullptr, 0, "STARTMENU", "0");
-	_submenzone->addRect(0x1C, 0x33, 0x5D, 0x42, nullptr, 0, "RECTLOAD", "0");
-	_submenzone->addRect(0x1C, 0x53, 0x5D, 0x62, nullptr, 0, "RECTSAVE", "0");
-	_submenzone->addRect(0x1C, 0x73, 0x5D, 0x82, nullptr, 0, "CONTMENU", "0");
-	_submenzone->addRect(0x1C, 0x93, 0x5D, 0xA2, nullptr, 0, "EXITMENU", "0");
-	_submenzone->addRect(0xDD, 0x34, 0x10A, 0x43, nullptr, 0, "RECTEASY", "0");
-	_submenzone->addRect(0xDD, 0x55, 0x10A, 0x64, nullptr, 0, "RECTAVG", "0");
-	_submenzone->addRect(0xDD, 0x75, 0x10A, 0x84, nullptr, 0, "RECTHARD", "0");
+	_subMenuZone = new Zone("SubMenu", "GLOBALHIT");
+	_subMenuZone->addRect(0x1C, 0x13, 0x5D, 0x22, nullptr, 0, "STARTMENU", "0");
+	_subMenuZone->addRect(0x1C, 0x33, 0x5D, 0x42, nullptr, 0, "RECTLOAD", "0");
+	_subMenuZone->addRect(0x1C, 0x53, 0x5D, 0x62, nullptr, 0, "RECTSAVE", "0");
+	_subMenuZone->addRect(0x1C, 0x73, 0x5D, 0x82, nullptr, 0, "CONTMENU", "0");
+	_subMenuZone->addRect(0x1C, 0x93, 0x5D, 0xA2, nullptr, 0, "EXITMENU", "0");
+	_subMenuZone->addRect(0xDD, 0x34, 0x10A, 0x43, nullptr, 0, "RECTEASY", "0");
+	_subMenuZone->addRect(0xDD, 0x55, 0x10A, 0x64, nullptr, 0, "RECTAVG", "0");
+	_subMenuZone->addRect(0xDD, 0x75, 0x10A, 0x84, nullptr, 0, "RECTHARD", "0");
 
 	_shotSound = loadSoundFile("blow.8b");
 	_emptySound = loadSoundFile("empty.8b");
@@ -283,7 +288,7 @@ Common::Error GameDrugWars::run() {
 			Common::Point firedCoords;
 			if (fired(&firedCoords)) {
 				if (!_holster) {
-					Rect *hitGlobalRect = checkZone(_menuzone, &firedCoords);
+					Rect *hitGlobalRect = checkZone(_menuZone, &firedCoords);
 					if (hitGlobalRect != nullptr) {
 						callScriptFunctionRectHit(hitGlobalRect->_rectHit, hitGlobalRect);
 					} else if (_shots > 0) {
@@ -291,9 +296,9 @@ Common::Error GameDrugWars::run() {
 							_shots--;
 						}
 						displayShotFiredImage(&firedCoords);
-						doShot();
+						playSound(_shotSound);
 						Rect *hitRect = nullptr;
-						Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords);
+						Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords, _difficulty);
 						if (hitSceneZone != nullptr) {
 							callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
 						} else {
@@ -384,7 +389,7 @@ void GameDrugWars::doMenu() {
 	while (_inMenu && !_vm->shouldQuit()) {
 		Common::Point firedCoords;
 		if (fired(&firedCoords)) {
-			Rect *hitMenuRect = checkZone(_submenzone, &firedCoords);
+			Rect *hitMenuRect = checkZone(_subMenuZone, &firedCoords);
 			if (hitMenuRect != nullptr) {
 				callScriptFunctionRectHit(hitMenuRect->_rectHit, hitMenuRect);
 			}
@@ -600,6 +605,30 @@ uint16 GameDrugWars::sceneToNumber(Scene *scene) {
 	return atoi(scene->_name.substr(5).c_str());
 }
 
+uint16 GameDrugWars::randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude) {
+	if (max == 1) {
+		return 0;
+	}
+	// reset mask if full
+	uint16 fullMask = 0xFFFF >> (16 - max);
+	if (*mask == fullMask) {
+		*mask = 0;
+	}
+	uint16 randomNum = 0;
+	// find an unused random number
+	while (1) {
+		randomNum = _rnd->getRandomNumber(max - 1);
+		// check if bit is already used
+		uint16 bit = 1 << randomNum;
+		if (!((*mask & bit) || randomNum == exclude)) {
+			// set the bit in mask
+			*mask |= bit;
+			break;
+		}
+	}
+	return randomNum;
+}
+
 uint16 GameDrugWars::pickRandomScene(uint8 index, uint8 max) {
 	if (_randomScenes[index] == nullptr) {
 		error("GameDrugWars::pickRandomScene(): called with illegal index: %d", index);
@@ -678,19 +707,26 @@ void GameDrugWars::rectSelectGeneric(uint8 index) {
 }
 
 // Script functions: RectHit
+void GameDrugWars::rectNewScene(Rect *rect) {
+	_score += rect->_score;
+	if (!rect->_scene.empty()) {
+		_curScene = rect->_scene;
+	}
+}
+
 void GameDrugWars::rectShotMenu(Rect *rect) {
 	doMenu();
 }
 
 void GameDrugWars::rectSave(Rect *rect) {
 	if (saveState()) {
-		doSaveSound();
+		playSound(_saveSound);
 	}
 }
 
 void GameDrugWars::rectLoad(Rect *rect) {
 	if (loadState()) {
-		doLoadSound();
+		playSound(_loadSound);
 	}
 }
 
@@ -720,6 +756,22 @@ void GameDrugWars::rectStart(Rect *rect) {
 	newGame();
 }
 
+void GameDrugWars::rectEasy(Rect *rect) {
+	_difficulty = 1;
+}
+
+void GameDrugWars::rectAverage(Rect *rect) {
+	_difficulty = 2;
+}
+
+void GameDrugWars::rectHard(Rect *rect) {
+	_difficulty = 3;
+}
+
+void GameDrugWars::rectExit(Rect *rect) {
+	shutdown();
+}
+
 void GameDrugWars::rectSelectTargetPractice(Rect *rect) {
 	rectSelectGeneric(0);
 	_gotTo[0] = 0;
@@ -978,6 +1030,13 @@ void GameDrugWars::sceneDefaultWepdwn(Scene *scene) {
 	_shots = 10;
 }
 
+// Script functions: ScnScr
+void GameDrugWars::sceneDefaultScore(Scene *scene) {
+	if (scene->_scnscrParam > 0) {
+		_score += scene->_scnscrParam;
+	}
+}
+
 // Debug methods
 void GameDrugWars::debugWarpTo(int val) {
 	// TODO implement
diff --git a/engines/alg/logic/game_drugwars.h b/engines/alg/logic/game_drugwars.h
index 32abe178780..74c821644b5 100644
--- a/engines/alg/logic/game_drugwars.h
+++ b/engines/alg/logic/game_drugwars.h
@@ -81,6 +81,13 @@ private:
 	Graphics::Surface *_difficultyIcon;
 	Graphics::Surface *_bulletholeIcon;
 
+	// sounds
+	Audio::SeekableAudioStream *_saveSound = nullptr;
+	Audio::SeekableAudioStream *_loadSound = nullptr;
+	Audio::SeekableAudioStream *_skullSound = nullptr;
+	Audio::SeekableAudioStream *_shotSound = nullptr;
+	Audio::SeekableAudioStream *_emptySound = nullptr;
+	
 	// constants
 	const int16 _randomScenes0[7] = {0x29, 0x2B, 0x2D, 0x2F, 0x31, 0x33, 0};
 	const int16 _randomScenes1[6] = {0x37, 0x39, 0x3B, 0x3D, 0x3F, 0};
@@ -111,6 +118,18 @@ private:
 	const uint16 _scenarioStartScenes[14] = {0x27, 0x36, 0x4A, 0x57, 0x9D, 0x8B, 0x74, 0xD8, 0xBF, 0xB8, 0x0160, 0x010A, 0x0137, 0x017F};
 
 	// gamestate
+	uint8 _difficulty = 1;
+	uint8 _oldDifficulty = 1;
+	bool _holster = false;
+	int8 _lives = 0;
+	int8 _oldLives = 0;
+	int32 _score = 0;
+	int32 _oldScore = -1;
+	uint16 _shots = 0;
+	uint8 _oldShots = 0;
+	uint8 _whichGun = 0;
+	uint8 _oldWhichGun = 0xFF;
+
 	uint8 _continues = 0;
 	uint16 _gotTo[14] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 	int8 _gotToIndex = 0;
@@ -145,17 +164,23 @@ private:
 	void displayShotFiredImage(Common::Point *point);
 	void enableVideoFadeIn();
 	uint16 sceneToNumber(Scene *scene);
+	uint16 randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude);
 	uint16 pickRandomScene(uint8 index, uint8 max);
 	uint16 pickDeathScene();
 	void sceneNxtscnGeneric(uint8 index);
 	void rectSelectGeneric(uint8 index);
 
 	// Script functions: RectHit
+	void rectNewScene(Rect *rect);
 	void rectShotMenu(Rect *rect);
 	void rectSave(Rect *rect);
 	void rectLoad(Rect *rect);
 	void rectContinue(Rect *rect);
 	void rectStart(Rect *rect);
+	void rectEasy(Rect *rect);
+	void rectAverage(Rect *rect);
+	void rectHard(Rect *rect);
+	void rectExit(Rect *rect);
 	void rectSelectTargetPractice(Rect *rect);
 	void rectSelectBar(Rect *rect);
 	void rectSelectCarChase(Rect *rect);
@@ -188,6 +213,9 @@ private:
 
 	// Script functions: Scene WepDwn
 	void sceneDefaultWepdwn(Scene *scene);
+
+	// Script functions: ScnScr
+	void sceneDefaultScore(Scene *scene);
 };
 
 class DebuggerDrugWars : public GUI::Debugger {
diff --git a/engines/alg/logic/game_johnnyrock.cpp b/engines/alg/logic/game_johnnyrock.cpp
index 7f909e749fa..ae50f81853b 100644
--- a/engines/alg/logic/game_johnnyrock.cpp
+++ b/engines/alg/logic/game_johnnyrock.cpp
@@ -23,6 +23,7 @@
 #include "common/rect.h"
 #include "common/savefile.h"
 #include "common/system.h"
+#include "common/timer.h"
 
 #include "graphics/cursorman.h"
 
@@ -48,6 +49,11 @@ GameJohnnyRock::~GameJohnnyRock() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	delete _saveSound;
+	delete _loadSound;
+	delete _moneySound;
+	delete _shotSound;
+	delete _emptySound;
 }
 
 void GameJohnnyRock::init() {
@@ -70,24 +76,24 @@ void GameJohnnyRock::init() {
 	registerScriptFunctions();
 	verifyScriptFunctions();
 
-	_menuzone = new Zone("MainMenu", "GLOBALHIT");
-	_menuzone->addRect(0x0C, 0xBB, 0x3C, 0xC7, nullptr, 0, "SHOTMENU", "0");
+	_menuZone = new Zone("MainMenu", "GLOBALHIT");
+	_menuZone->addRect(0x0C, 0xBB, 0x3C, 0xC7, nullptr, 0, "SHOTMENU", "0");
 
-	_submenzone = new Zone("SubMenu", "GLOBALHIT");
-	_submenzone->addRect(0x10, 0x0F, 0x78, 0x34, nullptr, 0, "STARTMENU", "0");
-	_submenzone->addRect(0x10, 0x8E, 0x8A, 0xAF, nullptr, 0, "CONTMENU", "0");
-	_submenzone->addRect(0x10, 0x3A, 0x6A, 0x5C, nullptr, 0, "RECTSAVE", "0");
-	_submenzone->addRect(0x10, 0x64, 0x84, 0x99, nullptr, 0, "RECTLOAD", "0");
-	_submenzone->addRect(0xD2, 0x8D, 0x12F, 0xB0, nullptr, 0, "EXITMENU", "0");
-	_submenzone->addRect(0xD0, 0x35, 0x123, 0x51, nullptr, 0, "RECTEASY", "0");
-	_submenzone->addRect(0xD2, 0x50, 0x125, 0x6B, nullptr, 0, "RECTAVG", "0");
-	_submenzone->addRect(0xD2, 0x6D, 0x122, 0x86, nullptr, 0, "RECTHARD", "0");
+	_subMenuZone = new Zone("SubMenu", "GLOBALHIT");
+	_subMenuZone->addRect(0x10, 0x0F, 0x78, 0x34, nullptr, 0, "STARTMENU", "0");
+	_subMenuZone->addRect(0x10, 0x8E, 0x8A, 0xAF, nullptr, 0, "CONTMENU", "0");
+	_subMenuZone->addRect(0x10, 0x3A, 0x6A, 0x5C, nullptr, 0, "RECTSAVE", "0");
+	_subMenuZone->addRect(0x10, 0x64, 0x84, 0x99, nullptr, 0, "RECTLOAD", "0");
+	_subMenuZone->addRect(0xD2, 0x8D, 0x12F, 0xB0, nullptr, 0, "EXITMENU", "0");
+	_subMenuZone->addRect(0xD0, 0x35, 0x123, 0x51, nullptr, 0, "RECTEASY", "0");
+	_subMenuZone->addRect(0xD2, 0x50, 0x125, 0x6B, nullptr, 0, "RECTAVG", "0");
+	_subMenuZone->addRect(0xD2, 0x6D, 0x122, 0x86, nullptr, 0, "RECTHARD", "0");
 
 	_shotSound = loadSoundFile("blow.8b");
 	_emptySound = loadSoundFile("empty.8b");
 	_saveSound = loadSoundFile("saved.8b");
 	_loadSound = loadSoundFile("loaded.8b");
-	_skullSound = loadSoundFile("money.8b");
+	_moneySound = loadSoundFile("money.8b");
 
 	_gun = AlgGraphics::loadScreenCoordAniImage("gun.ani", _palette);
 	_numbers = AlgGraphics::loadAniImage("numbers.ani", _palette);
@@ -334,7 +340,7 @@ Common::Error GameJohnnyRock::run() {
 			Common::Point firedCoords;
 			if (fired(&firedCoords)) {
 				if (!_holster) {
-					Rect *hitGlobalRect = checkZone(_menuzone, &firedCoords);
+					Rect *hitGlobalRect = checkZone(_menuZone, &firedCoords);
 					if (hitGlobalRect != nullptr) {
 						callScriptFunctionRectHit(hitGlobalRect->_rectHit, hitGlobalRect);
 					} else if (_shots > 0) {
@@ -513,7 +519,7 @@ void GameJohnnyRock::doMenu() {
 	while (_inMenu && !_vm->shouldQuit()) {
 		Common::Point firedCoords;
 		if (fired(&firedCoords)) {
-			Rect *hitMenuRect = checkZone(_submenzone, &firedCoords);
+			Rect *hitMenuRect = checkZone(_subMenuZone, &firedCoords);
 			if (hitMenuRect != nullptr) {
 				callScriptFunctionRectHit(hitMenuRect->_rectHit, hitMenuRect);
 			}
@@ -791,7 +797,7 @@ bool GameJohnnyRock::loadState() {
 }
 
 void GameJohnnyRock::doMoneySound() {
-	playSound(_skullSound);
+	playSound(_moneySound);
 }
 
 // Misc game functions
@@ -843,7 +849,7 @@ void GameJohnnyRock::defaultBullethole(Common::Point *point) {
 		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
 		updateCursor();
 		_shotFired = true;
-		doShot();
+		playSound(_shotSound);
 	}
 }
 
@@ -896,24 +902,72 @@ void GameJohnnyRock::showCombination() {
 	_curScene = numToScene(offset + 0xDB);
 }
 
+// Timer
+static void cursorTimerCallback(void *refCon) {
+	GameJohnnyRock *game = static_cast<GameJohnnyRock *>(refCon);
+	game->runCursorTimer();
+}
+
+void GameJohnnyRock::setupCursorTimer() {
+	g_system->getTimerManager()->installTimerProc(&cursorTimerCallback, 1000000 / 50, (void *)this, "cursortimer");
+}
+
+void GameJohnnyRock::removeCursorTimer() {
+	g_system->getTimerManager()->removeTimerProc(&cursorTimerCallback);
+}
+
+void GameJohnnyRock::runCursorTimer() {
+	_thisGameTimer += 2;
+	if (_whichGun == 9) {
+		if (_emptyCount > 0) {
+			_emptyCount--;
+		} else {
+			_whichGun = 0;
+		}
+	} else {
+		if (_shotFired) {
+			_whichGun++;
+			if (_whichGun > 5) {
+				_whichGun = 0;
+				_shotFired = false;
+			}
+		} else {
+			if (_inHolster > 0) {
+				_inHolster--;
+				if (_inHolster == 0 && _whichGun == 7) {
+					_whichGun = 6;
+				}
+			}
+		}
+	}
+}
+
 // Script functions: Zone
 void GameJohnnyRock::zoneBullethole(Common::Point *point) {
 	defaultBullethole(point);
 }
 
+// Script functions: RectHit
+void GameJohnnyRock::rectNewScene(Rect *rect) {
+	_score += rect->_score;
+	if (!rect->_scene.empty()) {
+		_curScene = rect->_scene;
+	}
+}
+
 void GameJohnnyRock::rectShotMenu(Rect *rect) {
 	doMenu();
 }
 
 void GameJohnnyRock::rectSave(Rect *rect) {
 	if (saveState()) {
-		doSaveSound();
+		playSound(_saveSound);
 	}
 }
 
 void GameJohnnyRock::rectLoad(Rect *rect) {
 	if (loadState()) {
-		doLoadSound();
+		playSound(_loadSound);
 	}
 }
 
@@ -951,6 +1005,22 @@ void GameJohnnyRock::rectStart(Rect *rect) {
 	updateStat();
 }
 
+void GameJohnnyRock::rectEasy(Rect *rect) {
+	_difficulty = 1;
+}
+
+void GameJohnnyRock::rectAverage(Rect *rect) {
+	_difficulty = 2;
+}
+
+void GameJohnnyRock::rectHard(Rect *rect) {
+	_difficulty = 3;
+}
+
+void GameJohnnyRock::rectExit(Rect *rect) {
+	shutdown();
+}
+
 void GameJohnnyRock::rectKillInnocent(Rect *rect) {
 	_inOffice = sceneToNum(_curScene);
 	if (_inOffice >= 0x13) {
@@ -1725,6 +1795,13 @@ void GameJohnnyRock::sceneDefaultWepdwn(Scene *scene) {
 	updateMouse();
 }
 
+// Script functions: ScnScr
+void GameJohnnyRock::sceneDefaultScore(Scene *scene) {
+	if (scene->_scnscrParam > 0) {
+		_score += scene->_scnscrParam;
+	}
+}
+
 // Debug methods
 void GameJohnnyRock::debugWarpTo(int val) {
 	// TODO implement
diff --git a/engines/alg/logic/game_johnnyrock.h b/engines/alg/logic/game_johnnyrock.h
index 83423d462a8..d426e00d941 100644
--- a/engines/alg/logic/game_johnnyrock.h
+++ b/engines/alg/logic/game_johnnyrock.h
@@ -56,6 +56,7 @@ public:
 	~GameJohnnyRock() override;
 	Common::Error run() override;
 	void debugWarpTo(int val);
+	void runCursorTimer();
 
 private:
 	void init() override;
@@ -83,6 +84,13 @@ private:
 	Graphics::Surface *_levelIcon;
 	Graphics::Surface *_bulletholeIcon;
 
+	// sounds
+	Audio::SeekableAudioStream *_saveSound = nullptr;
+	Audio::SeekableAudioStream *_loadSound = nullptr;
+	Audio::SeekableAudioStream *_moneySound = nullptr;
+	Audio::SeekableAudioStream *_shotSound = nullptr;
+	Audio::SeekableAudioStream *_emptySound = nullptr;
+	
 	// constants
 	const int16 _randomRooftop[6] = {2, -4, 0x104, 0x1E, 0x100, 0x102};
 	const int16 _randomTheater[9] = {5, -5, 0x111, 0x1E, 0x107, 0x109, 0x10B, 0x10D, 0x10F};
@@ -104,6 +112,21 @@ private:
 	const uint16 _diffPos[4][2] = {{0, 0}, {0xCD, 0x35}, {0xD2, 0x53}, {0xD2, 0x6E}};
 
 	// gamestate
+	uint8 _difficulty = 1;
+	uint8 _emptyCount = 0;
+	bool _holster = false;
+	uint8 _oldDifficulty = 1;
+	uint8 _inHolster = 0;
+	int8 _lives = 0;
+	int8 _oldLives = 0;
+	int32 _score = 0;
+	int32 _oldScore = -1;
+	bool _shotFired = false;
+	uint16 _shots = 0;
+	uint8 _oldShots = 0;
+	uint8 _whichGun = 0;
+	uint8 _oldWhichGun = 0xFF;
+
 	uint16 _totalDies = 0;
 	int16 _gameMoney = 0;
 	int16 _oldGameMoney = 0;
@@ -153,6 +176,11 @@ private:
 	uint8 _mgunCnt = 0;
 	uint32 _machGunTimer = 0;
 
+	Common::String _retScene;
+	Common::String _subScene;
+
+	uint32 _thisGameTimer = 0;
+
 	// base functions
 	bool fired(Common::Point *point);
 	void newGame();
@@ -182,15 +210,24 @@ private:
 	void shotCombination(uint8 combination, bool combinationB);
 	void shotLuckyNumber(uint8 number);
 
+	// Timer
+	void setupCursorTimer();
+	void removeCursorTimer();
+	
 	// Script functions: Zone
 	void zoneBullethole(Common::Point *point);
 
 	// Script functions: RectHit
+	void rectNewScene(Rect *rect);
 	void rectShotMenu(Rect *rect);
 	void rectSave(Rect *rect);
 	void rectLoad(Rect *rect);
 	void rectContinue(Rect *rect);
 	void rectStart(Rect *rect);
+	void rectEasy(Rect *rect);
+	void rectAverage(Rect *rect);
+	void rectHard(Rect *rect);
+	void rectExit(Rect *rect);
 	void rectKillInnocent(Rect *rect);
 	void rectSelectCasino(Rect *rect);
 	void rectSelectPoolhall(Rect *rect);
@@ -268,6 +305,9 @@ private:
 
 	// Script functions: Scene WepDwn
 	void sceneDefaultWepdwn(Scene *scene);
+
+	// Script functions: ScnScr
+	void sceneDefaultScore(Scene *scene);
 };
 
 class DebuggerJohnnyRock : public GUI::Debugger {
diff --git a/engines/alg/logic/game_maddog.cpp b/engines/alg/logic/game_maddog.cpp
index 8f1f7be009d..db17bf58b4c 100644
--- a/engines/alg/logic/game_maddog.cpp
+++ b/engines/alg/logic/game_maddog.cpp
@@ -23,6 +23,7 @@
 #include "common/rect.h"
 #include "common/savefile.h"
 #include "common/system.h"
+#include "common/timer.h"
 
 #include "graphics/cursorman.h"
 
@@ -68,6 +69,14 @@ GameMaddog::~GameMaddog() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	delete _saveSound;
+	delete _loadSound;
+	delete _easySound;
+	delete _avgSound;
+	delete _hardSound;
+	delete _skullSound;
+	delete _shotSound;
+	delete _emptySound;
 }
 
 void GameMaddog::init() {
@@ -85,21 +94,21 @@ void GameMaddog::init() {
 	registerScriptFunctions();
 	verifyScriptFunctions();
 
-	_menuzone = new Zone("MainMenu", "GLOBALHIT");
-	_menuzone->addRect(0x0C, 0xAC, 0x3D, 0xBF, nullptr, 0, "SHOTMENU", "0");
-	_menuzone->addRect(0x00, 0xA6, 0x013F, 0xC7, nullptr, 0, "DEFAULT", "0"); // _mm_bott
-	_menuzone->addRect(0x00, 0x00, 0x3B, 0xC7, nullptr, 0, "DEFAULT", "0");   // _mm_left
-
-	_submenzone = new Zone("SubMenu", "GLOBALHIT");
-	_submenzone->addRect(0x8A, 0x3B, 0xC2, 0x48, nullptr, 0, "STARTBOT", "0");
-	_submenzone->addRect(0x8A, 0x4E, 0xC2, 0x59, nullptr, 0, "STARTMENU", "0");
-	_submenzone->addRect(0x8A, 0x60, 0xC2, 0x6B, nullptr, 0, "CONTMENU", "0");
-	_submenzone->addRect(0xE3, 0x3B, 0x011B, 0x48, nullptr, 0, "RECTSAVE", "0");
-	_submenzone->addRect(0xE3, 0x4E, 0x011B, 0x59, nullptr, 0, "RECTLOAD", "0");
-	_submenzone->addRect(0xE3, 0x60, 0x011B, 0x6B, nullptr, 0, "EXITMENU", "0");
-	_submenzone->addRect(0x42, 0x34, 0x5C, 0x4E, nullptr, 0, "RECTEASY", "0");
-	_submenzone->addRect(0x42, 0x53, 0x5C, 0x70, nullptr, 0, "RECTAVG", "0");
-	_submenzone->addRect(0x42, 0x72, 0x62, 0x8A, nullptr, 0, "RECTHARD", "0");
+	_menuZone = new Zone("MainMenu", "GLOBALHIT");
+	_menuZone->addRect(0x0C, 0xAC, 0x3D, 0xBF, nullptr, 0, "SHOTMENU", "0");
+	_menuZone->addRect(0x00, 0xA6, 0x013F, 0xC7, nullptr, 0, "DEFAULT", "0"); // _mm_bott
+	_menuZone->addRect(0x00, 0x00, 0x3B, 0xC7, nullptr, 0, "DEFAULT", "0");   // _mm_left
+
+	_subMenuZone = new Zone("SubMenu", "GLOBALHIT");
+	_subMenuZone->addRect(0x8A, 0x3B, 0xC2, 0x48, nullptr, 0, "STARTBOT", "0");
+	_subMenuZone->addRect(0x8A, 0x4E, 0xC2, 0x59, nullptr, 0, "STARTMENU", "0");
+	_subMenuZone->addRect(0x8A, 0x60, 0xC2, 0x6B, nullptr, 0, "CONTMENU", "0");
+	_subMenuZone->addRect(0xE3, 0x3B, 0x011B, 0x48, nullptr, 0, "RECTSAVE", "0");
+	_subMenuZone->addRect(0xE3, 0x4E, 0x011B, 0x59, nullptr, 0, "RECTLOAD", "0");
+	_subMenuZone->addRect(0xE3, 0x60, 0x011B, 0x6B, nullptr, 0, "EXITMENU", "0");
+	_subMenuZone->addRect(0x42, 0x34, 0x5C, 0x4E, nullptr, 0, "RECTEASY", "0");
+	_subMenuZone->addRect(0x42, 0x53, 0x5C, 0x70, nullptr, 0, "RECTAVG", "0");
+	_subMenuZone->addRect(0x42, 0x72, 0x62, 0x8A, nullptr, 0, "RECTHARD", "0");
 
 	_shotSound = loadSoundFile("blow.8b");
 	_emptySound = loadSoundFile("empty.8b");
@@ -352,7 +361,7 @@ Common::Error GameMaddog::run() {
 			Common::Point firedCoords;
 			if (fired(&firedCoords)) {
 				if (!_holster) {
-					Rect *hitGlobalRect = checkZone(_menuzone, &firedCoords);
+					Rect *hitGlobalRect = checkZone(_menuZone, &firedCoords);
 					if (hitGlobalRect != nullptr) {
 						callScriptFunctionRectHit(hitGlobalRect->_rectHit, hitGlobalRect);
 					} else if (_shots > 0) {
@@ -471,7 +480,7 @@ void GameMaddog::doMenu() {
 	while (_inMenu && !_vm->shouldQuit()) {
 		Common::Point firedCoords;
 		if (fired(&firedCoords)) {
-			Rect *hitMenuRect = checkZone(_submenzone, &firedCoords);
+			Rect *hitMenuRect = checkZone(_subMenuZone, &firedCoords);
 			if (hitMenuRect != nullptr) {
 				callScriptFunctionRectHit(hitMenuRect->_rectHit, hitMenuRect);
 			}
@@ -686,7 +695,7 @@ void GameMaddog::defaultBullethole(Common::Point *point) {
 		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
 		updateCursor();
 		_shotFired = true;
-		doShot();
+		playSound(_shotSound);
 	}
 }
 
@@ -845,6 +854,46 @@ Common::String GameMaddog::mapLeft() {
 	}
 }
 
+// Timer
+static void cursorTimerCallback(void *refCon) {
+	GameMaddog *game = static_cast<GameMaddog *>(refCon);
+	game->runCursorTimer();
+}
+
+void GameMaddog::setupCursorTimer() {
+	g_system->getTimerManager()->installTimerProc(&cursorTimerCallback, 1000000 / 50, (void *)this, "cursortimer");
+}
+
+void GameMaddog::removeCursorTimer() {
+	g_system->getTimerManager()->removeTimerProc(&cursorTimerCallback);
+}
+
+void GameMaddog::runCursorTimer() {
+	_thisGameTimer += 2;
+	if (_whichGun == 9) {
+		if (_emptyCount > 0) {
+			_emptyCount--;
+		} else {
+			_whichGun = 0;
+		}
+	} else {
+		if (_shotFired) {
+			_whichGun++;
+			if (_whichGun > 5) {
+				_whichGun = 0;
+				_shotFired = false;
+			}
+		} else {
+			if (_inHolster > 0) {
+				_inHolster--;
+				if (_inHolster == 0 && _whichGun == 7) {
+					_whichGun = 6;
+				}
+			}
+		}
+	}
+}
+
 // Script functions: Zone
 void GameMaddog::zoneBullethole(Common::Point *point) {
 	defaultBullethole(point);
@@ -859,14 +908,21 @@ void GameMaddog::zoneSkullhole(Common::Point *point) {
 		_shotFired = true;
 
 		if (_hadSkull) {
-			doShot();
+			playSound(_shotSound);
 		} else {
-			doSkullSound();
+			playSound(_skullSound);
 		}
 	}
 }
 
 // Script functions: RectHit
+void GameMaddog::rectNewScene(Rect *rect) {
+	_score += rect->_score;
+	if (!rect->_scene.empty()) {
+		_curScene = rect->_scene;
+	}
+}
+
 void GameMaddog::rectHideFront(Rect *rect) {
 	if (_hideOutFront) {
 		_curScene = "scene214";
@@ -1047,13 +1103,13 @@ void GameMaddog::rectContinue(Rect *rect) {
 
 void GameMaddog::rectSave(Rect *rect) {
 	if (saveState()) {
-		doSaveSound();
+		playSound(_saveSound);
 	}
 }
 
 void GameMaddog::rectLoad(Rect *rect) {
 	if (loadState()) {
-		doLoadSound();
+		playSound(_loadSound);
 	}
 }
 
@@ -1079,6 +1135,25 @@ void GameMaddog::rectStartBottles(Rect *rect) {
 	updateStat();
 }
 
+void GameMaddog::rectEasy(Rect *rect) {
+	playSound(_easySound);
+	_difficulty = 1;
+}
+
+void GameMaddog::rectAverage(Rect *rect) {
+	playSound(_avgSound);
+	_difficulty = 2;
+}
+
+void GameMaddog::rectHard(Rect *rect) {
+	playSound(_hardSound);
+	_difficulty = 3;
+}
+
+void GameMaddog::rectExit(Rect *rect) {
+	shutdown();
+}
+
 // Script functions: Scene PreOps
 void GameMaddog::scenePsoShootout(Scene *scene) {
 	sscanf(scene->_preopParam.c_str(), "#%ldto%ld", &_minF, &_maxF);
@@ -1482,6 +1557,13 @@ void GameMaddog::sceneDefaultWepdwn(Scene *scene) {
 	}
 }
 
+// Script functions: ScnScr
+void GameMaddog::sceneDefaultScore(Scene *scene) {
+	if (scene->_scnscrParam > 0) {
+		_score += scene->_scnscrParam;
+	}
+}
+
 // Debug methods
 void GameMaddog::debugWarpTo(int val) {
 	switch (val) {
diff --git a/engines/alg/logic/game_maddog.h b/engines/alg/logic/game_maddog.h
index f6290becef8..85a5e5fbb84 100644
--- a/engines/alg/logic/game_maddog.h
+++ b/engines/alg/logic/game_maddog.h
@@ -56,6 +56,7 @@ public:
 	~GameMaddog() override;
 	Common::Error run() override;
 	void debugWarpTo(int val);
+	void runCursorTimer();
 
 private:
 	void init() override;
@@ -88,6 +89,16 @@ private:
 	Graphics::Surface *_knifeIcon;
 	Graphics::Surface *_bulletholeIcon;
 
+	// sounds
+	Audio::SeekableAudioStream *_saveSound = nullptr;
+	Audio::SeekableAudioStream *_loadSound = nullptr;
+	Audio::SeekableAudioStream *_easySound = nullptr;
+	Audio::SeekableAudioStream *_avgSound = nullptr;
+	Audio::SeekableAudioStream *_hardSound = nullptr;
+	Audio::SeekableAudioStream *_skullSound = nullptr;
+	Audio::SeekableAudioStream *_shotSound = nullptr;
+	Audio::SeekableAudioStream *_emptySound = nullptr;
+
 	// constants
 	const uint16 _fight[3] = {208, 228, 243};
 	const uint16 _ambush[3] = {192, 193, 192};
@@ -99,6 +110,23 @@ private:
 	const uint16 _shotPos[12][2] = {{0x3, 0x5}, {0x0D, 0x5}, {0x17, 0x5}, {0x21, 0x5}, {0x3, 0x21}, {0x0D, 0x21}, {0x17, 0x21}, {0x21, 0x21}, {0x3, 0x3D}, {0x0D, 0x3D}, {0x17, 0x3D}, {0x21, 0x3D}};
 
 	// gamestate
+	uint8 _difficulty = 1;
+	uint8 _emptyCount = 0;
+	bool _holster = false;
+	uint8 _oldDifficulty = 1;
+	uint8 _inHolster = 0;
+	int8 _lives = 0;
+	int8 _oldLives = 0;
+	int32 _score = 0;
+	int32 _oldScore = -1;
+	bool _shotFired = false;
+	uint16 _shots = 0;
+	uint8 _oldShots = 0;
+	uint8 _whichGun = 0;
+	uint8 _oldWhichGun = 0xFF;
+	long int _minF;
+	long int _maxF;
+
 	uint8 _badMen = 0;
 	uint8 _badMenBits = 0;
 	bool _bartenderAlive = false;
@@ -122,6 +150,11 @@ private:
 	uint8 _sheriffCnt = 0; // unused
 	uint8 _shootOutCnt = 0;
 
+	Common::String _retScene;
+	Common::String _subScene;
+
+	uint32 _thisGameTimer = 0;
+
 	// base functions
 	void newGame();
 	void resetParams();
@@ -148,17 +181,26 @@ private:
 	Common::String mapRight();
 	Common::String mapLeft();
 
+	// Timer
+	void setupCursorTimer();
+	void removeCursorTimer();
+	
 	// Script functions: Zone
 	void zoneBullethole(Common::Point *point);
 	void zoneSkullhole(Common::Point *point);
 
 	// Script functions: RectHit
+	void rectNewScene(Rect *rect);
 	void rectShotMenu(Rect *rect);
 	void rectContinue(Rect *rect);
 	void rectSave(Rect *rect);
 	void rectLoad(Rect *rect);
 	void rectStart(Rect *rect);
 	void rectStartBottles(Rect *rect);
+	void rectEasy(Rect *rect);
+	void rectAverage(Rect *rect);
+	void rectHard(Rect *rect);
+	void rectExit(Rect *rect);
 	void rectHideFront(Rect *rect);
 	void rectHideRear(Rect *rect);
 	void rectMenuSelect(Rect *rect);
@@ -220,6 +262,9 @@ private:
 
 	// Script functions: Scene WepDwn
 	void sceneDefaultWepdwn(Scene *scene);
+
+	// Script functions: ScnScr
+	void sceneDefaultScore(Scene *scene);
 };
 
 class DebuggerMaddog : public GUI::Debugger {
diff --git a/engines/alg/logic/game_maddog2.cpp b/engines/alg/logic/game_maddog2.cpp
index cd0ad42fc76..85738b8ad48 100644
--- a/engines/alg/logic/game_maddog2.cpp
+++ b/engines/alg/logic/game_maddog2.cpp
@@ -23,6 +23,7 @@
 #include "common/rect.h"
 #include "common/savefile.h"
 #include "common/system.h"
+#include "common/timer.h"
 
 #include "graphics/cursorman.h"
 
@@ -68,6 +69,11 @@ GameMaddog2::~GameMaddog2() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	delete _saveSound;
+	delete _loadSound;
+	delete _skullSound;
+	delete _shotSound;
+	delete _emptySound;
 }
 
 void GameMaddog2::init() {
@@ -90,19 +96,19 @@ void GameMaddog2::init() {
 	registerScriptFunctions();
 	verifyScriptFunctions();
 
-	_menuzone = new Zone("MainMenu", "GLOBALHIT");
-	_menuzone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
-	_menuzone->addRect(0x08, 0xA9, 0x013C, 0xC7, nullptr, 0, "DEFAULT", "0"); // _mm_bott
+	_menuZone = new Zone("MainMenu", "GLOBALHIT");
+	_menuZone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
+	_menuZone->addRect(0x08, 0xA9, 0x013C, 0xC7, nullptr, 0, "DEFAULT", "0"); // _mm_bott
 
-	_submenzone = new Zone("SubMenu", "GLOBALHIT");
-	_submenzone->addRect(0x2F, 0x16, 0x64, 0x2B, nullptr, 0, "STARTMENU", "0");
-	_submenzone->addRect(0x2F, 0xA0, 0x8D, 0xC7, nullptr, 0, "CONTMENU", "0");
-	_submenzone->addRect(0x2F, 0x40, 0x64, 0x54, nullptr, 0, "RECTSAVE", "0");
-	_submenzone->addRect(0x2F, 0x6E, 0x7B, 0x86, nullptr, 0, "RECTLOAD", "0");
-	_submenzone->addRect(0xEC, 0x15, 0x0122, 0x2C, nullptr, 0, "EXITMENU", "0");
-	_submenzone->addRect(0xAD, 0x58, 0xF2, 0x70, nullptr, 0, "RECTEASY", "0");
-	_submenzone->addRect(0xBC, 0x78, 0xF2, 0x93, nullptr, 0, "RECTAVG", "0");
-	_submenzone->addRect(0xB8, 0x9D, 0xF2, 0xC7, nullptr, 0, "RECTHARD", "0");
+	_subMenuZone = new Zone("SubMenu", "GLOBALHIT");
+	_subMenuZone->addRect(0x2F, 0x16, 0x64, 0x2B, nullptr, 0, "STARTMENU", "0");
+	_subMenuZone->addRect(0x2F, 0xA0, 0x8D, 0xC7, nullptr, 0, "CONTMENU", "0");
+	_subMenuZone->addRect(0x2F, 0x40, 0x64, 0x54, nullptr, 0, "RECTSAVE", "0");
+	_subMenuZone->addRect(0x2F, 0x6E, 0x7B, 0x86, nullptr, 0, "RECTLOAD", "0");
+	_subMenuZone->addRect(0xEC, 0x15, 0x0122, 0x2C, nullptr, 0, "EXITMENU", "0");
+	_subMenuZone->addRect(0xAD, 0x58, 0xF2, 0x70, nullptr, 0, "RECTEASY", "0");
+	_subMenuZone->addRect(0xBC, 0x78, 0xF2, 0x93, nullptr, 0, "RECTAVG", "0");
+	_subMenuZone->addRect(0xB8, 0x9D, 0xF2, 0xC7, nullptr, 0, "RECTHARD", "0");
 
 	_shotSound = loadSoundFile("blow.8b");
 	_emptySound = loadSoundFile("empty.8b");
@@ -363,7 +369,7 @@ Common::Error GameMaddog2::run() {
 			Common::Point firedCoords;
 			if (fired(&firedCoords)) {
 				if (!_holster) {
-					Rect *hitGlobalRect = checkZone(_menuzone, &firedCoords);
+					Rect *hitGlobalRect = checkZone(_menuZone, &firedCoords);
 					if (hitGlobalRect != nullptr) {
 						callScriptFunctionRectHit(hitGlobalRect->_rectHit, hitGlobalRect);
 					} else if (_shots > 0) {
@@ -483,7 +489,7 @@ void GameMaddog2::doMenu() {
 	while (_inMenu && !_vm->shouldQuit()) {
 		Common::Point firedCoords;
 		if (fired(&firedCoords)) {
-			Rect *hitMenuRect = checkZone(_submenzone, &firedCoords);
+			Rect *hitMenuRect = checkZone(_subMenuZone, &firedCoords);
 			if (hitMenuRect != nullptr) {
 				callScriptFunctionRectHit(hitMenuRect->_rectHit, hitMenuRect);
 			}
@@ -755,7 +761,7 @@ void GameMaddog2::defaultBullethole(Common::Point *point) {
 		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
 		updateCursor();
 		_shotFired = true;
-		doShot();
+		playSound(_shotSound);
 	}
 }
 
@@ -975,6 +981,46 @@ void GameMaddog2::playerWon() {
 	}
 }
 
+// Timer
+static void cursorTimerCallback(void *refCon) {
+	GameMaddog2 *game = static_cast<GameMaddog2 *>(refCon);
+	game->runCursorTimer();
+}
+
+void GameMaddog2::setupCursorTimer() {
+	g_system->getTimerManager()->installTimerProc(&cursorTimerCallback, 1000000 / 50, (void *)this, "cursortimer");
+}
+
+void GameMaddog2::removeCursorTimer() {
+	g_system->getTimerManager()->removeTimerProc(&cursorTimerCallback);
+}
+
+void GameMaddog2::runCursorTimer() {
+	_thisGameTimer += 2;
+	if (_whichGun == 9) {
+		if (_emptyCount > 0) {
+			_emptyCount--;
+		} else {
+			_whichGun = 0;
+		}
+	} else {
+		if (_shotFired) {
+			_whichGun++;
+			if (_whichGun > 5) {
+				_whichGun = 0;
+				_shotFired = false;
+			}
+		} else {
+			if (_inHolster > 0) {
+				_inHolster--;
+				if (_inHolster == 0 && _whichGun == 7) {
+					_whichGun = 6;
+				}
+			}
+		}
+	}
+}
+
 // Script functions: Zone
 void GameMaddog2::zoneBullethole(Common::Point *point) {
 	defaultBullethole(point);
@@ -989,14 +1035,21 @@ void GameMaddog2::zoneSkullhole(Common::Point *point) {
 		_shotFired = true;
 
 		if (_hadSkull) {
-			doShot();
+			playSound(_shotSound);
 		} else {
-			doSkullSound();
+			playSound(_skullSound);
 		}
 	}
 }
 
 // Script functions: RectHit
+void GameMaddog2::rectNewScene(Rect *rect) {
+	_score += rect->_score;
+	if (!rect->_scene.empty()) {
+		_curScene = rect->_scene;
+	}
+}
+
 void GameMaddog2::rectSkull(Rect *rect) {
 	if (_hadSkull) {
 		return;
@@ -1154,13 +1207,13 @@ void GameMaddog2::rectShotmenu(Rect *rect) {
 
 void GameMaddog2::rectSave(Rect *rect) {
 	if (saveState()) {
-		doSaveSound();
+		playSound(_saveSound);
 	}
 }
 
 void GameMaddog2::rectLoad(Rect *rect) {
 	if (loadState()) {
-		doLoadSound();
+		playSound(_loadSound);
 	}
 }
 
@@ -1193,6 +1246,22 @@ void GameMaddog2::rectStart(Rect *rect) {
 	updateStat();
 }
 
+void GameMaddog2::rectEasy(Rect *rect) {
+	_difficulty = 1;
+}
+
+void GameMaddog2::rectAverage(Rect *rect) {
+	_difficulty = 2;
+}
+
+void GameMaddog2::rectHard(Rect *rect) {
+	_difficulty = 3;
+}
+
+void GameMaddog2::rectExit(Rect *rect) {
+	shutdown();
+}
+
 // Script functions: Scene PreOps
 void GameMaddog2::scenePsoShootout(Scene *scene) {
 	sscanf(scene->_preopParam.c_str(), "#%ldto%ld", &_minF, &_maxF);
@@ -1378,7 +1447,7 @@ void GameMaddog2::sceneNxtscnShootSkull(Scene *scene) {
 		return;
 	}
 	_hadSkull = true;
-	doSkullSound();
+	playSound(_skullSound);
 	_shots = 12;
 	_score += 1000;
 	updateStat();
@@ -1678,6 +1747,13 @@ void GameMaddog2::sceneDefaultWepdwn(Scene *scene) {
 	}
 }
 
+// Script functions: ScnScr
+void GameMaddog2::sceneDefaultScore(Scene *scene) {
+	if (scene->_scnscrParam > 0) {
+		_score += scene->_scnscrParam;
+	}
+}
+
 // Debug methods
 void GameMaddog2::debugWarpTo(int val) {
 	// TODO implement
diff --git a/engines/alg/logic/game_maddog2.h b/engines/alg/logic/game_maddog2.h
index 7497c4007c2..2e05e5ea65e 100644
--- a/engines/alg/logic/game_maddog2.h
+++ b/engines/alg/logic/game_maddog2.h
@@ -56,6 +56,7 @@ public:
 	~GameMaddog2() override;
 	Common::Error run() override;
 	void debugWarpTo(int val);
+	void runCursorTimer();
 
 private:
 	void init() override;
@@ -88,6 +89,13 @@ private:
 	Graphics::Surface *_knifeIcon;
 	Graphics::Surface *_bulletholeIcon;
 
+	// sounds
+	Audio::SeekableAudioStream *_saveSound = nullptr;
+	Audio::SeekableAudioStream *_loadSound = nullptr;
+	Audio::SeekableAudioStream *_skullSound = nullptr;
+	Audio::SeekableAudioStream *_shotSound = nullptr;
+	Audio::SeekableAudioStream *_emptySound = nullptr;
+
 	// constants
 	const int16 _sbClue[3] = {0x67, 0x68, 0x69};
 	const int16 _bbClue[3] = {0x47, 0x49, 0x48};
@@ -109,6 +117,23 @@ private:
 	const uint16 _shotPos[12][2] = {{0x96, 0xBD}, {0x9A, 0xBD}, {0x9E, 0xBD}, {0x0A2, 0xBD}, {0x0A6, 0xBD}, {0x0AA, 0xBD}, {0x0AE, 0xBD}, {0x0B2, 0xBD}, {0x0B6, 0xBD}, {0x0BA, 0xBD}, {0x0BE, 0xBD}, {0x0C2, 0xBD}};
 
 	// gamestate
+	uint8 _difficulty = 1;
+	uint8 _emptyCount = 0;
+	bool _holster = false;
+	uint8 _oldDifficulty = 1;
+	uint8 _inHolster = 0;
+	int8 _lives = 0;
+	int8 _oldLives = 0;
+	int32 _score = 0;
+	int32 _oldScore = -1;
+	bool _shotFired = false;
+	uint16 _shots = 0;
+	uint8 _oldShots = 0;
+	uint8 _whichGun = 0;
+	uint8 _oldWhichGun = 0xFF;
+	long int _minF;
+	long int _maxF;
+
 	uint16 _dieBits = 0;
 	uint16 _gotTo = 0;
 	uint16 _sbGotTo = 0;
@@ -132,6 +157,12 @@ private:
 	uint16 _shootOutCnt = 0;
 	uint16 _totalDies = 0;
 
+	Common::String _lastScene;
+	Common::String _retScene;
+	Common::String _subScene;
+
+	uint32 _thisGameTimer = 0;
+
 	// base functions
 	void newGame();
 	void resetParams();
@@ -161,16 +192,25 @@ private:
 	void genericNext();
 	void playerWon();
 
+	// Timer
+	void setupCursorTimer();
+	void removeCursorTimer();
+	
 	// Script functions: Zone
 	void zoneBullethole(Common::Point *point);
 	void zoneSkullhole(Common::Point *point);
 
 	// Script functions: RectHit
+	void rectNewScene(Rect *rect);
 	void rectShotmenu(Rect *rect);
 	void rectSave(Rect *rect);
 	void rectLoad(Rect *rect);
 	void rectContinue(Rect *rect);
 	void rectStart(Rect *rect);
+	void rectEasy(Rect *rect);
+	void rectAverage(Rect *rect);
+	void rectHard(Rect *rect);
+	void rectExit(Rect *rect);
 	void rectSkull(Rect *rect);
 	void rectKillInnocentMan(Rect *rect);
 	void rectKillInnocentWoman(Rect *rect);
@@ -248,6 +288,9 @@ private:
 
 	// Script functions: Scene WepDwn
 	void sceneDefaultWepdwn(Scene *scene);
+
+	// Script functions: ScnScr
+	void sceneDefaultScore(Scene *scene);
 };
 
 class DebuggerMaddog2 : public GUI::Debugger {
diff --git a/engines/alg/logic/game_spacepirates.cpp b/engines/alg/logic/game_spacepirates.cpp
index 7cc8e4b71e3..b29425c39f1 100644
--- a/engines/alg/logic/game_spacepirates.cpp
+++ b/engines/alg/logic/game_spacepirates.cpp
@@ -64,6 +64,12 @@ GameSpacePirates::~GameSpacePirates() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	delete _saveSound;
+	delete _loadSound;
+	delete _difficultySound;
+	delete _skullSound;
+	delete _shotSound;
+	delete _emptySound;
 }
 
 void GameSpacePirates::init() {
@@ -90,27 +96,25 @@ void GameSpacePirates::init() {
 	registerScriptFunctions();
 	verifyScriptFunctions();
 
-	_menuzone = new Zone("MainMenu", "GLOBALHIT");
-	_menuzone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
+	_menuZone = new Zone("MainMenu", "GLOBALHIT");
+	_menuZone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
 
-	_submenzone = new Zone("SubMenu", "GLOBALHIT");
-	_submenzone->addRect(0x24, 0x16, 0x64, 0x26, nullptr, 0, "STARTMENU", "0");
-	_submenzone->addRect(0x24, 0x36, 0x64, 0x46, nullptr, 0, "RECTLOAD", "0");
-	_submenzone->addRect(0x24, 0x56, 0x64, 0x66, nullptr, 0, "RECTSAVE", "0");
-	_submenzone->addRect(0x24, 0x76, 0x64, 0x86, nullptr, 0, "CONTMENU", "0");
-	_submenzone->addRect(0x24, 0x96, 0x64, 0xA6, nullptr, 0, "EXITMENU", "0");
-	_submenzone->addRect(0xD5, 0x3B, 0x0115, 0x4B, nullptr, 0, "RECTEASY", "0");
-	_submenzone->addRect(0xD5, 0x63, 0x0115, 0x73, nullptr, 0, "RECTAVG", "0");
-	_submenzone->addRect(0xD5, 0x90, 0x0115, 0xA0, nullptr, 0, "RECTHARD", "0");
+	_subMenuZone = new Zone("SubMenu", "GLOBALHIT");
+	_subMenuZone->addRect(0x24, 0x16, 0x64, 0x26, nullptr, 0, "STARTMENU", "0");
+	_subMenuZone->addRect(0x24, 0x36, 0x64, 0x46, nullptr, 0, "RECTLOAD", "0");
+	_subMenuZone->addRect(0x24, 0x56, 0x64, 0x66, nullptr, 0, "RECTSAVE", "0");
+	_subMenuZone->addRect(0x24, 0x76, 0x64, 0x86, nullptr, 0, "CONTMENU", "0");
+	_subMenuZone->addRect(0x24, 0x96, 0x64, 0xA6, nullptr, 0, "EXITMENU", "0");
+	_subMenuZone->addRect(0xD5, 0x3B, 0x0115, 0x4B, nullptr, 0, "RECTEASY", "0");
+	_subMenuZone->addRect(0xD5, 0x63, 0x0115, 0x73, nullptr, 0, "RECTAVG", "0");
+	_subMenuZone->addRect(0xD5, 0x90, 0x0115, 0xA0, nullptr, 0, "RECTHARD", "0");
 
 	_shotSound = loadSoundFile("phaser.8b");
 	_emptySound = loadSoundFile("emptygun.8b");
 	_saveSound = loadSoundFile("saved.8b");
 	_loadSound = loadSoundFile("loaded.8b");
 	_skullSound = loadSoundFile("error.8b");
-	_easySound = loadSoundFile("difflev.8b");
-	_avgSound = loadSoundFile("difflev.8b");
-	_hardSound = loadSoundFile("difflev.8b");
+	_difficultySound = loadSoundFile("difflev.8b");
 
 	_gun = AlgGraphics::loadScreenCoordAniImage("gun.ani", _palette);
 	_difficultyIcon = (*_gun)[1];
@@ -356,7 +360,7 @@ Common::Error GameSpacePirates::run() {
 			Common::Point firedCoords;
 			if (fired(&firedCoords)) {
 				if (!_holster) {
-					Rect *hitGlobalRect = checkZone(_menuzone, &firedCoords);
+					Rect *hitGlobalRect = checkZone(_menuZone, &firedCoords);
 					if (hitGlobalRect != nullptr) {
 						callScriptFunctionRectHit(hitGlobalRect->_rectHit, hitGlobalRect);
 					} else if (_shots > 0) {
@@ -364,9 +368,9 @@ Common::Error GameSpacePirates::run() {
 							_shots--;
 						}
 						displayShotFiredImage(&firedCoords);
-						doShot();
+						playSound(_shotSound);
 						Rect *hitRect = nullptr;
-						Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords);
+						Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords, _difficulty);
 						if (hitSceneZone != nullptr) {
 							callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
 						} else {
@@ -473,7 +477,7 @@ void GameSpacePirates::doMenu() {
 	while (_inMenu && !_vm->shouldQuit()) {
 		Common::Point firedCoords;
 		if (fired(&firedCoords)) {
-			Rect *hitMenuRect = checkZone(_submenzone, &firedCoords);
+			Rect *hitMenuRect = checkZone(_subMenuZone, &firedCoords);
 			if (hitMenuRect != nullptr) {
 				callScriptFunctionRectHit(hitMenuRect->_rectHit, hitMenuRect);
 			}
@@ -868,19 +872,26 @@ uint16 GameSpacePirates::pickCrystalScene(uint16 scene1, uint16 scene2, uint16 s
 }
 
 // Script functions: RectHit
+void GameSpacePirates::rectNewScene(Rect *rect) {
+	_score += rect->_score;
+	if (!rect->_scene.empty()) {
+		_curScene = rect->_scene;
+	}
+}
+
 void GameSpacePirates::rectShotMenu(Rect *rect) {
 	doMenu();
 }
 
 void GameSpacePirates::rectSave(Rect *rect) {
 	if (saveState()) {
-		doSaveSound();
+		playSound(_saveSound);
 	}
 }
 
 void GameSpacePirates::rectLoad(Rect *rect) {
 	if (loadState()) {
-		doLoadSound();
+		playSound(_loadSound);
 	}
 }
 
@@ -901,23 +912,27 @@ void GameSpacePirates::rectStart(Rect *rect) {
 }
 
 void GameSpacePirates::rectEasy(Rect *rect) {
-	doDiffSound(1);
+	playSound(_difficultySound);
 	_difficulty = 0;
 }
 
 void GameSpacePirates::rectAverage(Rect *rect) {
-	doDiffSound(2);
+	playSound(_difficultySound);
 	_difficulty = 1;
 }
 
 void GameSpacePirates::rectHard(Rect *rect) {
-	doDiffSound(3);
+	playSound(_difficultySound);
 	_difficulty = 2;
 }
 
+void GameSpacePirates::rectExit(Rect *rect) {
+	shutdown();
+}
+
 void GameSpacePirates::rectDefault(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_score += rect->_score;
 	if (_score - _lastExtraLifeScore >= 1500 && _lives < 3) {
 		_lives++;
@@ -931,7 +946,7 @@ void GameSpacePirates::rectDefault(Rect *rect) {
 
 void GameSpacePirates::rectKillInnocentPerson(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	if (!_debug_godMode) {
 		_lives--;
 	}
@@ -951,7 +966,7 @@ void GameSpacePirates::rectKillInnocentPerson(Rect *rect) {
 
 void GameSpacePirates::rectContinueJunkRings(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_randomCount++;
 	uint16 picked = 0;
 	if (_randomCount >= 10) {
@@ -969,7 +984,7 @@ void GameSpacePirates::rectContinueJunkRings(Rect *rect) {
 
 void GameSpacePirates::rectShotGrinReaper(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_shotGrinReaperCount++;
 	uint16 picked = 0;
 	if (_clue - 223 <= _shotGrinReaperCount) {
@@ -989,14 +1004,14 @@ void GameSpacePirates::rectShotGrinReaper(Rect *rect) {
 
 void GameSpacePirates::rectShowMadDog(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_nextSceneFound = true;
 	_curScene = "scene354";
 }
 
 void GameSpacePirates::rectPottWorldShowCrystal(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	uint16 picked = 0;
 	if (_crystalState == 7) {
 		picked = 0xA6;
@@ -1009,21 +1024,21 @@ void GameSpacePirates::rectPottWorldShowCrystal(Rect *rect) {
 
 void GameSpacePirates::rectShotLeft(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_shotDirection = 1;
 	_nextSceneFound = true;
 }
 
 void GameSpacePirates::rectShotRight(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_shotDirection = 2;
 	_nextSceneFound = true;
 }
 
 void GameSpacePirates::rectShotGold(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_shotColor = 3;
 	_nextSceneFound = true;
 	_curScene = rect->_scene;
@@ -1031,7 +1046,7 @@ void GameSpacePirates::rectShotGold(Rect *rect) {
 
 void GameSpacePirates::rectShotSilver(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_shotColor = 4;
 	_nextSceneFound = true;
 	_curScene = rect->_scene;
@@ -1039,7 +1054,7 @@ void GameSpacePirates::rectShotSilver(Rect *rect) {
 
 void GameSpacePirates::rectSelectedDuneWorld(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	if (!_worldDone[0]) {
 		_selectedAWorld = true;
 		_currentWorld = 0;
@@ -1053,7 +1068,7 @@ void GameSpacePirates::rectSelectedDuneWorld(Rect *rect) {
 
 void GameSpacePirates::rectSelectedJunkWorld(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	if (!_worldDone[1]) {
 		_selectedAWorld = true;
 		_currentWorld = 1;
@@ -1067,7 +1082,7 @@ void GameSpacePirates::rectSelectedJunkWorld(Rect *rect) {
 
 void GameSpacePirates::rectSelectedDragonsTeethWorld(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	if (!_worldDone[2]) {
 		_selectedAWorld = true;
 		_currentWorld = 2;
@@ -1081,7 +1096,7 @@ void GameSpacePirates::rectSelectedDragonsTeethWorld(Rect *rect) {
 
 void GameSpacePirates::rectSelectedVolcanoWorld(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	if (!_worldDone[3]) {
 		_selectedAWorld = true;
 		_currentWorld = 3;
@@ -1098,7 +1113,7 @@ void GameSpacePirates::rectShotRedDeathGrip(Rect *rect) {
 	_nextSceneFound = true;
 	if (_clue == 0x36) {
 		displayShotFiredImage();
-		doShot();
+		playSound(_shotSound);
 		picked = 0x5A;
 	} else {
 		rectKillInnocentPerson(rect);
@@ -1112,7 +1127,7 @@ void GameSpacePirates::rectShotBlueDeathGrip(Rect *rect) {
 	_nextSceneFound = true;
 	if (_clue == 0x38) {
 		displayShotFiredImage();
-		doShot();
+		playSound(_shotSound);
 		picked = 0x5C;
 	} else {
 		rectKillInnocentPerson(rect);
@@ -1126,7 +1141,7 @@ void GameSpacePirates::rectShotGreenDeathGrip(Rect *rect) {
 	_nextSceneFound = true;
 	if (_clue == 0x37) {
 		displayShotFiredImage();
-		doShot();
+		playSound(_shotSound);
 		picked = 0x5B;
 	} else {
 		rectKillInnocentPerson(rect);
@@ -1137,14 +1152,14 @@ void GameSpacePirates::rectShotGreenDeathGrip(Rect *rect) {
 
 void GameSpacePirates::rectShotYellow(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_shotColor = 0x0F;
 	_nextSceneFound = true;
 }
 
 void GameSpacePirates::rectShotBlue(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_shotColor = 0x0E;
 	_nextSceneFound = true;
 }
@@ -1152,7 +1167,7 @@ void GameSpacePirates::rectShotBlue(Rect *rect) {
 void GameSpacePirates::rectShotRedCrystal(Rect *rect) {
 	uint16 picked = 0;
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	Scene *scene = _sceneInfo->findScene(_curScene);
 	if (_crystalsShot == 1) {
 		if (_pickedStartSplitter == 0x6A) {
@@ -1185,7 +1200,7 @@ void GameSpacePirates::rectShotRedCrystal(Rect *rect) {
 void GameSpacePirates::rectShotBlueCrystal(Rect *rect) {
 	uint16 picked = 0;
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	Scene *scene = _sceneInfo->findScene(_curScene);
 	if (_crystalsShot == 1) {
 		if (_pickedStartSplitter == 0x6C) {
@@ -1218,7 +1233,7 @@ void GameSpacePirates::rectShotBlueCrystal(Rect *rect) {
 void GameSpacePirates::rectShotGreenCrystal(Rect *rect) {
 	uint16 picked = 0;
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	Scene *scene = _sceneInfo->findScene(_curScene);
 	if (_crystalsShot == 1) {
 		if (_pickedStartSplitter == 0x6B) {
@@ -1250,7 +1265,7 @@ void GameSpacePirates::rectShotGreenCrystal(Rect *rect) {
 
 void GameSpacePirates::rectShotBlackDragon1(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_score += rect->_score;
 	_nextSceneFound = true;
 	_curScene = "scene203";
@@ -1258,7 +1273,7 @@ void GameSpacePirates::rectShotBlackDragon1(Rect *rect) {
 
 void GameSpacePirates::rectShotBlackDragon2(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_score += rect->_score;
 	_nextSceneFound = true;
 	_curScene = "scene204";
@@ -1266,7 +1281,7 @@ void GameSpacePirates::rectShotBlackDragon2(Rect *rect) {
 
 void GameSpacePirates::rectShotBlackDragon3(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_score += rect->_score;
 	_nextSceneFound = true;
 	_curScene = "scene335";
@@ -1274,7 +1289,7 @@ void GameSpacePirates::rectShotBlackDragon3(Rect *rect) {
 
 void GameSpacePirates::rectDoFlyingSkull(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_nextSceneFound = true;
 	Scene *scene = _sceneInfo->findScene(rect->_scene);
 	_sceneBeforeFlyingSkulls = sceneToNumber(scene);
@@ -1284,7 +1299,7 @@ void GameSpacePirates::rectDoFlyingSkull(Rect *rect) {
 
 void GameSpacePirates::rectSkipScene(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	_nextSceneFound = true;
 	Scene *scene = _sceneInfo->findScene(_curScene);
 	_curScene = scene->_next;
@@ -1292,7 +1307,7 @@ void GameSpacePirates::rectSkipScene(Rect *rect) {
 
 void GameSpacePirates::rectHitPirateShip(Rect *rect) {
 	displayShotFiredImage();
-	doShot();
+	playSound(_shotSound);
 	displayMultipleShotLines();
 	_score += rect->_score;
 	_nextSceneFound = true;
@@ -1931,6 +1946,13 @@ void GameSpacePirates::sceneDefaultWepdwn(Scene *scene) {
 	_shots = 10;
 }
 
+// Script functions: ScnScr
+void GameSpacePirates::sceneDefaultScore(Scene *scene) {
+	if (scene->_scnscrParam > 0) {
+		_score += scene->_scnscrParam;
+	}
+}
+
 // Debug methods
 void GameSpacePirates::debugWarpTo(int val) {
 	// TODO implement
diff --git a/engines/alg/logic/game_spacepirates.h b/engines/alg/logic/game_spacepirates.h
index 99fb188b433..dee1589a1e3 100644
--- a/engines/alg/logic/game_spacepirates.h
+++ b/engines/alg/logic/game_spacepirates.h
@@ -85,9 +85,29 @@ private:
 	Graphics::Surface *_difficultyIcon;
 	Graphics::Surface *_bulletholeIcon;
 
+	// sounds
+	Audio::SeekableAudioStream *_saveSound = nullptr;
+	Audio::SeekableAudioStream *_loadSound = nullptr;
+	Audio::SeekableAudioStream *_difficultySound = nullptr;
+	Audio::SeekableAudioStream *_skullSound = nullptr;
+	Audio::SeekableAudioStream *_shotSound = nullptr;
+	Audio::SeekableAudioStream *_emptySound = nullptr;
+
 	// constants
 
 	// gamestate
+	uint8 _difficulty = 1;
+	uint8 _oldDifficulty = 1;
+	bool _holster = false;
+	int8 _lives = 0;
+	int8 _oldLives = 0;
+	int32 _score = 0;
+	int32 _oldScore = -1;
+	uint16 _shots = 0;
+	uint8 _oldShots = 0;
+	uint8 _whichGun = 0;
+	uint8 _oldWhichGun = 0xFF;
+
 	bool _gameLoaded = false;
 	int8 _livesLoaded = 0;
 	uint16 _shotsLoaded = 0;
@@ -150,6 +170,7 @@ private:
 	uint16 pickCrystalScene(uint16 scene1, uint16 scene2, uint16 scene3);
 
 	// Script functions: RectHit
+	void rectNewScene(Rect *rect);
 	void rectShotMenu(Rect *rect);
 	void rectSave(Rect *rect);
 	void rectLoad(Rect *rect);
@@ -158,6 +179,7 @@ private:
 	void rectEasy(Rect *rect);
 	void rectAverage(Rect *rect);
 	void rectHard(Rect *rect);
+	void rectExit(Rect *rect);
 	void rectDefault(Rect *rect);
 	void rectKillInnocentPerson(Rect *rect);
 	void rectContinueJunkRings(Rect *rect);
@@ -249,8 +271,8 @@ private:
 	// Script functions: Scene WepDwn
 	void sceneDefaultWepdwn(Scene *scene);
 
-	// Script functions: Scene ScnScr
-	void sceneDefaultScnscr(Scene *scene);
+	// Script functions: ScnScr
+	void sceneDefaultScore(Scene *scene);
 };
 
 class DebuggerSpacePirates : public GUI::Debugger {


Commit: 7444a45dd9ad2dd02e3ca7ae694b2d4e64742ab9
    https://github.com/scummvm/scummvm/commit/7444a45dd9ad2dd02e3ca7ae694b2d4e64742ab9
Author: loki (loki at localhost)
Date: 2026-03-01T13:20:13+01:00

Commit Message:
ALG: Bugfixes for Graphics, script bugfix for SP

Changed paths:
    engines/alg/graphics.cpp
    engines/alg/logic/game_bountyhunter.cpp
    engines/alg/logic/game_crimepatrol.cpp
    engines/alg/logic/game_crimepatrol.h
    engines/alg/logic/game_drugwars.cpp
    engines/alg/logic/game_drugwars.h
    engines/alg/logic/game_johnnyrock.cpp
    engines/alg/logic/game_maddog.cpp
    engines/alg/logic/game_maddog2.cpp
    engines/alg/logic/game_spacepirates.cpp
    engines/alg/logic/game_spacepirates.h


diff --git a/engines/alg/graphics.cpp b/engines/alg/graphics.cpp
index 2d98b10ff33..c6e0d6675c4 100644
--- a/engines/alg/graphics.cpp
+++ b/engines/alg/graphics.cpp
@@ -167,7 +167,7 @@ void AlgGraphics::drawImage(Graphics::Surface *dst, Graphics::Surface *src, int3
 	if (dstY + src->h > dst->h) {
 		subRect.bottom -= dstY + src->h - dst->h;
 	}
-	dst->copyRectToSurfaceWithKey(src->getBasePtr(subRect.left, subRect.top), src->pitch, dstX, dstY, subRect.width(), subRect.height(), 0x00);
+	dst->copyRectToSurfaceWithKey(*src, dstX, dstY, subRect, 0x00);
 }
 
 void AlgGraphics::drawImageCentered(Graphics::Surface *dst, Graphics::Surface *src, int32 x, int32 y) {
diff --git a/engines/alg/logic/game_bountyhunter.cpp b/engines/alg/logic/game_bountyhunter.cpp
index e57f584523b..acf312d202f 100644
--- a/engines/alg/logic/game_bountyhunter.cpp
+++ b/engines/alg/logic/game_bountyhunter.cpp
@@ -679,9 +679,11 @@ void GameBountyHunter::setNextScene(uint16 sceneId) {
 
 void GameBountyHunter::displayShotFiredImage(Common::Point *point) {
 	if (point->x >= _videoPosX && point->x <= (_videoPosX + _videoDecoder->getWidth()) && point->y >= _videoPosY && point->y <= (_videoPosY + _videoDecoder->getHeight())) {
-		uint16 targetX = point->x - _videoPosX;
-		uint16 targetY = point->y - _videoPosY;
-		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		int32 targetX = point->x - _videoPosX;
+		int32 targetY = point->y - _videoPosY;
+		if (targetX > 0 && targetY > 0) {
+			AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		}
 	}
 }
 
diff --git a/engines/alg/logic/game_crimepatrol.cpp b/engines/alg/logic/game_crimepatrol.cpp
index 800d1b90f36..e03e5a974a3 100644
--- a/engines/alg/logic/game_crimepatrol.cpp
+++ b/engines/alg/logic/game_crimepatrol.cpp
@@ -639,9 +639,11 @@ bool GameCrimePatrol::loadState() {
 // misc game functions
 void GameCrimePatrol::displayShotFiredImage(Common::Point *point) {
 	if (point->x >= _videoPosX && point->x <= (_videoPosX + _videoDecoder->getWidth()) && point->y >= _videoPosY && point->y <= (_videoPosY + _videoDecoder->getHeight())) {
-		uint16 targetX = point->x - _videoPosX;
-		uint16 targetY = point->y - _videoPosY;
-		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		int32 targetX = point->x - _videoPosX;
+		int32 targetY = point->y - _videoPosY;
+		if (targetX > 0 && targetY > 0) {
+			AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		}
 	}
 }
 
@@ -649,8 +651,8 @@ void GameCrimePatrol::enableVideoFadeIn() {
 	// TODO implement
 }
 
-uint16 GameCrimePatrol::sceneToNumber(Scene *scene) {
-	return atoi(scene->_name.substr(5).c_str());
+uint16 GameCrimePatrol::sceneToNumber(Common::String sceneName) {
+	return atoi(sceneName.substr(5).c_str());
 }
 
 uint16 GameCrimePatrol::randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude) {
@@ -822,7 +824,7 @@ void GameCrimePatrol::rectExit(Rect *rect) {
 void GameCrimePatrol::rectTargetPractice(Rect *rect) {
 	uint16 nextScene = 0;
 	Scene *scene = _sceneInfo->findScene(_curScene);
-	if (_levelScenes[0][0] == sceneToNumber(scene)) {
+	if (_levelScenes[0][0] == sceneToNumber(_curScene)) {
 		_practiceMask = 0x1F;
 	}
 	if (_practiceMask == 0) {
@@ -912,13 +914,13 @@ void GameCrimePatrol::rectKillInnocentMan(Rect *rect) {
 
 // Script functions: Scene PreOps
 void GameCrimePatrol::scenePsoWarehouseGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	sceneIsoGotToGeneric(2, sceneId);
 	enableVideoFadeIn();
 }
 
 void GameCrimePatrol::scenePsoGangFightGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	sceneIsoGotToGeneric(1, sceneId);
 	enableVideoFadeIn();
 }
@@ -929,55 +931,55 @@ void GameCrimePatrol::scenePsoWestcoastSoundGotTo(Scene *scene) {
 }
 
 void GameCrimePatrol::scenePsoDrugDealGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	sceneIsoGotToGeneric(4, sceneId);
 	enableVideoFadeIn();
 }
 
 void GameCrimePatrol::scenePsoCarRingGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	sceneIsoGotToGeneric(5, sceneId);
 	enableVideoFadeIn();
 }
 
 void GameCrimePatrol::scenePsoBankGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	sceneIsoGotToGeneric(7, sceneId);
 	enableVideoFadeIn();
 }
 
 void GameCrimePatrol::scenePsoCrackHouseGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	sceneIsoGotToGeneric(9, sceneId);
 	enableVideoFadeIn();
 }
 
 void GameCrimePatrol::scenePsoMethLabGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	sceneIsoGotToGeneric(8, sceneId);
 	enableVideoFadeIn();
 }
 
 void GameCrimePatrol::scenePsoAirplaneGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	sceneIsoGotToGeneric(10, sceneId);
 	enableVideoFadeIn();
 }
 
 void GameCrimePatrol::scenePsoAirportGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	sceneIsoGotToGeneric(12, sceneId);
 	enableVideoFadeIn();
 }
 
 void GameCrimePatrol::scenePsoNukeTransportGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	sceneIsoGotToGeneric(11, sceneId);
 	enableVideoFadeIn();
 }
 
 void GameCrimePatrol::scenePsoPowerPlantGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	sceneIsoGotToGeneric(13, sceneId);
 	_finalStageScene = sceneId;
 	enableVideoFadeIn();
diff --git a/engines/alg/logic/game_crimepatrol.h b/engines/alg/logic/game_crimepatrol.h
index bdf1aaadae7..169d4b25866 100644
--- a/engines/alg/logic/game_crimepatrol.h
+++ b/engines/alg/logic/game_crimepatrol.h
@@ -184,7 +184,7 @@ private:
 	// misc game functions
 	void displayShotFiredImage(Common::Point *point);
 	void enableVideoFadeIn();
-	uint16 sceneToNumber(Scene *scene);
+	uint16 sceneToNumber(Common::String sceneName);
 	uint16 randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude);
 	uint16 pickRandomScene(uint8 index, uint8 max);
 	uint16 pickDeathScene();
diff --git a/engines/alg/logic/game_drugwars.cpp b/engines/alg/logic/game_drugwars.cpp
index eccc18345f6..203eb5c7022 100644
--- a/engines/alg/logic/game_drugwars.cpp
+++ b/engines/alg/logic/game_drugwars.cpp
@@ -591,9 +591,11 @@ bool GameDrugWars::loadState() {
 // misc game functions
 void GameDrugWars::displayShotFiredImage(Common::Point *point) {
 	if (point->x >= _videoPosX && point->x <= (_videoPosX + _videoDecoder->getWidth()) && point->y >= _videoPosY && point->y <= (_videoPosY + _videoDecoder->getHeight())) {
-		uint16 targetX = point->x - _videoPosX;
-		uint16 targetY = point->y - _videoPosY;
-		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		int32 targetX = point->x - _videoPosX;
+		int32 targetY = point->y - _videoPosY;
+		if (targetX > 0 && targetY > 0) {
+			AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		}
 	}
 }
 
@@ -601,8 +603,8 @@ void GameDrugWars::enableVideoFadeIn() {
 	// TODO implement
 }
 
-uint16 GameDrugWars::sceneToNumber(Scene *scene) {
-	return atoi(scene->_name.substr(5).c_str());
+uint16 GameDrugWars::sceneToNumber(Common::String sceneName) {
+	return atoi(sceneName.substr(5).c_str());
 }
 
 uint16 GameDrugWars::randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude) {
@@ -830,10 +832,10 @@ void GameDrugWars::rectSelectVillage(Rect *rect) {
 
 // Script functions: Scene PreOps
 void GameDrugWars::scenePsoGotTo(Scene *scene) {
-	uint16 sceneId = sceneToNumber(scene);
+	uint16 sceneId = sceneToNumber(scene->_name);
 	_gotTo[_gotToIndex] = sceneId;
 	if (_gotToIndex == 13) {
-		_finalStageScene = sceneToNumber(scene);
+		_finalStageScene = sceneToNumber(scene->_name);
 	}
 	enableVideoFadeIn();
 }
diff --git a/engines/alg/logic/game_drugwars.h b/engines/alg/logic/game_drugwars.h
index 74c821644b5..c7096f35c4d 100644
--- a/engines/alg/logic/game_drugwars.h
+++ b/engines/alg/logic/game_drugwars.h
@@ -163,7 +163,7 @@ private:
 	// misc game functions
 	void displayShotFiredImage(Common::Point *point);
 	void enableVideoFadeIn();
-	uint16 sceneToNumber(Scene *scene);
+	uint16 sceneToNumber(Common::String sceneName);
 	uint16 randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude);
 	uint16 pickRandomScene(uint8 index, uint8 max);
 	uint16 pickDeathScene();
diff --git a/engines/alg/logic/game_johnnyrock.cpp b/engines/alg/logic/game_johnnyrock.cpp
index ae50f81853b..c3855c5a88e 100644
--- a/engines/alg/logic/game_johnnyrock.cpp
+++ b/engines/alg/logic/game_johnnyrock.cpp
@@ -844,9 +844,11 @@ uint16 GameJohnnyRock::sceneToNum(Common::String sceneName) {
 
 void GameJohnnyRock::defaultBullethole(Common::Point *point) {
 	if (point->x >= 14 && point->x <= 306 && point->y >= 5 && point->y <= 169) {
-		uint16 targetX = point->x - _videoPosX;
-		uint16 targetY = point->y - _videoPosY;
-		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		int32 targetX = point->x - _videoPosX;
+		int32 targetY = point->y - _videoPosY;
+		if (targetX > 0 && targetY > 0) {
+			AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		}
 		updateCursor();
 		_shotFired = true;
 		playSound(_shotSound);
diff --git a/engines/alg/logic/game_maddog.cpp b/engines/alg/logic/game_maddog.cpp
index db17bf58b4c..218d4709e2e 100644
--- a/engines/alg/logic/game_maddog.cpp
+++ b/engines/alg/logic/game_maddog.cpp
@@ -690,9 +690,11 @@ bool GameMaddog::loadState() {
 // misc game functions
 void GameMaddog::defaultBullethole(Common::Point *point) {
 	if (point->x >= 59 && point->y <= 166) {
-		uint16 targetX = point->x - _videoPosX;
-		uint16 targetY = point->y - _videoPosY;
-		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		int32 targetX = point->x - _videoPosX;
+		int32 targetY = point->y - _videoPosY;
+		if (targetX > 0 && targetY > 0) {
+			AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		}
 		updateCursor();
 		_shotFired = true;
 		playSound(_shotSound);
@@ -901,9 +903,11 @@ void GameMaddog::zoneBullethole(Common::Point *point) {
 
 void GameMaddog::zoneSkullhole(Common::Point *point) {
 	if (point->x >= 59 && point->y <= 166) {
-		uint16 targetX = point->x - _videoPosX;
-		uint16 targetY = point->y - _videoPosY;
-		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		int32 targetX = point->x - _videoPosX;
+		int32 targetY = point->y - _videoPosY;
+		if (targetX > 0 && targetY > 0) {
+			AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		}
 		updateCursor();
 		_shotFired = true;
 
diff --git a/engines/alg/logic/game_maddog2.cpp b/engines/alg/logic/game_maddog2.cpp
index 85738b8ad48..e41cc04dcf2 100644
--- a/engines/alg/logic/game_maddog2.cpp
+++ b/engines/alg/logic/game_maddog2.cpp
@@ -756,9 +756,11 @@ uint16 GameMaddog2::sceneToNum(Common::String sceneName) {
 
 void GameMaddog2::defaultBullethole(Common::Point *point) {
 	if (point->x >= 14 && point->x <= 306 && point->y >= 5 && point->y <= 169) {
-		uint16 targetX = point->x - _videoPosX;
-		uint16 targetY = point->y - _videoPosY;
-		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		int32 targetX = point->x - _videoPosX;
+		int32 targetY = point->y - _videoPosY;
+		if (targetX > 0 && targetY > 0) {
+			AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		}
 		updateCursor();
 		_shotFired = true;
 		playSound(_shotSound);
@@ -1028,9 +1030,11 @@ void GameMaddog2::zoneBullethole(Common::Point *point) {
 
 void GameMaddog2::zoneSkullhole(Common::Point *point) {
 	if (point->x >= 14 && point->x <= 306 && point->y >= 5 && point->y <= 169) {
-		uint16 targetX = point->x - _videoPosX;
-		uint16 targetY = point->y - _videoPosY;
-		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		int32 targetX = point->x - _videoPosX;
+		int32 targetY = point->y - _videoPosY;
+		if (targetX > 0 && targetY > 0) {
+			AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		}
 		updateCursor();
 		_shotFired = true;
 
diff --git a/engines/alg/logic/game_spacepirates.cpp b/engines/alg/logic/game_spacepirates.cpp
index b29425c39f1..a66ea47ee18 100644
--- a/engines/alg/logic/game_spacepirates.cpp
+++ b/engines/alg/logic/game_spacepirates.cpp
@@ -95,6 +95,7 @@ void GameSpacePirates::init() {
 
 	registerScriptFunctions();
 	verifyScriptFunctions();
+	fixScriptBugs();
 
 	_menuZone = new Zone("MainMenu", "GLOBALHIT");
 	_menuZone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
@@ -273,6 +274,19 @@ void GameSpacePirates::verifyScriptFunctions() {
 	}
 }
 
+void GameSpacePirates::fixScriptBugs() {
+	auto scenes = _sceneInfo->getScenes();
+	for (auto scene : *scenes) {
+		for (auto zone : scene->_zones) {
+			for (auto rect : zone->_rects) {
+				if (rect->_scene == "scene363") {
+					rect->_scene = "scene11";
+				}
+			}
+		}
+	}
+}
+
 SPScriptFunctionRect GameSpacePirates::getScriptFunctionRectHit(Common::String name) {
 	auto it = _rectHitFuncs.find(name);
 	if (it != _rectHitFuncs.end()) {
@@ -640,8 +654,7 @@ bool GameSpacePirates::weaponDown() {
 }
 
 bool GameSpacePirates::saveState() {
-	Scene *scene = _sceneInfo->findScene(_curScene);
-	uint16 sceneNum = sceneToNumber(scene);
+	uint16 sceneNum = sceneToNumber(_curScene);
 	if ((sceneNum < 0xAC || sceneNum > 0xB9) && sceneNum != 0x6F) {
 		Common::OutSaveFile *outSaveFile;
 		Common::String saveFileName = _vm->getSaveStateName(0);
@@ -761,9 +774,11 @@ void GameSpacePirates::displayShotFiredImage() {
 
 void GameSpacePirates::displayShotFiredImage(Common::Point *point) {
 	if (point->x >= _videoPosX && point->x <= (_videoPosX + _videoDecoder->getWidth()) && point->y >= _videoPosY && point->y <= (_videoPosY + _videoDecoder->getHeight())) {
-		uint16 targetX = point->x - _videoPosX - 4;
-		uint16 targetY = point->y - _videoPosY - 4;
-		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		int32 targetX = point->x - _videoPosX - 4;
+		int32 targetY = point->y - _videoPosY - 4;
+		if (targetX > 0 && targetY > 0) {
+			AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+		}
 	}
 }
 
@@ -817,8 +832,8 @@ void GameSpacePirates::enableVideoFadeIn() {
 	// TODO implement
 }
 
-uint16 GameSpacePirates::sceneToNumber(Scene *scene) {
-	return atoi(scene->_name.substr(5).c_str());
+uint16 GameSpacePirates::sceneToNumber(Common::String sceneName) {
+	return atoi(sceneName.substr(5).c_str());
 }
 
 uint16 GameSpacePirates::randomUnusedScene(uint8 max) {
@@ -956,8 +971,7 @@ void GameSpacePirates::rectKillInnocentPerson(Rect *rect) {
 		_curScene = "scene185";
 		return;
 	}
-	Scene *scene = _sceneInfo->findScene(rect->_scene);
-	uint16 picked = sceneToNumber(scene);
+	uint16 picked = sceneToNumber(rect->_scene);
 	if (picked == 0) {
 		picked = randomNumberInRange(0xB7, 0xB9);
 	}
@@ -1291,8 +1305,7 @@ void GameSpacePirates::rectDoFlyingSkull(Rect *rect) {
 	displayShotFiredImage();
 	playSound(_shotSound);
 	_nextSceneFound = true;
-	Scene *scene = _sceneInfo->findScene(rect->_scene);
-	_sceneBeforeFlyingSkulls = sceneToNumber(scene);
+	_sceneBeforeFlyingSkulls = sceneToNumber(rect->_scene);
 	uint16 picked = randomNumberInRange(0x014A, 0x014D);
 	_curScene = Common::String::format("scene%d", picked);
 }
@@ -1320,16 +1333,16 @@ void GameSpacePirates::scenePsoFadeInVideo(Scene *scene) {
 
 void GameSpacePirates::scenePsoSetGotTo(Scene *scene) {
 	enableVideoFadeIn();
-	_gotTo = sceneToNumber(scene);
+	_gotTo = sceneToNumber(scene->_name);
 }
 
 void GameSpacePirates::scenePsoSetGotToNoFadeIn(Scene *scene) {
-	_gotTo = sceneToNumber(scene);
+	_gotTo = sceneToNumber(scene->_name);
 }
 
 void GameSpacePirates::scenePsoSetWorldGotTo(Scene *scene) {
 	enableVideoFadeIn();
-	uint16 sceneNum = sceneToNumber(scene);
+	uint16 sceneNum = sceneToNumber(scene->_name);
 	_worldGotTo[_currentWorld] = sceneNum;
 }
 
@@ -1339,8 +1352,8 @@ void GameSpacePirates::sceneIsoPickAWorld(Scene *scene) {
 	uint8 world = 3;
 	for (auto &rect : zone->_rects) {
 		if (_worldDone[world]) {
-			uint16 centerX = rect->left + (rect->width() / 2);
-			uint16 centerY = rect->top + (rect->height() / 2);
+			int32 centerX = rect->left + (rect->width() / 2);
+			int32 centerY = rect->top + (rect->height() / 2);
 			AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), (*_gun)[2], centerX - 16, centerY - 24);
 		}
 		world--;
@@ -1358,7 +1371,7 @@ void GameSpacePirates::sceneIsoPickAWorld(Scene *scene) {
 }
 
 void GameSpacePirates::sceneIsoSetWorldGotTo(Scene *scene) {
-	uint16 sceneNum = sceneToNumber(scene);
+	uint16 sceneNum = sceneToNumber(scene->_name);
 	_worldGotTo[_currentWorld] = sceneNum;
 }
 
@@ -1515,7 +1528,7 @@ void GameSpacePirates::sceneNxtscnAsteroidsDone(Scene *scene) {
 }
 
 void GameSpacePirates::sceneNxtscnDoFlyingSkulls(Scene *scene) {
-	_sceneBeforeFlyingSkulls = sceneToNumber(scene);
+	_sceneBeforeFlyingSkulls = sceneToNumber(scene->_name);
 	uint16 picked = randomNumberInRange(0x014A, 0x014D);
 	_curScene = Common::String::format("scene%d", picked);
 }
@@ -1761,7 +1774,7 @@ void GameSpacePirates::sceneNxtscnStartDragonsTeethPopup(Scene *scene) {
 }
 
 void GameSpacePirates::sceneNxtscnContinueDragonsTeethPopup(Scene *scene) {
-	if (sceneToNumber(scene) != 0x14F) {
+	if (sceneToNumber(scene->_name) != 0x14F) {
 		_randomCount++;
 	}
 	if (((_difficulty * 9) + 9) > _randomCount) {
diff --git a/engines/alg/logic/game_spacepirates.h b/engines/alg/logic/game_spacepirates.h
index dee1589a1e3..9162ade7bfe 100644
--- a/engines/alg/logic/game_spacepirates.h
+++ b/engines/alg/logic/game_spacepirates.h
@@ -60,6 +60,7 @@ private:
 	void init() override;
 	void registerScriptFunctions();
 	void verifyScriptFunctions();
+	void fixScriptBugs();
 	SPScriptFunctionRect getScriptFunctionRectHit(Common::String name);
 	SPScriptFunctionScene getScriptFunctionScene(SceneFuncType type, Common::String name);
 	void callScriptFunctionRectHit(Common::String name, Rect *rect);
@@ -164,7 +165,7 @@ private:
 	void displayShotLine(uint16 startX, uint16 startY, uint16 endX, uint16 endY);
 	void displayMultipleShotLines();
 	void enableVideoFadeIn();
-	uint16 sceneToNumber(Scene *scene);
+	uint16 sceneToNumber(Common::String sceneName);
 	uint16 randomUnusedScene(uint8 max);
 	uint16 randomNumberInRange(uint16 min, uint16 max);
 	uint16 pickCrystalScene(uint16 scene1, uint16 scene2, uint16 scene3);


Commit: 51a5e63af7f1e733ae15de637ed0e67688bae4b0
    https://github.com/scummvm/scummvm/commit/51a5e63af7f1e733ae15de637ed0e67688bae4b0
Author: loki (loki at localhost)
Date: 2026-03-01T13:20:13+01:00

Commit Message:
ALG: Refactoring, improve support for LBH

Changed paths:
    engines/alg/game.cpp
    engines/alg/game.h
    engines/alg/logic/game_bountyhunter.cpp
    engines/alg/logic/game_bountyhunter.h
    engines/alg/logic/game_crimepatrol.cpp
    engines/alg/logic/game_crimepatrol.h
    engines/alg/logic/game_drugwars.cpp
    engines/alg/logic/game_drugwars.h
    engines/alg/logic/game_johnnyrock.cpp
    engines/alg/logic/game_johnnyrock.h
    engines/alg/logic/game_maddog.cpp
    engines/alg/logic/game_maddog.h
    engines/alg/logic/game_maddog2.cpp
    engines/alg/logic/game_maddog2.h
    engines/alg/logic/game_spacepirates.cpp
    engines/alg/logic/game_spacepirates.h
    engines/alg/scene.cpp
    engines/alg/scene.h


diff --git a/engines/alg/game.cpp b/engines/alg/game.cpp
index 8b9c0e4bbd4..0c3914d3f15 100644
--- a/engines/alg/game.cpp
+++ b/engines/alg/game.cpp
@@ -199,59 +199,6 @@ Rect *Game::checkZone(Zone *zone, Common::Point *point) {
 	return nullptr;
 }
 
-// This is used by earlier games
-Zone *Game::checkZonesV1(Scene *scene, Rect *&hitRect, Common::Point *point) {
-	for (auto &zone : scene->_zones) {
-		uint32 startFrame = zone->_startFrame - _videoFrameSkip + 1;
-		uint32 endFrame = zone->_endFrame + _videoFrameSkip - 1;
-		if (_currentFrame >= startFrame && _currentFrame <= endFrame) {
-			hitRect = checkZone(zone, point);
-			if (hitRect != nullptr) {
-				return zone;
-			}
-		}
-	}
-	return nullptr;
-}
-
-// This is used by later games
-Zone *Game::checkZonesV2(Scene *scene, Rect *&hitRect, Common::Point *point, uint8 difficulty) {
-	for (auto &zone : scene->_zones) {
-		uint32 startFrame = zone->_startFrame - (_videoFrameSkip + 1) + ((difficulty - 1) * _videoFrameSkip);
-		uint32 endFrame = zone->_endFrame + (_videoFrameSkip - 1) - ((difficulty - 1) * _videoFrameSkip);
-		if (_currentFrame >= startFrame && _currentFrame <= endFrame) {
-			hitRect = checkZone(zone, point);
-			if (hitRect != nullptr) {
-				return zone;
-			}
-		}
-	}
-	return nullptr;
-}
-
-// only used by earlier games
-void Game::adjustDifficulty(uint8 newDifficulty, uint8 oldDifficulty) {
-	Common::Array<Scene *> *scenes = _sceneInfo->getScenes();
-	for (const auto &scene : *scenes) {
-		if (!(scene->_diff & 0x01)) {
-			if (scene->_preop == "PAUSE" || scene->_preop == "PAUSFI" || scene->_preop == "PAUSPR") {
-				scene->_dataParam1 = (scene->_dataParam1 * _pauseDiffScale[newDifficulty - 1]) / _pauseDiffScale[oldDifficulty - 1];
-			}
-		}
-		for (const auto &zone : scene->_zones) {
-			for (const auto &rect : zone->_rects) {
-				if (!(scene->_diff & 0x02)) {
-					int16 cx = (rect->left + rect->right) / 2;
-					int16 cy = (rect->top + rect->bottom) / 2;
-					int32 w = (rect->width() * _rectDiffScale[newDifficulty - 1]) / _rectDiffScale[oldDifficulty - 1];
-					int32 h = (rect->height() * _rectDiffScale[newDifficulty - 1]) / _rectDiffScale[oldDifficulty - 1];
-					rect->center(cx, cy, w, h);
-				}
-			}
-		}
-	}
-}
-
 uint32 Game::getFrame(Scene *scene) {
 	if (_videoDecoder->getCurrentFrame() == 0) {
 		return scene->_startFrame;
@@ -411,10 +358,14 @@ void Game::debug_drawZoneRects() {
 			Scene *targetScene = _sceneInfo->findScene(_curScene);
 			for (auto &zone : targetScene->_zones) {
 				for (auto rect : zone->_rects) {
-					_screen->drawLine(rect->left, rect->top, rect->right, rect->top, 1);
-					_screen->drawLine(rect->left, rect->top, rect->left, rect->bottom, 1);
-					_screen->drawLine(rect->right, rect->bottom, rect->right, rect->top, 1);
-					_screen->drawLine(rect->right, rect->bottom, rect->left, rect->bottom, 1);
+					// only draw frames that appear soon or are current
+					if (_currentFrame + 30 >= zone->_startFrame && _currentFrame <= zone->_endFrame) {
+						Common::Rect interpolated = rect->getInterpolatedRect(zone->_startFrame, zone->_endFrame, _currentFrame);
+						_screen->drawLine(interpolated.left, interpolated.top, interpolated.right, interpolated.top, 1);
+						_screen->drawLine(interpolated.left, interpolated.top, interpolated.left, interpolated.bottom, 1);
+						_screen->drawLine(interpolated.right, interpolated.bottom, interpolated.right, interpolated.top, 1);
+						_screen->drawLine(interpolated.right, interpolated.bottom, interpolated.left, interpolated.bottom, 1);
+					}
 				}
 			}
 		}
diff --git a/engines/alg/game.h b/engines/alg/game.h
index 25667054121..b18346b8a6b 100644
--- a/engines/alg/game.h
+++ b/engines/alg/game.h
@@ -84,10 +84,7 @@ protected:
 	uint32 getMsTime();
 	bool fired(Common::Point *point);
 	Rect *checkZone(Zone *zone, Common::Point *point);
-	Zone *checkZonesV1(Scene *scene, Rect *&hitRect, Common::Point *point);
-	Zone *checkZonesV2(Scene *scene, Rect *&hitRect, Common::Point *point, uint8 difficulty);
 	uint32 getFrame(Scene *scene);
-	void adjustDifficulty(uint8 newDifficulty, uint8 oldDifficulty);
 	int8 skipToNewScene(Scene *scene);
 	void debug_drawZoneRects();
 
diff --git a/engines/alg/logic/game_bountyhunter.cpp b/engines/alg/logic/game_bountyhunter.cpp
index acf312d202f..ecd56e19fa6 100644
--- a/engines/alg/logic/game_bountyhunter.cpp
+++ b/engines/alg/logic/game_bountyhunter.cpp
@@ -52,10 +52,6 @@ GameBountyHunter::~GameBountyHunter() {
 		_deadIcon->free();
 		delete _deadIcon;
 	}
-	if (_diffIcon) {
-		_diffIcon->free();
-		delete _diffIcon;
-	}
 	if (_bulletholeIcon) {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
@@ -370,6 +366,7 @@ void GameBountyHunter::callScriptFunctionScene(SceneFuncType type, Common::Strin
 Common::Error GameBountyHunter::run() {
 	init();
 	newGame();
+	_numPlayers = 1;
 	_curScene = _startScene;
 	Common::String oldscene;
 	while (!_vm->shouldQuit()) {
@@ -377,24 +374,21 @@ Common::Error GameBountyHunter::run() {
 		_fired = false;
 		Scene *scene = _sceneInfo->findScene(_curScene);
 		if (!loadScene(scene)) {
-			error("GameBountyHunter::run(): Cannot find scene %s in libfile", scene->_name.c_str());
+			_curScene = scene->_next;
+			continue;
 		}
 		_sceneSkipped = false;
 		_paletteDirty = true;
 		_nextFrameTime = getMsTime() + 100;
+		_lastShotTime = getMsTime();
 		callScriptFunctionScene(PREOP, scene->_preop, scene);
 		_currentFrame = getFrame(scene);
 		while (_currentFrame <= scene->_endFrame && _curScene == oldscene && !_vm->shouldQuit()) {
 			updateMouse();
-			callScriptFunctionScene(SHOWMSG, scene->_scnmsg, scene);
 			callScriptFunctionScene(INSOP, scene->_insop, scene);
-			_holster = weaponDown();
-			if (_holster) {
-				callScriptFunctionScene(WEPDWN, scene->_wepdwn, scene);
-			}
 			Common::Point firedCoords;
 			if (fired(&firedCoords)) {
-				if (!_holster) {
+				if (_lastShotTime == 0) {
 					Rect *hitGlobalRect = checkZone(_menuZone, &firedCoords);
 					if (hitGlobalRect != nullptr) {
 						callScriptFunctionRectHit(hitGlobalRect->_rectHit, hitGlobalRect);
@@ -402,24 +396,17 @@ Common::Error GameBountyHunter::run() {
 						if (_playerShots[_player] > 0) {
 							if (!_debug_unlimitedAmmo) {
 								_playerShots[_player]--;
+								_lastShotTime = getMsTime();
 							}
 							displayShotFiredImage(&firedCoords);
-							playSound(_shotSound);
+							playSound(_shotSound); // TODO different sound for shotgun
 							Rect *hitRect = nullptr;
-							Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords, _difficulty);
+							Zone *hitSceneZone = checkZones(scene, hitRect, &firedCoords);
 							if (hitSceneZone != nullptr) {
 								callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
-							} else {
-								int8 skip = skipToNewScene(scene);
-								if (skip == -1) {
+								if (_curScene == oldscene) {
+									// skip to new scene
 									callScriptFunctionScene(NXTSCN, scene->_nxtscn, scene);
-								} else if (skip == 1) {
-									if (scene->_dataParam4 > 0) {
-										uint32 framesToSkip = (scene->_dataParam4 - _currentFrame) / _videoFrameSkip;
-										_videoDecoder->skipNumberOfFrames(framesToSkip);
-									} else {
-										callScriptFunctionScene(NXTSCN, scene->_nxtscn, scene);
-									}
 								}
 							}
 						} else {
@@ -431,9 +418,20 @@ Common::Error GameBountyHunter::run() {
 			if (_curScene == oldscene) {
 				callScriptFunctionScene(NXTFRM, scene->_nxtfrm, scene);
 			}
-			displayLivesLeft(0);
-			displayScores(0);
-			displayShotsLeft(0);
+			for (int i = 0; i < _numPlayers; i++) {
+				if (weaponDown()) {
+					sceneDefaultWepdwn(scene);
+				}
+				displayScores(i);
+				displayLivesLeft(i);
+				displayShotsLeft(i);
+			}
+			if (_lastShotTime != 0) {
+                if (getMsTime() - _lastShotTime > 185) {
+                    _lastShotTime = 0;
+                }
+            }
+
 			moveMouse();
 			if (_pauseTime > 0) {
 				_videoDecoder->pauseAudio(true);
@@ -466,6 +464,9 @@ Common::Error GameBountyHunter::run() {
 		if (_curScene == oldscene) {
 			callScriptFunctionScene(NXTSCN, scene->_nxtscn, scene);
 		}
+		if (_curScene == oldscene) {
+			_curScene = scene->_next;
+		}
 		if (_curScene == "") {
 			shutdown();
 		}
@@ -479,7 +480,6 @@ void GameBountyHunter::newGame() {
 	_playerGun[0] = _playerGun[1] = 1;
 	_playerScore[0] = _playerScore[1] = 0;
 	_currentSubLevelSceneId = 0x017B;
-	_holster = false;
 }
 
 void GameBountyHunter::doMenu() {
@@ -562,51 +562,52 @@ void GameBountyHunter::moveMouse() {
 }
 
 void GameBountyHunter::displayLivesLeft(uint8 player) {
-	if (_lives == _oldLives) {
+	if (_lives[player] == _oldLives[player]) {
 		return;
 	}
-	int posY = 0x67;
+	int posX = player == 0 ? 0xD7 : 0x50;
 	for (int i = 0; i < 3; i++) {
-		AlgGraphics::drawImage(_screen, _deadIcon, 0x12F, posY);
-		posY += 0xE;
+		AlgGraphics::drawImage(_screen, _deadIcon, posX, 0xBE);
+		posX += 10;
 	}
-	posY = 0x67;
-	for (int i = 0; i < _lives; i++) {
-		AlgGraphics::drawImage(_screen, _liveIcon, 0x12F, posY);
-		posY += 0xE;
+	posX = player == 0 ? 0xD7 : 0x50;
+	for (int i = 0; i < _lives[player]; i++) {
+		AlgGraphics::drawImage(_screen, _liveIcon, posX, 0xBE);
+		posX += 10;
 	}
-	_oldLives = _lives;
+	_oldLives[player] = _lives[player];
 }
 
 void GameBountyHunter::displayScores(uint8 player) {
-	if (_score == _oldScore) {
+	if (_score[player] == _oldScore[player]) {
 		return;
 	}
-	Common::String scoreString = Common::String::format("%05d", _score);
-	int posX = 0x9B;
+	Common::String scoreString = Common::String::format("%05d", _score[player]);
+	int posX = player == 0 ? 0xA5 : 0x78;
 	for (int i = 0; i < 5; i++) {
 		uint8 digit = scoreString[i] - '0';
-		AlgGraphics::drawImage(_screen, (*_numbers)[digit], posX, 0xBF);
-		posX += 7;
+		uint8 digitOffset = player == 1 ? digit : digit + 10;
+		AlgGraphics::drawImage(_screen, (*_numbers)[digitOffset], posX, 0xBF);
+		posX += 5;
 	}
-	_oldScore = _score;
+	_oldScore[player] = _score[player];
 }
 
 void GameBountyHunter::displayShotsLeft(uint8 player) {
-	if (_shots == _oldShots) {
+	if (_shots[player] == _oldShots[player]) {
 		return;
 	}
-	uint16 posX = 0xEE;
+	uint16 posX = player == 0 ? 0xF8 : 0x0C;
 	for (int i = 0; i < 10; i++) {
-		AlgGraphics::drawImage(_screen, _emptyIcon, posX, 0xBE);
+		AlgGraphics::drawImage(_screen, _emptyIcon, posX, 0xBF);
 		posX += 5;
 	}
-	posX = 0xEE;
-	for (int i = 0; i < _shots; i++) {
-		AlgGraphics::drawImage(_screen, _shotIcon, posX, 0xBE);
+	posX = player == 0 ? 0xF8 : 0x0C;
+	for (int i = 0; i < _shots[player]; i++) {
+		AlgGraphics::drawImage(_screen, _shotIcon, posX, 0xBF);
 		posX += 5;
 	}
-	_oldShots = _shots;
+	_oldShots[player] = _shots[player];
 }
 
 bool GameBountyHunter::weaponDown() {
@@ -635,7 +636,7 @@ bool GameBountyHunter::saveState() {
 		outSaveFile->writeByte(_playerShots[i]);
 		outSaveFile->writeUint32LE(_playerScore[i]);
 	}
-	outSaveFile->writeByte(_unk_2ADA6);
+	outSaveFile->writeByte(_difficulty);
 	outSaveFile->writeByte(_numPlayers);
 	outSaveFile->finalize();
 	delete outSaveFile;
@@ -664,7 +665,7 @@ bool GameBountyHunter::loadState() {
 		_playerShots[i] = inSaveFile->readByte();
 		_playerScore[i] = inSaveFile->readUint32LE();
 	}
-	_unk_2ADA6 = inSaveFile->readByte();
+	_difficulty = inSaveFile->readByte();
 	_numPlayers = inSaveFile->readByte();
 	assert(_numPlayers <= 2);
 	delete inSaveFile;
@@ -672,6 +673,35 @@ bool GameBountyHunter::loadState() {
 	return true;
 }
 
+Zone *GameBountyHunter::checkZones(Scene *scene, Rect *&hitRect, Common::Point *point) {
+	for (auto &zone : scene->_zones) {
+		if (zone->_difficulty == _difficulty) {
+			uint32 startFrame = zone->_startFrame - (_videoFrameSkip + 1);
+			uint32 endFrame = zone->_endFrame + (_videoFrameSkip - 1);
+			if (_currentFrame >= startFrame && _currentFrame <= endFrame) {
+				hitRect = checkZone(zone, point);
+				if (hitRect != nullptr) {
+					return zone;
+				}
+			}
+		}
+	}
+	return nullptr;
+}
+
+Rect *GameBountyHunter::checkZone(Zone *zone, Common::Point *point) {
+	for (auto &rect : zone->_rects) {
+		Common::Rect interpolated = rect->getInterpolatedRect(zone->_startFrame, zone->_endFrame, _currentFrame);
+		if (point->x >= interpolated.left &&
+			point->x <= interpolated.right &&
+			point->y >= interpolated.top &&
+			point->y <= interpolated.bottom) {
+			return rect;
+		}
+	}
+	return nullptr;
+}
+
 // misc game functions
 void GameBountyHunter::setNextScene(uint16 sceneId) {
 	_curScene = Common::String::format("%d", sceneId);
@@ -735,16 +765,15 @@ uint16 GameBountyHunter::randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude
 }
 
 uint16 GameBountyHunter::pickRandomScene(uint16 *sceneList, uint8 max) {
-	if (max == 0) {
-		return 0;
-	}
-	_randomSceneList = sceneList;
-	_randomMax = max;
-	_randomMask = 0;
-	_randomPicked = -1;
-	_randomSceneCount = 0;
-	while (_randomSceneList[_randomSceneCount] != 0) {
-		_randomSceneCount++;
+	if (max != 0) {
+		_randomSceneList = sceneList;
+		_randomMax = max;
+		_randomMask = 0;
+		_randomPicked = -1;
+		_randomSceneCount = 0;
+		while (_randomSceneList[_randomSceneCount] != 0) {
+			_randomSceneCount++;
+		}
 	}
 	uint16 count = _randomMax--;
 	if (count > 0) {
@@ -799,7 +828,7 @@ uint16 GameBountyHunter::pickDeathScene() {
 uint16 GameBountyHunter::timeForGunfight() {
 	uint16 picked = 0;
 	if (--_gunfightCount <= 0) {
-		int index = (_unk_2ADA6 * 5) + (_numLevelsDone);
+		int index = (_difficulty * 5) + (_numLevelsDone);
 		_gunfightCount = _gunfightCountDown[index];
 		picked = pickGunfightScene();
 	}
@@ -834,7 +863,13 @@ void GameBountyHunter::doShotgunSound() {
 
 // Script functions: RectHit
 void GameBountyHunter::rectNewScene(Rect *rect) {
-	_score += rect->_score;
+	uint32 _weightedScore = rect->_score + (_difficulty * 10);
+	_score[_player] += _weightedScore;
+	_pointsSinceLastBonus[_player] += _weightedScore;
+	if (_pointsSinceLastBonus[_player] > 5000) {
+		_pointsSinceLastBonus[_player] = 0;
+		_lives[_player]++;
+	}
 	if (!rect->_scene.empty()) {
 		_curScene = rect->_scene;
 	}
@@ -890,18 +925,6 @@ void GameBountyHunter::rectStart(Rect *rect) {
 	newGame();
 }
 
-void GameBountyHunter::rectEasy(Rect *rect) {
-	_difficulty = 1;
-}
-
-void GameBountyHunter::rectAverage(Rect *rect) {
-	_difficulty = 2;
-}
-
-void GameBountyHunter::rectHard(Rect *rect) {
-	_difficulty = 3;
-}
-
 void GameBountyHunter::rectExit(Rect *rect) {
 	shutdown();
 }
@@ -909,17 +932,17 @@ void GameBountyHunter::rectExit(Rect *rect) {
 void GameBountyHunter::rectTogglePlayers(Rect *rect) {
 	if (_numPlayers == 1) {
 		_numPlayers = 2;
-		AlgGraphics::drawImage(_screen, _playersIcon2, 0xCE, 0x95);
-		AlgGraphics::drawImage(_screen, _textBlackBarIcon, 0x78, 0xBF);
-		AlgGraphics::drawImage(_screen, _textBlackBarIcon, 0x0C, 0xBF);
+		AlgGraphics::drawImage(_background, _playersIcon2, 0xCE, 0x95);
+		AlgGraphics::drawImage(_background, _textBlackBarIcon, 0x78, 0xBF);
+		AlgGraphics::drawImage(_background, _textBlackBarIcon, 0x0C, 0xBF);
 		displayShotsLeft(1);
 		displayLivesLeft(1);
 	} else {
 		_numPlayers = 1;
-		AlgGraphics::drawImage(_screen, _playersIcon1, 0xCE, 0x95);
-		AlgGraphics::drawImage(_screen, _textScoreIcon, 0x78, 0xBF);
-		AlgGraphics::drawImage(_screen, _textMenuIcon, 0x0C, 0xBF);
-		AlgGraphics::drawImage(_screen, _textBlackBarIcon, 0x50, 0xBE);
+		AlgGraphics::drawImage(_background, _playersIcon1, 0xCE, 0x95);
+		AlgGraphics::drawImage(_background, _textScoreIcon, 0x78, 0xBF);
+		AlgGraphics::drawImage(_background, _textMenuIcon, 0x0C, 0xBF);
+		AlgGraphics::drawImage(_background, _textBlackBarIcon, 0x50, 0xBE);
 	}
 	playSound(_skullSound);
 	_screen->copyRectToSurface(_background->getBasePtr(_videoPosX, _videoPosY), _background->pitch, _videoPosX, _videoPosY, _videoDecoder->getWidth(), _videoDecoder->getHeight());
@@ -1037,8 +1060,8 @@ void GameBountyHunter::scenePsoSetCurrentScene(Scene *scene) {
 	if (sceneId == 0) {
 		uint8 index = (_currentLevel * 24) + (_numLevelsDone * 6) + _numSubLevelsDone;
 		uint8 subLevel = _subLevelOrder[index];
-		uint16 picked = (_currentLevel * 20) + (subLevel * 4);
-		_currentSubLevelSceneId = 0x0D32 + picked;
+		uint16 picked = (_currentLevel * 10) + subLevel;
+		_currentSubLevelSceneId = _subLevelSceneIds[picked];
 	}
 }
 
@@ -1152,16 +1175,12 @@ void GameBountyHunter::sceneNxtscnGotoLevelSelect(Scene *scene) {
 }
 
 void GameBountyHunter::sceneNxtscnContinueRandom(Scene *scene) {
-	// TODO verify
-	sceneNxtscnNextSubLevel(scene);
-	/*
-	uint16 picked = _PickRandomScene(0, 0);
+	uint16 picked = pickRandomScene(0, 0);
 	if (picked == 0) {
-		_scene_nxtscn_next_sub_level(scene);
-		return;
+		sceneNxtscnNextSubLevel(scene);
+	} else {
+		setNextScene(picked);
 	}
-	_SetNextScene(picked);
-	*/
 }
 
 void GameBountyHunter::sceneNxtscnInitRandomHarry1(Scene *scene) {
@@ -1282,8 +1301,8 @@ void GameBountyHunter::sceneNxtscnWoundedMain(Scene *scene) {
 void GameBountyHunter::sceneNxtscnEndLevel(Scene *scene) {
 	_levelDoneMask |= (2 << _currentLevel);
 	_numLevelsDone++;
-	if (_numLevelsDone > 1 && _unk_2ADA6 < 2) {
-		_unk_2ADA6++;
+	if (_numLevelsDone > 1 && _difficulty < 2) {
+		_difficulty++;
 	}
 	_numSubLevelsDone = 0;
 	_currentSubLevelSceneId = 0;
@@ -1370,11 +1389,39 @@ void GameBountyHunter::sceneDefaultWepdwn(Scene *scene) {
 // Script functions: ScnScr
 void GameBountyHunter::sceneDefaultScore(Scene *scene) {
 	if (scene->_scnscrParam > 0) {
-		_score += scene->_scnscrParam;
+		_score[0] += scene->_scnscrParam;
 	}
+	// TODO verify, nothing for player2?
 }
 
 // Debug methods
+void GameBountyHunter::debug_drawZoneRects() {
+	if (_debug_drawRects || debugChannelSet(1, Alg::kAlgDebugGraphics)) {
+		if (_inMenu) {
+			for (auto rect : _subMenuZone->_rects) {
+				_screen->drawLine(rect->left, rect->top, rect->right, rect->top, 1);
+				_screen->drawLine(rect->left, rect->top, rect->left, rect->bottom, 1);
+				_screen->drawLine(rect->right, rect->bottom, rect->right, rect->top, 1);
+				_screen->drawLine(rect->right, rect->bottom, rect->left, rect->bottom, 1);
+			}
+		} else if (_curScene != "") {
+			Scene *targetScene = _sceneInfo->findScene(_curScene);
+			for (auto &zone : targetScene->_zones) {
+				for (auto rect : zone->_rects) {
+					// only draw frames that appear soon or are current and match current difficulty level
+					if (_currentFrame + 30 >= zone->_startFrame && _currentFrame <= zone->_endFrame && zone->_difficulty == _difficulty) {
+						Common::Rect interpolated = rect->getInterpolatedRect(zone->_startFrame, zone->_endFrame, _currentFrame);
+						_screen->drawLine(interpolated.left, interpolated.top, interpolated.right, interpolated.top, 1);
+						_screen->drawLine(interpolated.left, interpolated.top, interpolated.left, interpolated.bottom, 1);
+						_screen->drawLine(interpolated.right, interpolated.bottom, interpolated.right, interpolated.top, 1);
+						_screen->drawLine(interpolated.right, interpolated.bottom, interpolated.left, interpolated.bottom, 1);
+					}
+				}
+			}
+		}
+	}
+}
+
 void GameBountyHunter::debugWarpTo(int val) {
 	// TODO implement
 }
diff --git a/engines/alg/logic/game_bountyhunter.h b/engines/alg/logic/game_bountyhunter.h
index 55d1ac769f1..fe9f7680f44 100644
--- a/engines/alg/logic/game_bountyhunter.h
+++ b/engines/alg/logic/game_bountyhunter.h
@@ -80,7 +80,6 @@ private:
 	Graphics::Surface *_emptyIcon;
 	Graphics::Surface *_liveIcon;
 	Graphics::Surface *_deadIcon;
-	Graphics::Surface *_diffIcon;
 	Graphics::Surface *_bulletholeIcon;
 	Graphics::Surface *_playersIcon1;
 	Graphics::Surface *_playersIcon2;
@@ -123,7 +122,7 @@ private:
 	const uint8 _mainLevelMasks[5] = {2, 4, 8, 0x10, 0x80};
 	const uint8 _gunfightCountDown[15] = {5, 4, 3, 3, 3, 4, 3, 3, 2, 1, 3, 2, 2, 2, 1};
 
-	// const uint16 _firstSceneInScenario[4] = {4, 0x36, 0x36, 0x66};
+	// const uint16 _firstSceneInScenario[4] = {0x04, 0x36, 0x36, 0x66};
 	const uint16 _moneyScenes[4] = {0x017D, 0x013C, 0xC3, 0x69};
 	const uint16 _gunfightScenarios[18] = {0x0116, 0x0118, 0x011B, 0x011D, 0x011F, 0x0121, 0x0123, 0x0125, 0x0127,
 										   0x0129, 0x012B, 0x012D, 0x012F, 0x0131, 0x0133, 0x0135, 0x0137, 0x0139};
@@ -133,17 +132,19 @@ private:
 	const uint16 _allPlayersDead = 0x108;
 
 	// gamestate
-	uint8 _difficulty = 1;
+	uint8 _difficulty = 0;
 	uint8 _oldDifficulty = 1;
-	bool _holster = false;
-	int8 _lives = 0;
-	int8 _oldLives = 0;
-	int32 _score = 0;
-	int32 _oldScore = -1;
-	uint16 _shots = 0;
-	uint8 _oldShots = 0;
+	uint32 _lastShotTime = 0;
+	int8 _lives[2] = {0, 0};
+	int8 _oldLives[2] = {0, 0};
+	int32 _score[2] = {0, 0};
+	int32 _oldScore[2] = {-1, -1};
+	uint32 _pointsSinceLastBonus[2] = {0, 0};
+	uint16 _shots[2] = {0, 0};
+	uint8 _oldShots[2] = {0, 0};
 	uint8 _whichGun = 0;
 	uint8 _oldWhichGun = 0xFF;
+	// TODO verify, whichGun for player2?
 
 	uint16 _restartScene = 0;
 	uint8 _numPlayers = 1;
@@ -191,8 +192,6 @@ private:
 	uint32 _firstDrawFrame = 0;
 	uint8 _count = 0;
 
-	uint8 _unk_2ADA6 = 0;
-
 	Common::String _subScene;
 
 	// base functions
@@ -207,6 +206,8 @@ private:
 	bool weaponDown();
 	bool saveState();
 	bool loadState();
+	Zone *checkZones(Scene *scene, Rect *&hitRect, Common::Point *point);
+	Rect *checkZone(Zone *zone, Common::Point *point);
 
 	// misc game functions
 	void setNextScene(uint16 sceneId);
@@ -231,9 +232,6 @@ private:
 	void rectLoad(Rect *rect);
 	void rectContinue(Rect *rect);
 	void rectStart(Rect *rect);
-	void rectEasy(Rect *rect);
-	void rectAverage(Rect *rect);
-	void rectHard(Rect *rect);
 	void rectExit(Rect *rect);
 	void rectTogglePlayers(Rect *rect);
 	void rectHitIconJug(Rect *rect);
@@ -299,6 +297,9 @@ private:
 
 	// Script functions: ScnScr
 	void sceneDefaultScore(Scene *scene);
+
+	// debug methods
+	void debug_drawZoneRects();
 };
 
 class DebuggerBountyHunter : public GUI::Debugger {
diff --git a/engines/alg/logic/game_crimepatrol.cpp b/engines/alg/logic/game_crimepatrol.cpp
index e03e5a974a3..514260b63e3 100644
--- a/engines/alg/logic/game_crimepatrol.cpp
+++ b/engines/alg/logic/game_crimepatrol.cpp
@@ -354,7 +354,7 @@ Common::Error GameCrimePatrol::run() {
 						displayShotFiredImage(&firedCoords);
 						playSound(_shotSound);
 						Rect *hitRect = nullptr;
-						Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords, _difficulty);
+						Zone *hitSceneZone = checkZones(scene, hitRect, &firedCoords);
 						if (hitSceneZone != nullptr) {
 							callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
 						} else {
@@ -636,6 +636,20 @@ bool GameCrimePatrol::loadState() {
 	return true;
 }
 
+Zone *GameCrimePatrol::checkZones(Scene *scene, Rect *&hitRect, Common::Point *point) {
+	for (auto &zone : scene->_zones) {
+		uint32 startFrame = zone->_startFrame - (_videoFrameSkip + 1) + ((_difficulty - 1) * _videoFrameSkip);
+		uint32 endFrame = zone->_endFrame + (_videoFrameSkip - 1) - ((_difficulty - 1) * _videoFrameSkip);
+		if (_currentFrame >= startFrame && _currentFrame <= endFrame) {
+			hitRect = checkZone(zone, point);
+			if (hitRect != nullptr) {
+				return zone;
+			}
+		}
+	}
+	return nullptr;
+}
+
 // misc game functions
 void GameCrimePatrol::displayShotFiredImage(Common::Point *point) {
 	if (point->x >= _videoPosX && point->x <= (_videoPosX + _videoDecoder->getWidth()) && point->y >= _videoPosY && point->y <= (_videoPosY + _videoDecoder->getHeight())) {
diff --git a/engines/alg/logic/game_crimepatrol.h b/engines/alg/logic/game_crimepatrol.h
index 169d4b25866..ac4d587ecec 100644
--- a/engines/alg/logic/game_crimepatrol.h
+++ b/engines/alg/logic/game_crimepatrol.h
@@ -180,6 +180,7 @@ private:
 	bool weaponDown();
 	bool saveState();
 	bool loadState();
+	Zone *checkZones(Scene *scene, Rect *&hitRect, Common::Point *point);
 
 	// misc game functions
 	void displayShotFiredImage(Common::Point *point);
diff --git a/engines/alg/logic/game_drugwars.cpp b/engines/alg/logic/game_drugwars.cpp
index 203eb5c7022..b60e3a8a861 100644
--- a/engines/alg/logic/game_drugwars.cpp
+++ b/engines/alg/logic/game_drugwars.cpp
@@ -298,7 +298,7 @@ Common::Error GameDrugWars::run() {
 						displayShotFiredImage(&firedCoords);
 						playSound(_shotSound);
 						Rect *hitRect = nullptr;
-						Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords, _difficulty);
+						Zone *hitSceneZone = checkZones(scene, hitRect, &firedCoords);
 						if (hitSceneZone != nullptr) {
 							callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
 						} else {
@@ -588,6 +588,20 @@ bool GameDrugWars::loadState() {
 	return true;
 }
 
+Zone *GameDrugWars::checkZones(Scene *scene, Rect *&hitRect, Common::Point *point) {
+	for (auto &zone : scene->_zones) {
+		uint32 startFrame = zone->_startFrame - (_videoFrameSkip + 1) + ((_difficulty - 1) * _videoFrameSkip);
+		uint32 endFrame = zone->_endFrame + (_videoFrameSkip - 1) - ((_difficulty - 1) * _videoFrameSkip);
+		if (_currentFrame >= startFrame && _currentFrame <= endFrame) {
+			hitRect = checkZone(zone, point);
+			if (hitRect != nullptr) {
+				return zone;
+			}
+		}
+	}
+	return nullptr;
+}
+
 // misc game functions
 void GameDrugWars::displayShotFiredImage(Common::Point *point) {
 	if (point->x >= _videoPosX && point->x <= (_videoPosX + _videoDecoder->getWidth()) && point->y >= _videoPosY && point->y <= (_videoPosY + _videoDecoder->getHeight())) {
diff --git a/engines/alg/logic/game_drugwars.h b/engines/alg/logic/game_drugwars.h
index c7096f35c4d..998bbc86238 100644
--- a/engines/alg/logic/game_drugwars.h
+++ b/engines/alg/logic/game_drugwars.h
@@ -159,6 +159,7 @@ private:
 	bool weaponDown();
 	bool saveState();
 	bool loadState();
+	Zone *checkZones(Scene *scene, Rect *&hitRect, Common::Point *point);
 
 	// misc game functions
 	void displayShotFiredImage(Common::Point *point);
diff --git a/engines/alg/logic/game_johnnyrock.cpp b/engines/alg/logic/game_johnnyrock.cpp
index c3855c5a88e..6acdc8041d5 100644
--- a/engines/alg/logic/game_johnnyrock.cpp
+++ b/engines/alg/logic/game_johnnyrock.cpp
@@ -349,7 +349,7 @@ Common::Error GameJohnnyRock::run() {
 						}
 						updateStat();
 						Rect *hitRect = nullptr;
-						Zone *hitSceneZone = checkZonesV1(scene, hitRect, &firedCoords);
+						Zone *hitSceneZone = checkZones(scene, hitRect, &firedCoords);
 						if (hitSceneZone != nullptr) {
 							callScriptFunctionZonePtrFb(hitSceneZone->_ptrfb, &firedCoords);
 							callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
@@ -587,11 +587,36 @@ void GameJohnnyRock::changeDifficulty(uint8 newDifficulty) {
 		return;
 	}
 	showDifficulty(newDifficulty, true);
-	Game::adjustDifficulty(newDifficulty, _oldDifficulty);
+	adjustDifficulty(newDifficulty, _oldDifficulty);
 	_oldDifficulty = newDifficulty;
 	_difficulty = newDifficulty;
 }
 
+void GameJohnnyRock::adjustDifficulty(uint8 newDifficulty, uint8 oldDifficulty) {
+	Common::Array<Scene *> *scenes = _sceneInfo->getScenes();
+	for (size_t i = 0; i < scenes->size(); i++) {
+		Scene *scene = (*scenes)[i];
+		if (!(scene->_diff & 0x01)) {
+			if (scene->_preop == "PAUSE" || scene->_preop == "PAUSFI" || scene->_preop == "PAUSPR") {
+				scene->_dataParam1 = (scene->_dataParam1 * _pauseDiffScale[newDifficulty - 1]) / _pauseDiffScale[oldDifficulty - 1];
+			}
+		}
+		for (size_t j = 0; j < scene->_zones.size(); j++) {
+			Zone *zone = scene->_zones[j];
+			for (size_t k = 0; k < zone->_rects.size(); k++) {
+				Rect *rect = zone->_rects[k];
+				if (!(scene->_diff & 0x02)) {
+					int16 cx = (rect->left + rect->right) / 2;
+					int16 cy = (rect->top + rect->bottom) / 2;
+					int32 w = (rect->width() * _rectDiffScale[newDifficulty - 1]) / _rectDiffScale[oldDifficulty - 1];
+					int32 h = (rect->height() * _rectDiffScale[newDifficulty - 1]) / _rectDiffScale[oldDifficulty - 1];
+					rect->center(cx, cy, w, h);
+				}
+			}
+		}
+	}
+}
+
 void GameJohnnyRock::updateCursor() {
 	_oldWhichGun = _whichGun;
 }
@@ -800,6 +825,20 @@ void GameJohnnyRock::doMoneySound() {
 	playSound(_moneySound);
 }
 
+Zone *GameJohnnyRock::checkZones(Scene *scene, Rect *&hitRect, Common::Point *point) {
+	for (auto &zone : scene->_zones) {
+		uint32 startFrame = zone->_startFrame - _videoFrameSkip + 1;
+		uint32 endFrame = zone->_endFrame + _videoFrameSkip - 1;
+		if (_currentFrame >= startFrame && _currentFrame <= endFrame) {
+			hitRect = checkZone(zone, point);
+			if (hitRect != nullptr) {
+				return zone;
+			}
+		}
+	}
+	return nullptr;
+}
+
 // Misc game functions
 Common::String GameJohnnyRock::numToScene(int n) {
 	switch (n) {
diff --git a/engines/alg/logic/game_johnnyrock.h b/engines/alg/logic/game_johnnyrock.h
index d426e00d941..614af5a2af3 100644
--- a/engines/alg/logic/game_johnnyrock.h
+++ b/engines/alg/logic/game_johnnyrock.h
@@ -191,6 +191,7 @@ private:
 	void displayScore();
 	void showDifficulty(uint8 newDifficulty, bool updateCursor);
 	void changeDifficulty(uint8 newDifficulty);
+	void adjustDifficulty(uint8 newDifficulty, uint8 oldDifficulty);
 	void updateCursor();
 	void updateMouse();
 	void moveMouse();
@@ -198,6 +199,7 @@ private:
 	bool saveState();
 	bool loadState();
 	void doMoneySound();
+	Zone *checkZones(Scene *scene, Rect *&hitRect, Common::Point *point);
 
 	// misc game functions
 	Common::String numToScene(int n);
diff --git a/engines/alg/logic/game_maddog.cpp b/engines/alg/logic/game_maddog.cpp
index 218d4709e2e..09121246305 100644
--- a/engines/alg/logic/game_maddog.cpp
+++ b/engines/alg/logic/game_maddog.cpp
@@ -370,7 +370,7 @@ Common::Error GameMaddog::run() {
 						}
 						updateStat();
 						hitRect = nullptr;
-						Zone *hitSceneZone = checkZonesV1(scene, hitRect, &firedCoords);
+						Zone *hitSceneZone = checkZones(scene, hitRect, &firedCoords);
 						if (hitSceneZone != nullptr) {
 							callScriptFunctionZonePtrFb(hitSceneZone->_ptrfb, &firedCoords);
 							callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
@@ -533,7 +533,7 @@ void GameMaddog::changeDifficulty(uint8 newDifficulty) {
 		return;
 	}
 	showDifficulty(newDifficulty, true);
-	Game::adjustDifficulty(newDifficulty, _oldDifficulty);
+	adjustDifficulty(newDifficulty, _oldDifficulty);
 	_oldDifficulty = newDifficulty;
 	_difficulty = newDifficulty;
 }
@@ -547,6 +547,31 @@ void GameMaddog::showDifficulty(uint8 newDifficulty, bool cursor) {
 	}
 }
 
+void GameMaddog::adjustDifficulty(uint8 newDifficulty, uint8 oldDifficulty) {
+	Common::Array<Scene *> *scenes = _sceneInfo->getScenes();
+	for (size_t i = 0; i < scenes->size(); i++) {
+		Scene *scene = (*scenes)[i];
+		if (!(scene->_diff & 0x01)) {
+			if (scene->_preop == "PAUSE" || scene->_preop == "PAUSFI" || scene->_preop == "PAUSPR") {
+				scene->_dataParam1 = (scene->_dataParam1 * _pauseDiffScale[newDifficulty - 1]) / _pauseDiffScale[oldDifficulty - 1];
+			}
+		}
+		for (size_t j = 0; j < scene->_zones.size(); j++) {
+			Zone *zone = scene->_zones[j];
+			for (size_t k = 0; k < zone->_rects.size(); k++) {
+				Rect *rect = zone->_rects[k];
+				if (!(scene->_diff & 0x02)) {
+					int16 cx = (rect->left + rect->right) / 2;
+					int16 cy = (rect->top + rect->bottom) / 2;
+					int32 w = (rect->width() * _rectDiffScale[newDifficulty - 1]) / _rectDiffScale[oldDifficulty - 1];
+					int32 h = (rect->height() * _rectDiffScale[newDifficulty - 1]) / _rectDiffScale[oldDifficulty - 1];
+					rect->center(cx, cy, w, h);
+				}
+			}
+		}
+	}
+}
+
 void GameMaddog::updateCursor() {
 	updateMouse();
 }
@@ -687,6 +712,20 @@ bool GameMaddog::loadState() {
 	return true;
 }
 
+Zone *GameMaddog::checkZones(Scene *scene, Rect *&hitRect, Common::Point *point) {
+	for (auto &zone : scene->_zones) {
+		uint32 startFrame = zone->_startFrame - _videoFrameSkip + 1;
+		uint32 endFrame = zone->_endFrame + _videoFrameSkip - 1;
+		if (_currentFrame >= startFrame && _currentFrame <= endFrame) {
+			hitRect = checkZone(zone, point);
+			if (hitRect != nullptr) {
+				return zone;
+			}
+		}
+	}
+	return nullptr;
+}
+
 // misc game functions
 void GameMaddog::defaultBullethole(Common::Point *point) {
 	if (point->x >= 59 && point->y <= 166) {
diff --git a/engines/alg/logic/game_maddog.h b/engines/alg/logic/game_maddog.h
index 85a5e5fbb84..6f9a1fe38e8 100644
--- a/engines/alg/logic/game_maddog.h
+++ b/engines/alg/logic/game_maddog.h
@@ -162,6 +162,7 @@ private:
 	void updateStat();
 	void changeDifficulty(uint8 newDifficulty);
 	void showDifficulty(uint8 newDifficulty, bool updateCursor);
+	void adjustDifficulty(uint8 newDifficulty, uint8 oldDifficulty);
 	void updateCursor();
 	void updateMouse();
 	void moveMouse();
@@ -169,6 +170,7 @@ private:
 	bool weaponDown();
 	bool saveState();
 	bool loadState();
+	Zone *checkZones(Scene *scene, Rect *&hitRect, Common::Point *point);
 
 	// misc game functions
 	void defaultBullethole(Common::Point *point);
diff --git a/engines/alg/logic/game_maddog2.cpp b/engines/alg/logic/game_maddog2.cpp
index e41cc04dcf2..78c0cbf46fa 100644
--- a/engines/alg/logic/game_maddog2.cpp
+++ b/engines/alg/logic/game_maddog2.cpp
@@ -378,7 +378,7 @@ Common::Error GameMaddog2::run() {
 						}
 						updateStat();
 						Rect *hitRect = nullptr;
-						Zone *hitSceneZone = checkZonesV1(scene, hitRect, &firedCoords);
+						Zone *hitSceneZone = checkZones(scene, hitRect, &firedCoords);
 						if (hitSceneZone != nullptr) {
 							callScriptFunctionZonePtrFb(hitSceneZone->_ptrfb, &firedCoords);
 							callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
@@ -542,7 +542,7 @@ void GameMaddog2::changeDifficulty(uint8 newDifficulty) {
 		return;
 	}
 	showDifficulty(newDifficulty, true);
-	Game::adjustDifficulty(newDifficulty, _oldDifficulty);
+	adjustDifficulty(newDifficulty, _oldDifficulty);
 	_oldDifficulty = newDifficulty;
 	_difficulty = newDifficulty;
 }
@@ -556,6 +556,31 @@ void GameMaddog2::showDifficulty(uint8 newDifficulty, bool cursor) {
 	}
 }
 
+void GameMaddog2::adjustDifficulty(uint8 newDifficulty, uint8 oldDifficulty) {
+	Common::Array<Scene *> *scenes = _sceneInfo->getScenes();
+	for (size_t i = 0; i < scenes->size(); i++) {
+		Scene *scene = (*scenes)[i];
+		if (!(scene->_diff & 0x01)) {
+			if (scene->_preop == "PAUSE" || scene->_preop == "PAUSFI" || scene->_preop == "PAUSPR") {
+				scene->_dataParam1 = (scene->_dataParam1 * _pauseDiffScale[newDifficulty - 1]) / _pauseDiffScale[oldDifficulty - 1];
+			}
+		}
+		for (size_t j = 0; j < scene->_zones.size(); j++) {
+			Zone *zone = scene->_zones[j];
+			for (size_t k = 0; k < zone->_rects.size(); k++) {
+				Rect *rect = zone->_rects[k];
+				if (!(scene->_diff & 0x02)) {
+					int16 cx = (rect->left + rect->right) / 2;
+					int16 cy = (rect->top + rect->bottom) / 2;
+					int32 w = (rect->width() * _rectDiffScale[newDifficulty - 1]) / _rectDiffScale[oldDifficulty - 1];
+					int32 h = (rect->height() * _rectDiffScale[newDifficulty - 1]) / _rectDiffScale[oldDifficulty - 1];
+					rect->center(cx, cy, w, h);
+				}
+			}
+		}
+	}
+}
+
 void GameMaddog2::updateCursor() {
 	updateMouse();
 }
@@ -705,6 +730,20 @@ bool GameMaddog2::loadState() {
 	return true;
 }
 
+Zone *GameMaddog2::checkZones(Scene *scene, Rect *&hitRect, Common::Point *point) {
+	for (auto &zone : scene->_zones) {
+		uint32 startFrame = zone->_startFrame - _videoFrameSkip + 1;
+		uint32 endFrame = zone->_endFrame + _videoFrameSkip - 1;
+		if (_currentFrame >= startFrame && _currentFrame <= endFrame) {
+			hitRect = checkZone(zone, point);
+			if (hitRect != nullptr) {
+				return zone;
+			}
+		}
+	}
+	return nullptr;
+}
+
 // misc game functions
 Common::String GameMaddog2::numToScene(int n) {
 	switch (n) {
diff --git a/engines/alg/logic/game_maddog2.h b/engines/alg/logic/game_maddog2.h
index 2e05e5ea65e..b099f5735c9 100644
--- a/engines/alg/logic/game_maddog2.h
+++ b/engines/alg/logic/game_maddog2.h
@@ -170,6 +170,7 @@ private:
 	void updateStat();
 	void changeDifficulty(uint8 newDifficulty);
 	void showDifficulty(uint8 newDifficulty, bool updateCursor);
+	void adjustDifficulty(uint8 newDifficulty, uint8 oldDifficulty);
 	void updateCursor();
 	void updateMouse();
 	void moveMouse();
@@ -177,6 +178,7 @@ private:
 	bool weaponDown();
 	bool saveState();
 	bool loadState();
+	Zone *checkZones(Scene *scene, Rect *&hitRect, Common::Point *point);
 
 	// misc game functions
 	Common::String numToScene(int n);
diff --git a/engines/alg/logic/game_spacepirates.cpp b/engines/alg/logic/game_spacepirates.cpp
index a66ea47ee18..1e99f82c0d1 100644
--- a/engines/alg/logic/game_spacepirates.cpp
+++ b/engines/alg/logic/game_spacepirates.cpp
@@ -384,7 +384,7 @@ Common::Error GameSpacePirates::run() {
 						displayShotFiredImage(&firedCoords);
 						playSound(_shotSound);
 						Rect *hitRect = nullptr;
-						Zone *hitSceneZone = checkZonesV2(scene, hitRect, &firedCoords, _difficulty);
+						Zone *hitSceneZone = checkZones(scene, hitRect, &firedCoords);
 						if (hitSceneZone != nullptr) {
 							callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
 						} else {
@@ -763,6 +763,20 @@ bool GameSpacePirates::loadState() {
 	return true;
 }
 
+Zone *GameSpacePirates::checkZones(Scene *scene, Rect *&hitRect, Common::Point *point) {
+	for (auto &zone : scene->_zones) {
+		uint32 startFrame = zone->_startFrame - (_videoFrameSkip + 1) + ((_difficulty - 1) * _videoFrameSkip);
+		uint32 endFrame = zone->_endFrame + (_videoFrameSkip - 1) - ((_difficulty - 1) * _videoFrameSkip);
+		if (_currentFrame >= startFrame && _currentFrame <= endFrame) {
+			hitRect = checkZone(zone, point);
+			if (hitRect != nullptr) {
+				return zone;
+			}
+		}
+	}
+	return nullptr;
+}
+
 // misc game functions
 void GameSpacePirates::playErrorSound() {
 	playSound(_skullSound);
diff --git a/engines/alg/logic/game_spacepirates.h b/engines/alg/logic/game_spacepirates.h
index 9162ade7bfe..bdd2a0625e1 100644
--- a/engines/alg/logic/game_spacepirates.h
+++ b/engines/alg/logic/game_spacepirates.h
@@ -157,6 +157,7 @@ private:
 	bool weaponDown();
 	bool saveState();
 	bool loadState();
+	Zone *checkZones(Scene *scene, Rect *&hitRect, Common::Point *point);
 
 	// misc game functions
 	void playErrorSound();
diff --git a/engines/alg/scene.cpp b/engines/alg/scene.cpp
index 562ef4f44fe..1bb677a4d25 100644
--- a/engines/alg/scene.cpp
+++ b/engines/alg/scene.cpp
@@ -248,37 +248,37 @@ void SceneInfo::parseZone(const Common::String &zoneName, uint32 startFrame, uin
 void SceneInfo::addZonesToScenes() {
 	for (auto &scene : _scenes) {
 		if (!scene->_zonesStart.empty()) {
-			Zone *zone = findZone(scene->_zonesStart);
-			scene->_zones.push_back(zone);
-			while (!zone->_next.empty()) {
-				zone = findZone(zone->_next);
-				if (zone == nullptr) {
-					break;
+			Zone *zone = nullptr;
+			do {
+				Zone *found = findZone(scene->_zonesStart);
+				if (zone) {
+					zone = found->clone();
+					zone->_difficulty = 0;
+					scene->_zones.push_back(zone);
 				}
-				scene->_zones.push_back(zone);
-			}
+			} while (zone && !zone->_next.empty());
 		}
 		if (!scene->_zonesStart2.empty() && scene->_zonesStart2 != scene->_zonesStart) {
-			Zone *zone = findZone(scene->_zonesStart2);
-			scene->_zones.push_back(zone);
-			while (!zone->_next.empty()) {
-				zone = findZone(zone->_next);
-				if (zone == nullptr) {
-					break;
+			Zone *zone = nullptr;
+			do {
+				Zone *found = findZone(scene->_zonesStart);
+				if (zone) {
+					zone = found->clone();
+					zone->_difficulty = 1;
+					scene->_zones.push_back(zone);
 				}
-				scene->_zones.push_back(zone);
-			}
+			} while (zone && !zone->_next.empty());
 		}
 		if (!scene->_zonesStart3.empty() && scene->_zonesStart3 != scene->_zonesStart2) {
-			Zone *zone = findZone(scene->_zonesStart3);
-			scene->_zones.push_back(zone);
-			while (!zone->_next.empty()) {
-				zone = findZone(zone->_next);
-				if (zone == nullptr) {
-					break;
+			Zone *zone = nullptr;
+			do {
+				Zone *found = findZone(scene->_zonesStart);
+				if (zone) {
+					zone = found->clone();
+					zone->_difficulty = 2;
+					scene->_zones.push_back(zone);
 				}
-				scene->_zones.push_back(zone);
-			}
+			} while (zone && !zone->_next.empty());
 		}
 	}
 }
@@ -355,6 +355,12 @@ Scene::Scene(const Common::String &name, uint32 startFrame, uint32 endFrame) {
 	_difficultyMod = 0;
 }
 
+Scene::~Scene() {
+	for (auto zone : _zones) {
+		delete zone;
+	}
+}
+
 Zone::Zone(const Common::String &name, const Common::String &ptrfb) {
 	_name = name;
 	_ptrfb = ptrfb;
@@ -372,7 +378,7 @@ Zone::~Zone() {
 	}
 }
 
-void Zone::addRect(int16 left, int16 top, int16 right, int16 bottom, const Common::String &scene, uint32 score, const Common::String &rectHit, const Common::String &unknown) {
+void Zone::addRect(int16 left, int16 top, int16 right, int16 bottom, const Common::String scene, uint32 score, const Common::String rectHit, const Common::String unknown) {
 	Rect *rect = new Rect();
 	rect->left = left;
 	rect->top = top;
@@ -385,4 +391,47 @@ void Zone::addRect(int16 left, int16 top, int16 right, int16 bottom, const Commo
 	_rects.push_back(rect);
 }
 
+Zone *Zone::clone() {
+	Zone *clone = new Zone(_name, _startFrame, _endFrame);
+	clone->_ptrfb = _ptrfb;
+	clone->_next = _next;
+	clone->_rects = _rects;
+	return clone;
+}
+
+void Rect::center(int16 cx, int16 cy, int16 w, int16 h) {
+	right = cx + (w / 2);
+	left = cx - (w / 2);
+	top = cy - (h / 2);
+	bottom = cy + (h / 2);
+}
+
+Common::Rect Rect::getInterpolatedRect(uint32 startFrame, uint32 endFrame, uint32 currentFrame) {
+	if (_isMoving && currentFrame >= startFrame && currentFrame < endFrame) {
+		Rect interpolated;
+		uint32 totalFrames = endFrame - startFrame;
+		uint32 relativeFrame = currentFrame - startFrame;
+		double percentage = relativeFrame * 100.0f / totalFrames;
+		interpolated.top = top + ((_dest.top - top) / 100.0f * percentage);
+		interpolated.left = left + ((_dest.left - left) / 100.0f * percentage);
+		interpolated.bottom = bottom + ((_dest.bottom - bottom) / 100.0f * percentage);
+		interpolated.right = right + ((_dest.right - right) / 100.0f * percentage);
+		return interpolated;
+	} else if (_isMoving && currentFrame >= endFrame) {
+		Rect finished;
+		finished.top = _dest.top;
+		finished.left = _dest.left;
+		finished.bottom = _dest.bottom;
+		finished.right = _dest.right;
+		return finished;
+	} else {
+		Rect normal;
+		normal.top = top;
+		normal.left = left;
+		normal.bottom = bottom;
+		normal.right = right;
+		return normal;
+	}
+}
+
 } // End of namespace Alg
diff --git a/engines/alg/scene.h b/engines/alg/scene.h
index 270a1ed0b66..5ecc8a71cb7 100644
--- a/engines/alg/scene.h
+++ b/engines/alg/scene.h
@@ -77,12 +77,8 @@ public:
 	Common::String _unknown;
 	bool _isMoving = false;
 	Common::Rect _dest;
-	void center(int16 cx, int16 cy, int16 w, int16 h) {
-		right = cx + (w / 2);
-		left = cx - (w / 2);
-		top = cy - (h / 2);
-		bottom = cy + (h / 2);
-	}
+	void center(int16 cx, int16 cy, int16 w, int16 h);
+	Common::Rect getInterpolatedRect(uint32 startFrame, uint32 endFrame, uint32 currentFrame);
 };
 
 class Zone {
@@ -96,13 +92,15 @@ public:
 	Common::String _ptrfb;
 	Common::Array<Rect *> _rects;
 	Common::String _next;
-	void addRect(int16 left, int16 top, int16 right, int16 bottom, const Common::String &scene, uint32 score, const Common::String &rectHit, const Common::String &unknown);
+	uint8 _difficulty;
+	void addRect(int16 left, int16 top, int16 right, int16 bottom, const Common::String scene, uint32 score, const Common::String rectHit, const Common::String unknown);
+	Zone *clone();
 };
 
 class Scene {
 public:
 	Scene(const Common::String &name, uint32 startFrame, uint32 endFrame);
-	~Scene() = default;
+	~Scene();
 	Common::String _name;
 	uint32 _startFrame;
 	uint32 _endFrame;


Commit: 3d5a98788bb165e6af0423469532bef9704152df
    https://github.com/scummvm/scummvm/commit/3d5a98788bb165e6af0423469532bef9704152df
Author: loki (loki at localhost)
Date: 2026-03-01T13:20:13+01:00

Commit Message:
ALG: Refactoring, further implementations for LBH

Changed paths:
    engines/alg/detection_tables.h
    engines/alg/game.cpp
    engines/alg/game.h
    engines/alg/logic/game_bountyhunter.cpp
    engines/alg/logic/game_bountyhunter.h
    engines/alg/logic/game_crimepatrol.cpp
    engines/alg/logic/game_crimepatrol.h
    engines/alg/logic/game_drugwars.cpp
    engines/alg/logic/game_drugwars.h
    engines/alg/logic/game_johnnyrock.cpp
    engines/alg/logic/game_johnnyrock.h
    engines/alg/logic/game_maddog.cpp
    engines/alg/logic/game_maddog.h
    engines/alg/logic/game_maddog2.cpp
    engines/alg/logic/game_maddog2.h
    engines/alg/logic/game_spacepirates.cpp
    engines/alg/logic/game_spacepirates.h
    engines/alg/scene.cpp
    engines/alg/scene.h


diff --git a/engines/alg/detection_tables.h b/engines/alg/detection_tables.h
index c2caad8ecd2..3b87c5b8c9d 100644
--- a/engines/alg/detection_tables.h
+++ b/engines/alg/detection_tables.h
@@ -95,7 +95,7 @@ static const AlgGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("BHDS.LIB", "6fad52a6a72830ab3373cbe3e0a3a779", 281473503),
 			Common::EN_ANY,
 			Common::kPlatformDOS,
-			ADGF_UNSTABLE,
+			ADGF_NO_FLAGS,
 			GUIO0()
 		},
 		GType_LAST_BOUNTY_HUNTER,
@@ -108,7 +108,7 @@ static const AlgGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("BHDEMO.LIB", "af5fbbd5e18d96225077eb6bf2cac680", 28368775),
 			Common::EN_ANY,
 			Common::kPlatformDOS,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_DEMO,
 			GUIO1(GAMEOPTION_SINGLE_SPEED_VERSION)
 		},
 		GType_LAST_BOUNTY_HUNTER,
diff --git a/engines/alg/game.cpp b/engines/alg/game.cpp
index 0c3914d3f15..ac972311079 100644
--- a/engines/alg/game.cpp
+++ b/engines/alg/game.cpp
@@ -52,18 +52,6 @@ Game::~Game() {
 		_screen->free();
 		delete _screen;
 	}
-	for (auto item : *_gun) {
-		if (item) {
-			item->free();
-			delete item;
-		}
-	}
-	for (auto item : *_numbers) {
-		if (item) {
-			item->free();
-			delete item;
-		}
-	}
 }
 
 void Game::init() {
@@ -189,10 +177,7 @@ bool Game::fired(Common::Point *point) {
 
 Rect *Game::checkZone(Zone *zone, Common::Point *point) {
 	for (auto &rect : zone->_rects) {
-		if (point->x >= rect->left &&
-			point->x <= rect->right &&
-			point->y >= rect->top &&
-			point->y <= rect->bottom) {
+		if (rect->contains(*point)) {
 			return rect;
 		}
 	}
diff --git a/engines/alg/game.h b/engines/alg/game.h
index b18346b8a6b..1069d314445 100644
--- a/engines/alg/game.h
+++ b/engines/alg/game.h
@@ -59,8 +59,6 @@ protected:
 
 	Graphics::Surface *_background;
 	Graphics::Surface *_screen;
-	Common::Array<Graphics::Surface *> *_gun;
-	Common::Array<Graphics::Surface *> *_numbers;
 
 	Audio::SoundHandle _sfxAudioHandle;
 
@@ -80,13 +78,13 @@ protected:
 	Audio::SeekableAudioStream *loadSoundFile(const Common::Path &path);
 	void playSound(Audio::SeekableAudioStream *stream);
 	bool loadScene(Scene *scene);
-	void updateScreen();
+	virtual void updateScreen();
 	uint32 getMsTime();
 	bool fired(Common::Point *point);
 	Rect *checkZone(Zone *zone, Common::Point *point);
 	uint32 getFrame(Scene *scene);
 	int8 skipToNewScene(Scene *scene);
-	void debug_drawZoneRects();
+	virtual void debug_drawZoneRects();
 
 	// Script functions: Zone
 	void zoneGlobalHit(Common::Point *point);
diff --git a/engines/alg/logic/game_bountyhunter.cpp b/engines/alg/logic/game_bountyhunter.cpp
index ecd56e19fa6..27238dcd268 100644
--- a/engines/alg/logic/game_bountyhunter.cpp
+++ b/engines/alg/logic/game_bountyhunter.cpp
@@ -24,6 +24,7 @@
 #include "common/savefile.h"
 #include "common/system.h"
 
+#include "graphics/paletteman.h"
 #include "graphics/cursorman.h"
 
 #include "alg/graphics.h"
@@ -44,6 +45,10 @@ GameBountyHunter::~GameBountyHunter() {
 		_emptyIcon->free();
 		delete _emptyIcon;
 	}
+	if (_shellIcon) {
+		_shellIcon->free();
+		delete _shellIcon;
+	}
 	if (_liveIcon) {
 		_liveIcon->free();
 		delete _liveIcon;
@@ -52,10 +57,6 @@ GameBountyHunter::~GameBountyHunter() {
 		_deadIcon->free();
 		delete _deadIcon;
 	}
-	if (_bulletholeIcon) {
-		_bulletholeIcon->free();
-		delete _bulletholeIcon;
-	}
 	if (_playersIcon1) {
 		_playersIcon1->free();
 		delete _playersIcon1;
@@ -76,11 +77,23 @@ GameBountyHunter::~GameBountyHunter() {
 		_textBlackBarIcon->free();
 		delete _textBlackBarIcon;
 	}
+	for (auto item : *_gun) {
+		item->free();
+		delete item;
+	}
+	for (auto item : *_numbers) {
+		item->free();
+		delete item;
+	}
 	for (auto item : *_bagsIcons) {
 		item->free();
 		delete item;
 	}
-	for (auto item : *_shotgun) {
+	for (auto item : *_bulletHoleIcon) {
+		item->free();
+		delete item;
+	}
+	for (auto item : *_shotgunHoleIcon) {
 		item->free();
 		delete item;
 	}
@@ -111,7 +124,7 @@ void GameBountyHunter::init() {
 	verifyScriptFunctions();
 
 	_menuZone = new Zone("MainMenu", "GLOBALHIT");
-	_menuZone->addRect(0x0C, 0xAA, 0x38, 0xC7, nullptr, 0, "SHOTMENU", "0");
+	_menuZone->addRect(0x0A, 0xAE, 0x3C, 0xC7, nullptr, 0, "SHOTMENU", "0");
 
 	_subMenuZone = new Zone("SubMenu", "GLOBALHIT");
 	_subMenuZone->addRect(0, 0, 0x78, 0x3C, nullptr, 0, "STARTMENU", "0");
@@ -129,16 +142,16 @@ void GameBountyHunter::init() {
 	_skullSound = loadSoundFile("skull.8b");
 
 	_gun = AlgGraphics::loadScreenCoordAniImage("bh_gun.ani", _palette);
-	_shotgun = AlgGraphics::loadScreenCoordAniImage("bh_buck.ani", _palette);
+	_bulletHoleIcon = AlgGraphics::loadScreenCoordAniImage("bh_hole.ani", _palette);
+	_shotgunHoleIcon = AlgGraphics::loadScreenCoordAniImage("bh_buck.ani", _palette);
 	_numbers = AlgGraphics::loadAniImage("bh_num.ani", _palette);
 	auto bullets = AlgGraphics::loadAniImage("bh_ammo.ani", _palette);
 	_shotIcon = (*bullets)[0];
 	_emptyIcon = (*bullets)[1];
+	_shellIcon = (*bullets)[2];
 	auto lives = AlgGraphics::loadAniImage("bh_life.ani", _palette);
 	_liveIcon = (*lives)[0];
 	_deadIcon = (*lives)[1];
-	auto hole = AlgGraphics::loadScreenCoordAniImage("bh_hole.ani", _palette);
-	_bulletholeIcon = (*hole)[0];
 	auto players = AlgGraphics::loadAniImage("bh_plyr.ani", _palette);
 	_playersIcon1 = (*players)[0];
 	_playersIcon2 = (*players)[1];
@@ -149,6 +162,9 @@ void GameBountyHunter::init() {
 	_bagsIcons = AlgGraphics::loadScreenCoordAniImage("bh_bags.ani", _palette);
 
 	_background = AlgGraphics::loadVgaBackground("bh_menu.vga", _palette);
+	AlgGraphics::drawImage(_background, _textScoreIcon, 0x78, 0xBF);
+	AlgGraphics::drawImage(_background, _textMenuIcon, 0x0C, 0xBF);
+	displayScores(0);
 	_screen->copyRectToSurface(_background->getPixels(), _background->pitch, 0, 0, _background->w, _background->h);
 
 	moveMouse();
@@ -365,7 +381,7 @@ void GameBountyHunter::callScriptFunctionScene(SceneFuncType type, Common::Strin
 
 Common::Error GameBountyHunter::run() {
 	init();
-	newGame();
+	startMyGame();
 	_numPlayers = 1;
 	_curScene = _startScene;
 	Common::String oldscene;
@@ -390,6 +406,11 @@ Common::Error GameBountyHunter::run() {
 			if (fired(&firedCoords)) {
 				if (_lastShotTime == 0) {
 					Rect *hitGlobalRect = checkZone(_menuZone, &firedCoords);
+					if (_bagActive && _bagRect.contains(firedCoords)) {
+						if (_iconCounter < 3) {
+							_iconCounter++;
+						}
+					}
 					if (hitGlobalRect != nullptr) {
 						callScriptFunctionRectHit(hitGlobalRect->_rectHit, hitGlobalRect);
 					} else {
@@ -399,15 +420,15 @@ Common::Error GameBountyHunter::run() {
 								_lastShotTime = getMsTime();
 							}
 							displayShotFiredImage(&firedCoords);
-							playSound(_shotSound); // TODO different sound for shotgun
+							if (_playerGun[_player] == 2) {
+								playSound(_shotgunSound);
+							} else {
+								playSound(_shotSound);
+							}
 							Rect *hitRect = nullptr;
 							Zone *hitSceneZone = checkZones(scene, hitRect, &firedCoords);
 							if (hitSceneZone != nullptr) {
 								callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
-								if (_curScene == oldscene) {
-									// skip to new scene
-									callScriptFunctionScene(NXTSCN, scene->_nxtscn, scene);
-								}
 							}
 						} else {
 							playSound(_emptySound);
@@ -446,6 +467,7 @@ Common::Error GameBountyHunter::run() {
 			if (remainingMillis < 10) {
 				if (_videoDecoder->getCurrentFrame() > 0) {
 					_videoDecoder->getNextFrame();
+					moveCurrentBag();
 				}
 				remainingMillis = _nextFrameTime - getMsTime();
 				_nextFrameTime = getMsTime() + (remainingMillis > 0 ? remainingMillis : 0) + 100;
@@ -474,12 +496,54 @@ Common::Error GameBountyHunter::run() {
 	return Common::kNoError;
 }
 
-void GameBountyHunter::newGame() {
-	_playerLives[0] = _playerLives[1] = 3;
-	_playerShots[0] = _playerShots[1] = 6;
-	_playerGun[0] = _playerGun[1] = 1;
-	_playerScore[0] = _playerScore[1] = 0;
+void GameBountyHunter::updateScreen() {
+	_screen->copyRectToSurface(_background->getPixels(), _background->pitch, 0, 0, _background->w, _background->h);
+	if (!_inMenu) {
+		Graphics::Surface *frame = _videoDecoder->getVideoFrame();
+		_screen->copyRectToSurface(frame->getPixels(), frame->pitch, _videoPosX, _videoPosY, frame->w, frame->h);
+	}
+	debug_drawZoneRects();
+	displayCurrentBag();
+	if (_paletteDirty || _videoDecoder->isPaletteDirty()) {
+		g_system->getPaletteManager()->setPalette(_palette, 0, 256);
+		_paletteDirty = false;
+	}
+	g_system->copyRectToScreen(_screen->getPixels(), _screen->pitch, 0, 0, _screen->w, _screen->h);
+	g_system->updateScreen();
+}
+
+uint16 GameBountyHunter::startMyGame() {
+	uint16 startScene = 0;
+	if (_restartScene == 0) {
+		_playerGun[0] = _playerGun[1] = 1;
+		_playerShots[0] = _playerShots[1] = 6;
+		_playerLives[0] = _playerLives[1] = 3;
+		_playerScore[0] = _playerScore[1] = 0;
+		initGameStatus();
+		if (_vm->isDemo()) {
+			startScene = 69; // TODO fix
+		} else {
+			startScene = 69;
+		}
+	} else {
+		startScene = _restartScene;
+		_restartScene = 0;
+	}
+	return startScene;
+}
+
+void GameBountyHunter::initGameStatus() {
+	_currentLevel = 0;
+	_continuesUsed = 0;
+	_restartScene = 0;
+	_levelDoneMask = 0;
+	_numLevelsDone = 0;
+	_numSubLevelsDone = 0;
+	_difficulty = 0;
+
 	_currentSubLevelSceneId = 0x017B;
+	iconSetup();
+	iconReset(); // not in original!
 }
 
 void GameBountyHunter::doMenu() {
@@ -517,11 +581,8 @@ void GameBountyHunter::updateCursor() {
 void GameBountyHunter::updateMouse() {
 	if (_oldWhichGun != _whichGun) {
 		Graphics::Surface *cursor = (*_gun)[_whichGun];
-		if (_playerGun[0] == 2 && _whichGun < 2) {
-			cursor = (*_shotgun)[_whichGun];
-		}
-		uint16 hotspotX = (cursor->w / 2) + 8;
-		uint16 hotspotY = (cursor->h / 2) + 8;
+		uint16 hotspotX = (cursor->w / 2) + 7;
+		uint16 hotspotY = (cursor->h / 2) + 6;
 		if (debugChannelSet(1, Alg::kAlgDebugGraphics)) {
 			cursor->drawLine(0, hotspotY, cursor->w, hotspotY, 1);
 			cursor->drawLine(hotspotX, 0, hotspotX, cursor->h, 1);
@@ -550,68 +611,75 @@ void GameBountyHunter::moveMouse() {
 			g_system->warpMouse(x, y);
 		}
 		*/
-		if (_mousePos.y >= 0xA3 && _mousePos.x >= 0xF0) {
-			_whichGun = 1; // holster
-		} else if (_mousePos.y >= 0xA3 && _mousePos.x <= 0x43) {
+		if (_mousePos.y >= 0xAE && _mousePos.x >= 0xF0) {
+			_whichGun = 4; // holster
+		} else if (_mousePos.y >= 0xAE && _mousePos.x <= 0x3C) {
 			_whichGun = 2; // menu button cursor
 		} else {
-			_whichGun = 0; // regular gun
+			_whichGun = 0; // player 1 gun cursor is 0, player 2 is 1
 		}
 	}
 	updateMouse();
 }
 
 void GameBountyHunter::displayLivesLeft(uint8 player) {
-	if (_lives[player] == _oldLives[player]) {
+	if (_playerLives[player] == _oldLives[player]) {
 		return;
 	}
+    if (_playerLives[player] < 0) {
+        _playerLives[player] = 0;
+    }
 	int posX = player == 0 ? 0xD7 : 0x50;
 	for (int i = 0; i < 3; i++) {
-		AlgGraphics::drawImage(_screen, _deadIcon, posX, 0xBE);
+		AlgGraphics::drawImage(_background, _deadIcon, posX, 0xBE);
 		posX += 10;
 	}
 	posX = player == 0 ? 0xD7 : 0x50;
-	for (int i = 0; i < _lives[player]; i++) {
-		AlgGraphics::drawImage(_screen, _liveIcon, posX, 0xBE);
+	for (int i = 0; i < _playerLives[player]; i++) {
+		AlgGraphics::drawImage(_background, _liveIcon, posX, 0xBE);
 		posX += 10;
 	}
-	_oldLives[player] = _lives[player];
+	_oldLives[player] = _playerLives[player];
 }
 
 void GameBountyHunter::displayScores(uint8 player) {
-	if (_score[player] == _oldScore[player]) {
+	if (_playerScore[player] == _oldScore[player]) {
 		return;
 	}
-	Common::String scoreString = Common::String::format("%05d", _score[player]);
+	Common::String scoreString = Common::String::format("%05d", _playerScore[player]);
 	int posX = player == 0 ? 0xA5 : 0x78;
 	for (int i = 0; i < 5; i++) {
 		uint8 digit = scoreString[i] - '0';
-		uint8 digitOffset = player == 1 ? digit : digit + 10;
-		AlgGraphics::drawImage(_screen, (*_numbers)[digitOffset], posX, 0xBF);
+		uint8 digitOffset = player == 0 ? digit : digit + 10;
+		AlgGraphics::drawImage(_background, (*_numbers)[digitOffset], posX, 0xBF);
 		posX += 5;
 	}
-	_oldScore[player] = _score[player];
+	_oldScore[player] = _playerScore[player];
 }
 
 void GameBountyHunter::displayShotsLeft(uint8 player) {
-	if (_shots[player] == _oldShots[player]) {
+	if (_playerShots[player] == _oldShots[player]) {
 		return;
 	}
 	uint16 posX = player == 0 ? 0xF8 : 0x0C;
 	for (int i = 0; i < 10; i++) {
-		AlgGraphics::drawImage(_screen, _emptyIcon, posX, 0xBF);
+		AlgGraphics::drawImage(_background, _emptyIcon, posX, 0xBF);
 		posX += 5;
 	}
 	posX = player == 0 ? 0xF8 : 0x0C;
-	for (int i = 0; i < _shots[player]; i++) {
-		AlgGraphics::drawImage(_screen, _shotIcon, posX, 0xBF);
+	for (int i = 0; i < _playerShots[player]; i++) {
+		if (_playerGun[_player] == 2) {
+			AlgGraphics::drawImage(_background, _shellIcon, posX, 0xBF);
+		} else {
+			AlgGraphics::drawImage(_background, _shotIcon, posX, 0xBF);
+		}
 		posX += 5;
 	}
-	_oldShots[player] = _shots[player];
+	_oldShots[player] = _playerShots[player];
 }
 
 bool GameBountyHunter::weaponDown() {
-	if (_rightDown && _mousePos.y >= 0xAA && _mousePos.x >= 0x113) {
+	if (_rightDown && _mousePos.y >= 0xAE && _mousePos.x >= 0xF0) {
 		return true;
 	}
 	return false;
@@ -692,10 +760,11 @@ Zone *GameBountyHunter::checkZones(Scene *scene, Rect *&hitRect, Common::Point *
 Rect *GameBountyHunter::checkZone(Zone *zone, Common::Point *point) {
 	for (auto &rect : zone->_rects) {
 		Common::Rect interpolated = rect->getInterpolatedRect(zone->_startFrame, zone->_endFrame, _currentFrame);
-		if (point->x >= interpolated.left &&
-			point->x <= interpolated.right &&
-			point->y >= interpolated.top &&
-			point->y <= interpolated.bottom) {
+		if (_playerGun[_player] == 2) {
+			// shotgun, hit area is enlarged
+			interpolated.grow(15);
+		}
+		if (interpolated.contains(*point)) {
 			return rect;
 		}
 	}
@@ -712,7 +781,11 @@ void GameBountyHunter::displayShotFiredImage(Common::Point *point) {
 		int32 targetX = point->x - _videoPosX;
 		int32 targetY = point->y - _videoPosY;
 		if (targetX > 0 && targetY > 0) {
-			AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
+			if (_playerGun[_player] == 2) {
+				AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), (*_shotgunHoleIcon)[_player], targetX - 8, targetY - 10);
+			} else {
+				AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), (*_bulletHoleIcon)[_player], targetX - 4, targetY - 4);
+			}
 		}
 	}
 }
@@ -721,12 +794,115 @@ void GameBountyHunter::enableVideoFadeIn() {
 	// TODO implement
 }
 
+void GameBountyHunter::iconBags() {
+    if (!_bagDropped) {
+        _bagActive = true;
+		_bagRect.top = _mousePos.y;
+		_bagRect.left = _mousePos.x;
+		_bagRect.bottom = _mousePos.y + 0x26;
+		_bagRect.right = _mousePos.x + 0x11;
+        _playerWhoHitBag = _player;
+        _bagDropped = true;
+    }
+}
+
+void GameBountyHunter::iconBullets() {
+    if (!_bulletsGiven) {
+        if (_playerGun[_player] == 1) {
+        	_playerShots[_player] = 12;
+        } else {
+        	_playerShots[_player] = 5;
+        }
+        _bulletsGiven = true;
+        displayShotsLeft(_player);
+    }
+}
+
+void GameBountyHunter::iconMoney() {
+    if (!_moneyGiven) {
+        _playerScore[_player] += 50;
+        _moneyGiven = 1;
+    }
+}
+
 void GameBountyHunter::iconShotgun() {
-	// TODO implement
+    if (!_shotgunGiven) {
+        _shotgunGiven = 1;
+		_playerShots[_player] = 5;
+		_playerGun[_player] = 2;
+        displayShotsLeft(_player);
+    }
 }
 
 void GameBountyHunter::iconReset() {
-	// TODO implement
+    _bulletsGiven = 0;
+    _moneyGiven = false;
+    _shotgunGiven = false;
+    _bagDropped = false;
+    _iconOffset = 0;
+    _iconCounter = 0;
+
+}
+
+void GameBountyHunter::iconSetup() {
+    uint16 usedFlags = 0;
+    _iconOrder[0] = randomUnusedInt(4, &usedFlags, -1);
+    _iconOrder[1] = randomUnusedInt(4, &usedFlags, -1);
+    _iconOrder[2] = randomUnusedInt(4, &usedFlags, -1);
+    _iconOrder[3] = randomUnusedInt(4, &usedFlags, -1);
+    _iconOffset = 0;
+    _iconCounter = 0;
+}
+
+void GameBountyHunter::iconHitGeneric(uint8 type) {
+	uint8 selected = 0;
+	for (uint8 i = 0; i < 4; i++) {
+		if(_iconOrder[i] == type) {
+			selected = i;
+			break;
+		}
+	}
+	switch (selected)
+	{
+	case 0:
+		iconBullets();
+		break;
+	case 1:
+		iconMoney();
+		break;
+	case 2:
+		iconBags();
+		break;
+	case 3:
+		iconShotgun();
+		break;
+	}
+}
+
+void GameBountyHunter::displayCurrentBag() {
+    if (_bagActive) {
+		AlgGraphics::drawImageCentered(_screen, (*_bagsIcons)[_iconOffset], _bagRect.left, _bagRect.top);
+    }
+}
+
+void GameBountyHunter::moveCurrentBag() {
+    if (_bagActive) {
+        uint8 animState = _bagAnimToggle ? 1 : 0;
+        _iconOffset = (_iconCounter * 2) + animState;
+        _bagAnimToggle = !_bagAnimToggle;
+
+        uint16 offsetY = _difficulty + 4;
+		_bagRect.top += offsetY;
+		_bagRect.bottom += offsetY;
+
+        if (_bagRect.top > 0x99) {
+            _bagActive = false;
+            _playerScore[_playerWhoHitBag] += 2 << _iconCounter;
+            displayScores(_playerWhoHitBag);
+            _iconOffset = 0;
+            _iconCounter = 0;
+        }
+    }
 }
 
 uint16 GameBountyHunter::beginLevel(uint8 levelNumber) {
@@ -771,14 +947,18 @@ uint16 GameBountyHunter::pickRandomScene(uint16 *sceneList, uint8 max) {
 		_randomMask = 0;
 		_randomPicked = -1;
 		_randomSceneCount = 0;
-		while (_randomSceneList[_randomSceneCount] != 0) {
-			_randomSceneCount++;
+		if (_randomSceneList != nullptr) {
+			while (_randomSceneList[_randomSceneCount] != 0) {
+				_randomSceneCount++;
+			}
 		}
 	}
-	uint16 count = _randomMax--;
+	int16 count = _randomMax--;
 	if (count > 0) {
 		_randomPicked = randomUnusedInt(_randomSceneCount, &_randomMask, _randomPicked);
-		return _randomSceneList[_randomPicked];
+		if (_randomSceneList != nullptr) {
+			return _randomSceneList[_randomPicked];
+		}
 	}
 	return 0;
 }
@@ -826,13 +1006,12 @@ uint16 GameBountyHunter::pickDeathScene() {
 }
 
 uint16 GameBountyHunter::timeForGunfight() {
-	uint16 picked = 0;
 	if (--_gunfightCount <= 0) {
 		int index = (_difficulty * 5) + (_numLevelsDone);
 		_gunfightCount = _gunfightCountDown[index];
-		picked = pickGunfightScene();
+		return pickGunfightScene();
 	}
-	return picked;
+	return 0;
 }
 
 void GameBountyHunter::waitingForShootout(uint32 drawFrame) {
@@ -844,31 +1023,20 @@ void GameBountyHunter::waitingForShootout(uint32 drawFrame) {
 			displayShotsLeft(i);
 		}
 	}
-	// TODO investigate & fix
 	if (_currentFrame > _firstDrawFrame) {
-		// player1 = 1;
-		// player2 = 1;
-	}
-	/*
-	if (shotsPlayer1 <= 0 && shotsPlayer2 <= 0) {
-		return false;
+		_playerGun[0] = 1;
+		_playerGun[1] = 1;
 	}
-	return true;
-	*/
-}
-
-void GameBountyHunter::doShotgunSound() {
-	playSound(_shotgunSound);
 }
 
 // Script functions: RectHit
 void GameBountyHunter::rectNewScene(Rect *rect) {
 	uint32 _weightedScore = rect->_score + (_difficulty * 10);
-	_score[_player] += _weightedScore;
+	_playerScore[_player] += _weightedScore;
 	_pointsSinceLastBonus[_player] += _weightedScore;
 	if (_pointsSinceLastBonus[_player] > 5000) {
 		_pointsSinceLastBonus[_player] = 0;
-		_lives[_player]++;
+		_playerLives[_player]++;
 	}
 	if (!rect->_scene.empty()) {
 		_curScene = rect->_scene;
@@ -915,14 +1083,8 @@ void GameBountyHunter::rectStart(Rect *rect) {
 	_inMenu = false;
 	_fired = false;
 	_gameInProgress = true;
-	_restartScene = 0;
-	if (_vm->isDemo()) {
-		debug(5, "FIXME: Demo Next Scene is 0x45?");
-		setNextScene(0x45); // TODO fix
-	} else {
-		setNextScene(0x45);
-	}
-	newGame();
+	uint16 startScene = startMyGame();
+	setNextScene(startScene);
 }
 
 void GameBountyHunter::rectExit(Rect *rect) {
@@ -935,45 +1097,39 @@ void GameBountyHunter::rectTogglePlayers(Rect *rect) {
 		AlgGraphics::drawImage(_background, _playersIcon2, 0xCE, 0x95);
 		AlgGraphics::drawImage(_background, _textBlackBarIcon, 0x78, 0xBF);
 		AlgGraphics::drawImage(_background, _textBlackBarIcon, 0x0C, 0xBF);
+		// force rendering
+		_oldShots[1] = 0;
+		_oldLives[1] = 0;
 		displayShotsLeft(1);
 		displayLivesLeft(1);
 	} else {
 		_numPlayers = 1;
 		AlgGraphics::drawImage(_background, _playersIcon1, 0xCE, 0x95);
+		AlgGraphics::drawImage(_background, _textBlackBarIcon, 0x50, 0xBE);
 		AlgGraphics::drawImage(_background, _textScoreIcon, 0x78, 0xBF);
 		AlgGraphics::drawImage(_background, _textMenuIcon, 0x0C, 0xBF);
-		AlgGraphics::drawImage(_background, _textBlackBarIcon, 0x50, 0xBE);
 	}
 	playSound(_skullSound);
-	_screen->copyRectToSurface(_background->getBasePtr(_videoPosX, _videoPosY), _background->pitch, _videoPosX, _videoPosY, _videoDecoder->getWidth(), _videoDecoder->getHeight());
 }
 
 void GameBountyHunter::rectHitIconJug(Rect *rect) {
-	// TODO fix
-	// Icon.funcs[1](param);
-	// PlaySound(4);
-	// Icon.hitCount++;
+	iconHitGeneric(1);
+	playSound(_skullSound);
 }
 
 void GameBountyHunter::rectHitIconLantern(Rect *rect) {
-	// TODO fix
-	// Icon.funcs[3](param);
-	// PlaySound(4);
-	// Icon.hitCount++;
+	iconHitGeneric(3);
+	playSound(_skullSound);
 }
 
 void GameBountyHunter::rectHitIconSkull(Rect *rect) {
-	// TODO fix
-	// Icon.funcs[0](param);
-	// PlaySound(4);
-	// Icon.hitCount++;
+	iconHitGeneric(0);
+	playSound(_skullSound);
 }
 
 void GameBountyHunter::rectHitIconWheel(Rect *rect) {
-	// TODO fix
-	// Icon.funcs[2](param);
-	// PlaySound(4);
-	// Icon.hitCount++;
+	iconHitGeneric(2);
+	playSound(_skullSound);
 }
 
 void GameBountyHunter::rectHitSelectHarry(Rect *rect) {
@@ -1013,12 +1169,10 @@ void GameBountyHunter::rectHitGiveShotgun(Rect *rect) {
 }
 
 void GameBountyHunter::rectHitKill3(Rect *rect) {
-	_count++;
-	if (_count == 3) {
-		_count = 0;
+	_kill3Count++;
+	if (_kill3Count == 3) {
+		_kill3Count = 0;
 		rectNewScene(rect);
-		// TODO verify
-		// _RHONewScene(param1, param2);
 	}
 }
 
@@ -1072,12 +1226,11 @@ void GameBountyHunter::sceneIsoShootout(Scene *scene) {
 
 void GameBountyHunter::sceneIsoGivemoney(Scene *scene) {
 	const int moneyFrames[] = {0x1E8F, 0x3BB4, 0x7814, 0xA287};
-	const int woundBits[] = {2, 4, 8, 0x10};
+	const int woundBits[] = {2, 4, 8, 16};
 	for (int i = 0; i < _numPlayers; i++) {
 		if (_currentLevel <= 3) {
 			uint32 moneyFrame = moneyFrames[_currentLevel];
-			// TODO investigate
-			if (moneyFrame == _currentFrame && !_given) {
+			if (moneyFrame == _currentFrame && !_moneyGiven) {
 				if (_wounded) {
 					_mainWounds |= woundBits[_currentLevel];
 					int bonus = (2 ^ _numLevelsDone) * 200;
@@ -1087,9 +1240,9 @@ void GameBountyHunter::sceneIsoGivemoney(Scene *scene) {
 					_playerScore[i] += bonus;
 				}
 				_wounded = false;
-				_given = true;
+				_moneyGiven = true;
 			} else if (moneyFrame != _currentFrame) {
-				_given = false;
+				_moneyGiven = false;
 			}
 		}
 		displayScores(i);
@@ -1099,19 +1252,21 @@ void GameBountyHunter::sceneIsoGivemoney(Scene *scene) {
 // Script functions: Scene NxtScn
 void GameBountyHunter::sceneNxtscnLoseALife(Scene *scene) {
 	uint16 picked = 0;
-	int deadPlayerCount = 0;
+	uint8 deadCount = 0;
 	(void)scene;
-	for (uint i = 0; i < _numPlayers; i++) {
-		_playerLives[i]--;
+	for (int i = 0; i < _numPlayers; i++) {
+		if (!_debug_godMode) {
+			_playerLives[i]--;
+		}
 		displayLivesLeft(i);
 		if (_playerLives[i] <= 0) {
 			_playerScore[i] = (_playerScore[i] * 6) / 10;
-			deadPlayerCount++;
+			deadCount++;
 		}
 	}
-	if (deadPlayerCount == 1 && _numPlayers == 2) {
+	if (deadCount == 1 && _numPlayers == 2) {
 		picked = _onePlayerOfTwoDead[_numSubLevelsDone & 1];
-	} else if (deadPlayerCount > 0) {
+	} else if (deadCount > 0) {
 		picked = _allPlayersDead;
 	} else {
 		picked = pickDeathScene();
@@ -1175,7 +1330,7 @@ void GameBountyHunter::sceneNxtscnGotoLevelSelect(Scene *scene) {
 }
 
 void GameBountyHunter::sceneNxtscnContinueRandom(Scene *scene) {
-	uint16 picked = pickRandomScene(0, 0);
+	uint16 picked = pickRandomScene(nullptr, 0);
 	if (picked == 0) {
 		sceneNxtscnNextSubLevel(scene);
 	} else {
@@ -1235,21 +1390,19 @@ void GameBountyHunter::sceneNxtscnInitRandomKid2(Scene *scene) {
 void GameBountyHunter::sceneNxtscnNextSubLevel(Scene *scene) {
 	iconReset();
 	_numSubLevelsDone++;
-	int index = (_currentLevel * 24) + (_numLevelsDone * 6) + _numSubLevelsDone;
-	uint8 subLevel = _subLevelOrder[index];
+	int subLevelIndex = (_currentLevel * 24) + (_numLevelsDone * 6) + _numSubLevelsDone;
+	uint8 subLevel = _subLevelOrder[subLevelIndex];
 	uint16 sceneIndex = (_currentLevel * 5) + subLevel;
 	uint16 picked = _subLevelSceneIds[sceneIndex];
 	_currentSubLevelSceneId = picked;
 	uint16 gunfightScene = timeForGunfight();
 	if (gunfightScene != 0) {
-		setNextScene(gunfightScene);
+		picked = gunfightScene;
 	}
-	if (subLevel == 2) {
+	else if (subLevel != 2) {
 		if (_currentLevel == 0) {
-			setNextScene(picked);
-			return;
+			picked = _clueLevels[_currentLevel];
 		}
-		picked = _clueLevels[_currentLevel];
 	}
 	setNextScene(picked);
 }
@@ -1343,7 +1496,9 @@ void GameBountyHunter::sceneNxtscnDiedRefed(Scene *scene) {
 	uint8 deadCount = 0;
 	(void)scene;
 	for (uint i = 0; i < _numPlayers; i++) {
-		_playerLives[i]--;
+		if (!_debug_godMode) {
+			_playerLives[i]--;
+		}
 		displayLivesLeft(i);
 		if (_playerLives[i] <= 0) {
 			deadCount++;
@@ -1388,10 +1543,12 @@ void GameBountyHunter::sceneDefaultWepdwn(Scene *scene) {
 
 // Script functions: ScnScr
 void GameBountyHunter::sceneDefaultScore(Scene *scene) {
-	if (scene->_scnscrParam > 0) {
-		_score[0] += scene->_scnscrParam;
+	uint16 score = scene->_scnscrParam + (_difficulty * 4);
+	if (score > 0) {
+		for (uint8 i = 0; i < _numPlayers; i++) {
+			_playerScore[i] += score;
+		}
 	}
-	// TODO verify, nothing for player2?
 }
 
 // Debug methods
diff --git a/engines/alg/logic/game_bountyhunter.h b/engines/alg/logic/game_bountyhunter.h
index fe9f7680f44..cbd1c79a2de 100644
--- a/engines/alg/logic/game_bountyhunter.h
+++ b/engines/alg/logic/game_bountyhunter.h
@@ -78,16 +78,19 @@ private:
 	// images
 	Graphics::Surface *_shotIcon;
 	Graphics::Surface *_emptyIcon;
+	Graphics::Surface *_shellIcon;
 	Graphics::Surface *_liveIcon;
 	Graphics::Surface *_deadIcon;
-	Graphics::Surface *_bulletholeIcon;
 	Graphics::Surface *_playersIcon1;
 	Graphics::Surface *_playersIcon2;
 	Graphics::Surface *_textScoreIcon;
 	Graphics::Surface *_textMenuIcon;
 	Graphics::Surface *_textBlackBarIcon;
+	Common::Array<Graphics::Surface *> *_gun;
+	Common::Array<Graphics::Surface *> *_numbers;
 	Common::Array<Graphics::Surface *> *_bagsIcons;
-	Common::Array<Graphics::Surface *> *_shotgun;
+	Common::Array<Graphics::Surface *> *_bulletHoleIcon;
+	Common::Array<Graphics::Surface *> *_shotgunHoleIcon;
 
 	// sounds
 	Audio::SeekableAudioStream *_saveSound = nullptr;
@@ -124,49 +127,43 @@ private:
 
 	// const uint16 _firstSceneInScenario[4] = {0x04, 0x36, 0x36, 0x66};
 	const uint16 _moneyScenes[4] = {0x017D, 0x013C, 0xC3, 0x69};
-	const uint16 _gunfightScenarios[18] = {0x0116, 0x0118, 0x011B, 0x011D, 0x011F, 0x0121, 0x0123, 0x0125, 0x0127,
-										   0x0129, 0x012B, 0x012D, 0x012F, 0x0131, 0x0133, 0x0135, 0x0137, 0x0139};
+	const uint16 _gunfightScenarios[19] = {0x0116, 0x0118, 0x011B, 0x011D, 0x011F, 0x0121, 0x0123, 0x0125, 0x0127,
+										   0x0129, 0x012B, 0x012D, 0x012F, 0x0131, 0x0133, 0x0135, 0x0137, 0x0139, 0};
 	const uint16 _innocentScenarios[5] = {0x0110, 0x010F, 0x010C, 0x010B, 0};
 	const uint16 _deathScenarios[9] = {0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0};
 	const uint16 _onePlayerOfTwoDead[2] = {0x0109, 0x010A};
 	const uint16 _allPlayersDead = 0x108;
 
 	// gamestate
+	uint8 _continuesUsed = 0;
+	uint8 _currentLevel = 0;
+	uint16 _currentSubLevelSceneId = 0;
 	uint8 _difficulty = 0;
 	uint8 _oldDifficulty = 1;
+	uint32 _firstDrawFrame = 0;
+	int8 _gunfightCount = 0;
+	uint8 _kill3Count = 0;
 	uint32 _lastShotTime = 0;
-	int8 _lives[2] = {0, 0};
-	int8 _oldLives[2] = {0, 0};
-	int32 _score[2] = {0, 0};
-	int32 _oldScore[2] = {-1, -1};
-	uint32 _pointsSinceLastBonus[2] = {0, 0};
-	uint16 _shots[2] = {0, 0};
-	uint8 _oldShots[2] = {0, 0};
-	uint8 _whichGun = 0;
-	uint8 _oldWhichGun = 0xFF;
-	// TODO verify, whichGun for player2?
-
-	uint16 _restartScene = 0;
+	uint8 _levelDoneMask = 0;
+	uint16 _mainWounds = 0;
+	uint8 _numLevelsDone = 0;
+	uint8 _numSubLevelsDone = 0;
 	uint8 _numPlayers = 1;
 	uint8 _player = 0;
-	uint8 _playerLives[2] = {0, 0};
+	int8 _playerLives[2] = {3, 3};
+	int8 _oldLives[2] = {0, 0};
 	uint8 _playerGun[2] = {1, 1};
-	uint8 _playerShots[2] = {0, 0};
+	uint8 _playerShots[2] = {6, 6};
+	uint8 _oldShots[2] = {0, 0};
 	uint32 _playerScore[2] = {0, 0};
+	uint32 _oldScore[2] = {1, 1};
+	uint32 _pointsSinceLastBonus[2] = {0, 0};
+	uint16 _restartScene = 0;
+	uint8 _whichGun = 0;
+	uint8 _oldWhichGun = 0xFF;
+	bool _wounded = false;
 
-	uint8 _currentLevel = 0;
-	uint16 _currentSubLevelSceneId = 0;
-	uint8 _numLevelsDone = 0;
-	uint8 _levelDoneMask = 0;
-	uint8 _numSubLevelsDone = 0;
-
-	// TODO remove?
-	// uint16 _usedScenes = 0;
-	// int16 _lastPick = -1;
-	// int16 _initted = 0;
-	// int16 _sceneCount = 0;
-
-	uint16 *_randomSceneList;
+	uint16 *_randomSceneList = nullptr;
 	uint8 _randomMax = 0;
 	uint16 _randomMask = 0;
 	int16 _randomPicked = 0;
@@ -184,18 +181,25 @@ private:
 	int16 _deathPicked = 0;
 	uint8 _deathSceneCount = 0;
 
-	uint8 _continuesUsed = 0;
-	bool _wounded = false;
-	uint16 _mainWounds = 0;
-	int8 _gunfightCount = 0;
-	bool _given = false;
-	uint32 _firstDrawFrame = 0;
-	uint8 _count = 0;
+	uint8 _iconOrder[4] = {0, 0, 0, 0};
+	uint32 _iconOffset = 0;
+	uint32 _iconCounter = 0;
+	bool _bagAnimToggle = false;
+	Rect _bagRect;
+	bool _bagActive = false;
+	uint8 _playerWhoHitBag = 0;
+
+	bool _bulletsGiven = false;
+	bool _moneyGiven = false;
+	bool _shotgunGiven = false;
+	bool _bagDropped = false;
 
 	Common::String _subScene;
 
 	// base functions
-	void newGame();
+	void updateScreen() override;
+	uint16 startMyGame();
+	void initGameStatus();
 	void doMenu();
 	void updateCursor();
 	void updateMouse();
@@ -213,8 +217,15 @@ private:
 	void setNextScene(uint16 sceneId);
 	void displayShotFiredImage(Common::Point *point);
 	void enableVideoFadeIn();
+	void iconBags();
+	void iconBullets();
+	void iconMoney();
 	void iconShotgun();
 	void iconReset();
+	void iconSetup();
+	void iconHitGeneric(uint8 type);
+	void displayCurrentBag();
+	void moveCurrentBag();
 	uint16 beginLevel(uint8 levelNumber);
 	uint16 randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude);
 	uint16 pickRandomScene(uint16 *sceneList, uint8 max);
@@ -223,7 +234,6 @@ private:
 	uint16 pickDeathScene();
 	uint16 timeForGunfight();
 	void waitingForShootout(uint32 drawFrame);
-	void doShotgunSound();
 
 	// Script functions: RectHit
 	void rectNewScene(Rect *rect);
@@ -299,7 +309,7 @@ private:
 	void sceneDefaultScore(Scene *scene);
 
 	// debug methods
-	void debug_drawZoneRects();
+	void debug_drawZoneRects() override;
 };
 
 class DebuggerBountyHunter : public GUI::Debugger {
diff --git a/engines/alg/logic/game_crimepatrol.cpp b/engines/alg/logic/game_crimepatrol.cpp
index 514260b63e3..20c106832d3 100644
--- a/engines/alg/logic/game_crimepatrol.cpp
+++ b/engines/alg/logic/game_crimepatrol.cpp
@@ -60,6 +60,14 @@ GameCrimePatrol::~GameCrimePatrol() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	for (auto item : *_gun) {
+		item->free();
+		delete item;
+	}
+	for (auto item : *_numbers) {
+		item->free();
+		delete item;
+	}
 	delete _saveSound;
 	delete _loadSound;
 	delete _skullSound;
diff --git a/engines/alg/logic/game_crimepatrol.h b/engines/alg/logic/game_crimepatrol.h
index ac4d587ecec..628a181fd60 100644
--- a/engines/alg/logic/game_crimepatrol.h
+++ b/engines/alg/logic/game_crimepatrol.h
@@ -80,6 +80,8 @@ private:
 	Graphics::Surface *_deadIcon;
 	Graphics::Surface *_difficultyIcon;
 	Graphics::Surface *_bulletholeIcon;
+	Common::Array<Graphics::Surface *> *_gun;
+	Common::Array<Graphics::Surface *> *_numbers;
 
 	// sounds
 	Audio::SeekableAudioStream *_saveSound = nullptr;
diff --git a/engines/alg/logic/game_drugwars.cpp b/engines/alg/logic/game_drugwars.cpp
index b60e3a8a861..87595d136ae 100644
--- a/engines/alg/logic/game_drugwars.cpp
+++ b/engines/alg/logic/game_drugwars.cpp
@@ -60,6 +60,14 @@ GameDrugWars::~GameDrugWars() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	for (auto item : *_gun) {
+		item->free();
+		delete item;
+	}
+	for (auto item : *_numbers) {
+		item->free();
+		delete item;
+	}
 	delete _saveSound;
 	delete _loadSound;
 	delete _skullSound;
diff --git a/engines/alg/logic/game_drugwars.h b/engines/alg/logic/game_drugwars.h
index 998bbc86238..da67b8760b9 100644
--- a/engines/alg/logic/game_drugwars.h
+++ b/engines/alg/logic/game_drugwars.h
@@ -80,6 +80,8 @@ private:
 	Graphics::Surface *_deadIcon;
 	Graphics::Surface *_difficultyIcon;
 	Graphics::Surface *_bulletholeIcon;
+	Common::Array<Graphics::Surface *> *_gun;
+	Common::Array<Graphics::Surface *> *_numbers;
 
 	// sounds
 	Audio::SeekableAudioStream *_saveSound = nullptr;
diff --git a/engines/alg/logic/game_johnnyrock.cpp b/engines/alg/logic/game_johnnyrock.cpp
index 6acdc8041d5..0cf0903543c 100644
--- a/engines/alg/logic/game_johnnyrock.cpp
+++ b/engines/alg/logic/game_johnnyrock.cpp
@@ -49,6 +49,14 @@ GameJohnnyRock::~GameJohnnyRock() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	for (auto item : *_gun) {
+		item->free();
+		delete item;
+	}
+	for (auto item : *_numbers) {
+		item->free();
+		delete item;
+	}
 	delete _saveSound;
 	delete _loadSound;
 	delete _moneySound;
diff --git a/engines/alg/logic/game_johnnyrock.h b/engines/alg/logic/game_johnnyrock.h
index 614af5a2af3..3a92ece666f 100644
--- a/engines/alg/logic/game_johnnyrock.h
+++ b/engines/alg/logic/game_johnnyrock.h
@@ -83,6 +83,8 @@ private:
 	Common::Array<Graphics::Surface *> *_difficultyIcon;
 	Graphics::Surface *_levelIcon;
 	Graphics::Surface *_bulletholeIcon;
+	Common::Array<Graphics::Surface *> *_gun;
+	Common::Array<Graphics::Surface *> *_numbers;
 
 	// sounds
 	Audio::SeekableAudioStream *_saveSound = nullptr;
diff --git a/engines/alg/logic/game_maddog.cpp b/engines/alg/logic/game_maddog.cpp
index 09121246305..ef0723c7b44 100644
--- a/engines/alg/logic/game_maddog.cpp
+++ b/engines/alg/logic/game_maddog.cpp
@@ -69,6 +69,14 @@ GameMaddog::~GameMaddog() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	for (auto item : *_gun) {
+		item->free();
+		delete item;
+	}
+	for (auto item : *_numbers) {
+		item->free();
+		delete item;
+	}
 	delete _saveSound;
 	delete _loadSound;
 	delete _easySound;
diff --git a/engines/alg/logic/game_maddog.h b/engines/alg/logic/game_maddog.h
index 6f9a1fe38e8..5a1c87435c7 100644
--- a/engines/alg/logic/game_maddog.h
+++ b/engines/alg/logic/game_maddog.h
@@ -88,6 +88,8 @@ private:
 	Graphics::Surface *_drawIcon;
 	Graphics::Surface *_knifeIcon;
 	Graphics::Surface *_bulletholeIcon;
+	Common::Array<Graphics::Surface *> *_gun;
+	Common::Array<Graphics::Surface *> *_numbers;
 
 	// sounds
 	Audio::SeekableAudioStream *_saveSound = nullptr;
diff --git a/engines/alg/logic/game_maddog2.cpp b/engines/alg/logic/game_maddog2.cpp
index 78c0cbf46fa..5d3eed11aed 100644
--- a/engines/alg/logic/game_maddog2.cpp
+++ b/engines/alg/logic/game_maddog2.cpp
@@ -69,6 +69,14 @@ GameMaddog2::~GameMaddog2() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	for (auto item : *_gun) {
+		item->free();
+		delete item;
+	}
+	for (auto item : *_numbers) {
+		item->free();
+		delete item;
+	}
 	delete _saveSound;
 	delete _loadSound;
 	delete _skullSound;
diff --git a/engines/alg/logic/game_maddog2.h b/engines/alg/logic/game_maddog2.h
index b099f5735c9..4113347cb7c 100644
--- a/engines/alg/logic/game_maddog2.h
+++ b/engines/alg/logic/game_maddog2.h
@@ -88,6 +88,8 @@ private:
 	Graphics::Surface *_drawIcon;
 	Graphics::Surface *_knifeIcon;
 	Graphics::Surface *_bulletholeIcon;
+	Common::Array<Graphics::Surface *> *_gun;
+	Common::Array<Graphics::Surface *> *_numbers;
 
 	// sounds
 	Audio::SeekableAudioStream *_saveSound = nullptr;
diff --git a/engines/alg/logic/game_spacepirates.cpp b/engines/alg/logic/game_spacepirates.cpp
index 1e99f82c0d1..6c50e2a7b12 100644
--- a/engines/alg/logic/game_spacepirates.cpp
+++ b/engines/alg/logic/game_spacepirates.cpp
@@ -64,6 +64,14 @@ GameSpacePirates::~GameSpacePirates() {
 		_bulletholeIcon->free();
 		delete _bulletholeIcon;
 	}
+	for (auto item : *_gun) {
+		item->free();
+		delete item;
+	}
+	for (auto item : *_numbers) {
+		item->free();
+		delete item;
+	}
 	delete _saveSound;
 	delete _loadSound;
 	delete _difficultySound;
diff --git a/engines/alg/logic/game_spacepirates.h b/engines/alg/logic/game_spacepirates.h
index bdd2a0625e1..054cb9759e9 100644
--- a/engines/alg/logic/game_spacepirates.h
+++ b/engines/alg/logic/game_spacepirates.h
@@ -85,6 +85,8 @@ private:
 	Graphics::Surface *_liveIcon3;
 	Graphics::Surface *_difficultyIcon;
 	Graphics::Surface *_bulletholeIcon;
+	Common::Array<Graphics::Surface *> *_gun;
+	Common::Array<Graphics::Surface *> *_numbers;
 
 	// sounds
 	Audio::SeekableAudioStream *_saveSound = nullptr;
diff --git a/engines/alg/scene.cpp b/engines/alg/scene.cpp
index 1bb677a4d25..3e447c7180b 100644
--- a/engines/alg/scene.cpp
+++ b/engines/alg/scene.cpp
@@ -248,37 +248,49 @@ void SceneInfo::parseZone(const Common::String &zoneName, uint32 startFrame, uin
 void SceneInfo::addZonesToScenes() {
 	for (auto &scene : _scenes) {
 		if (!scene->_zonesStart.empty()) {
-			Zone *zone = nullptr;
-			do {
-				Zone *found = findZone(scene->_zonesStart);
+			Zone *zone = findZone(scene->_zonesStart);
+			while (true) {
 				if (zone) {
-					zone = found->clone();
+					zone = zone->clone();
 					zone->_difficulty = 0;
 					scene->_zones.push_back(zone);
 				}
-			} while (zone && !zone->_next.empty());
+				if (zone->_next.empty()) {
+					break;
+				} else {
+					zone = findZone(zone->_next);
+				}
+			}
 		}
-		if (!scene->_zonesStart2.empty() && scene->_zonesStart2 != scene->_zonesStart) {
-			Zone *zone = nullptr;
-			do {
-				Zone *found = findZone(scene->_zonesStart);
+		if (!scene->_zonesStart2.empty()) {
+			Zone *zone = findZone(scene->_zonesStart);
+			while (true) {
 				if (zone) {
-					zone = found->clone();
+					zone = zone->clone();
 					zone->_difficulty = 1;
 					scene->_zones.push_back(zone);
 				}
-			} while (zone && !zone->_next.empty());
+				if (zone->_next.empty()) {
+					break;
+				} else {
+					zone = findZone(zone->_next);
+				}
+			}
 		}
-		if (!scene->_zonesStart3.empty() && scene->_zonesStart3 != scene->_zonesStart2) {
-			Zone *zone = nullptr;
-			do {
-				Zone *found = findZone(scene->_zonesStart);
+		if (!scene->_zonesStart3.empty()) {
+			Zone *zone = findZone(scene->_zonesStart);
+			while (true) {
 				if (zone) {
-					zone = found->clone();
+					zone = zone->clone();
 					zone->_difficulty = 2;
 					scene->_zones.push_back(zone);
 				}
-			} while (zone && !zone->_next.empty());
+				if (zone->_next.empty()) {
+					break;
+				} else {
+					zone = findZone(zone->_next);
+				}
+			}
 		}
 	}
 }
@@ -359,6 +371,7 @@ Scene::~Scene() {
 	for (auto zone : _zones) {
 		delete zone;
 	}
+	_zones.clear();
 }
 
 Zone::Zone(const Common::String &name, const Common::String &ptrfb) {
@@ -376,6 +389,7 @@ Zone::~Zone() {
 	for (auto rect : _rects) {
 		delete rect;
 	}
+	_rects.clear();
 }
 
 void Zone::addRect(int16 left, int16 top, int16 right, int16 bottom, const Common::String scene, uint32 score, const Common::String rectHit, const Common::String unknown) {
@@ -395,7 +409,10 @@ Zone *Zone::clone() {
 	Zone *clone = new Zone(_name, _startFrame, _endFrame);
 	clone->_ptrfb = _ptrfb;
 	clone->_next = _next;
-	clone->_rects = _rects;
+	for (auto rect : _rects) {
+		Rect *cloneRect = rect->clone();
+		clone->_rects.push_back(cloneRect);
+	}
 	return clone;
 }
 
@@ -434,4 +451,19 @@ Common::Rect Rect::getInterpolatedRect(uint32 startFrame, uint32 endFrame, uint3
 	}
 }
 
+Rect *Rect::clone() {
+	Rect *clone = new Rect();
+	clone->top = top;
+	clone->left = left;
+	clone->bottom = bottom;
+	clone->right = right;
+	clone->_scene = _scene;
+	clone->_score = _score;
+	clone->_rectHit = _rectHit;
+	clone->_unknown = _unknown;
+	clone->_isMoving = _isMoving;
+	clone->_dest = _dest;
+	return clone;
+}
+
 } // End of namespace Alg
diff --git a/engines/alg/scene.h b/engines/alg/scene.h
index 5ecc8a71cb7..85a7320a7e9 100644
--- a/engines/alg/scene.h
+++ b/engines/alg/scene.h
@@ -79,6 +79,7 @@ public:
 	Common::Rect _dest;
 	void center(int16 cx, int16 cy, int16 w, int16 h);
 	Common::Rect getInterpolatedRect(uint32 startFrame, uint32 endFrame, uint32 currentFrame);
+	Rect *clone();
 };
 
 class Zone {


Commit: 8ef78e5e6d787df0f1911314a8cfaec4ac778255
    https://github.com/scummvm/scummvm/commit/8ef78e5e6d787df0f1911314a8cfaec4ac778255
Author: loki (loki at localhost)
Date: 2026-03-01T13:20:13+01:00

Commit Message:
ALG: Bugfixes for LBH and SP, refactoring

Changed paths:
    engines/alg/logic/game_bountyhunter.cpp
    engines/alg/logic/game_bountyhunter.h
    engines/alg/logic/game_crimepatrol.cpp
    engines/alg/logic/game_crimepatrol.h
    engines/alg/logic/game_drugwars.cpp
    engines/alg/logic/game_drugwars.h
    engines/alg/logic/game_johnnyrock.cpp
    engines/alg/logic/game_johnnyrock.h
    engines/alg/logic/game_maddog.cpp
    engines/alg/logic/game_maddog.h
    engines/alg/logic/game_maddog2.cpp
    engines/alg/logic/game_maddog2.h
    engines/alg/logic/game_spacepirates.cpp
    engines/alg/logic/game_spacepirates.h
    engines/alg/scene.cpp


diff --git a/engines/alg/logic/game_bountyhunter.cpp b/engines/alg/logic/game_bountyhunter.cpp
index 27238dcd268..81e8f643a44 100644
--- a/engines/alg/logic/game_bountyhunter.cpp
+++ b/engines/alg/logic/game_bountyhunter.cpp
@@ -117,7 +117,7 @@ void GameBountyHunter::init() {
 		loadLibArchive("bhds.lib");
 	}
 
-	_sceneInfo->loadScnFile("bh.scn");
+	_sceneInfo->loadScnFile(_vm->isDemo() ? "bhdemo.scn" : "bh.scn");
 	_startScene = _sceneInfo->getStartScene();
 
 	registerScriptFunctions();
@@ -218,8 +218,9 @@ void GameBountyHunter::registerScriptFunctions() {
 	PRE_OPS_FUNCTION("L1DSETUP", scenePsoDrawRct);
 	PRE_OPS_FUNCTION("L2ASETUP", scenePsoSetupNdRandom1);
 	PRE_OPS_FUNCTION("L2BSETUP", scenePsoDrawRct);
-	PRE_OPS_FUNCTION("L4A1SETUP", scenePsoDrawRct);
-	PRE_OPS_FUNCTION("L4A2SETUP", scenePsoDrawRct);
+	// i have no clue how the original calls these 2, but apparently not via NXSTSCN
+	PRE_OPS_FUNCTION("L4A1SETUP", sceneNxtscnInitRandomKid1);
+	PRE_OPS_FUNCTION("L4A2SETUP", sceneNxtscnInitRandomKid2);
 	PRE_OPS_FUNCTION("SETUPL3A", scenePsoDrawRct);
 	PRE_OPS_FUNCTION("SET3SHOT", scenePsoDrawRct);
 	PRE_OPS_FUNCTION("L3BSETUP", scenePsoDrawRct);
@@ -439,7 +440,7 @@ Common::Error GameBountyHunter::run() {
 			if (_curScene == oldscene) {
 				callScriptFunctionScene(NXTFRM, scene->_nxtfrm, scene);
 			}
-			for (int i = 0; i < _numPlayers; i++) {
+			for (uint8 i = 0; i < _numPlayers; i++) {
 				if (weaponDown()) {
 					sceneDefaultWepdwn(scene);
 				}
@@ -466,6 +467,9 @@ Common::Error GameBountyHunter::run() {
 			int32 remainingMillis = _nextFrameTime - getMsTime();
 			if (remainingMillis < 10) {
 				if (_videoDecoder->getCurrentFrame() > 0) {
+					if (_videoDecoder->isFinished()) {
+						break;
+					}
 					_videoDecoder->getNextFrame();
 					moveCurrentBag();
 				}
@@ -489,6 +493,9 @@ Common::Error GameBountyHunter::run() {
 		if (_curScene == oldscene) {
 			_curScene = scene->_next;
 		}
+		if (_curScene == "" && _vm->isDemo()) {
+			setNextScene(387);
+		}
 		if (_curScene == "") {
 			shutdown();
 		}
@@ -521,7 +528,7 @@ uint16 GameBountyHunter::startMyGame() {
 		_playerScore[0] = _playerScore[1] = 0;
 		initGameStatus();
 		if (_vm->isDemo()) {
-			startScene = 69; // TODO fix
+			startScene = 387;
 		} else {
 			startScene = 69;
 		}
@@ -541,7 +548,7 @@ void GameBountyHunter::initGameStatus() {
 	_numSubLevelsDone = 0;
 	_difficulty = 0;
 
-	_currentSubLevelSceneId = 0x017B;
+	_currentSubLevelSceneId = _selectOpponentScreen;
 	iconSetup();
 	iconReset(); // not in original!
 }
@@ -662,7 +669,7 @@ void GameBountyHunter::displayShotsLeft(uint8 player) {
 		return;
 	}
 	uint16 posX = player == 0 ? 0xF8 : 0x0C;
-	for (int i = 0; i < 10; i++) {
+	for (int i = 0; i < 12; i++) {
 		AlgGraphics::drawImage(_background, _emptyIcon, posX, 0xBF);
 		posX += 5;
 	}
@@ -692,13 +699,12 @@ bool GameBountyHunter::saveState() {
 		warning("GameBountyHunter::saveState(): Can't create file '%s', game not saved", saveFileName.c_str());
 		return false;
 	}
-	uint16 currentSceneNum = atoi(_curScene.c_str());
 	outSaveFile->writeUint32BE(MKTAG('A', 'L', 'G', 'S')); // header
 	outSaveFile->writeByte(0);                             // version, unused for now
 	outSaveFile->writeByte(_currentLevel);
 	outSaveFile->writeUint16LE(_currentSubLevelSceneId);
 	outSaveFile->writeByte(_continuesUsed);
-	outSaveFile->writeUint16LE(currentSceneNum);
+	outSaveFile->writeUint16LE(_restartScene);
 	for (int i = 0; i < 2; i++) {
 		outSaveFile->writeByte(_playerLives[i]);
 		outSaveFile->writeByte(_playerShots[i]);
@@ -707,6 +713,7 @@ bool GameBountyHunter::saveState() {
 	outSaveFile->writeByte(_difficulty);
 	outSaveFile->writeByte(_numPlayers);
 	outSaveFile->finalize();
+	_restartScene = 0;
 	delete outSaveFile;
 	return true;
 }
@@ -737,7 +744,6 @@ bool GameBountyHunter::loadState() {
 	_numPlayers = inSaveFile->readByte();
 	assert(_numPlayers <= 2);
 	delete inSaveFile;
-	_gameInProgress = true;
 	return true;
 }
 
@@ -908,8 +914,8 @@ void GameBountyHunter::moveCurrentBag() {
 uint16 GameBountyHunter::beginLevel(uint8 levelNumber) {
 	_currentLevel = levelNumber;
 	_numSubLevelsDone = 0;
-	int index = (levelNumber * 24) + (_numLevelsDone * 6) + _numSubLevelsDone;
-	uint8 subLevel = _subLevelOrder[index];
+	uint8 subLevelIndex = (levelNumber * 24) + (_numLevelsDone * 6) + _numSubLevelsDone;
+	uint8 subLevel = _subLevelOrder[subLevelIndex];
 	uint16 sceneIndex = (_currentLevel * 5) + subLevel;
 	uint16 sceneNum = _subLevelSceneIds[sceneIndex];
 	_currentSubLevelSceneId = sceneNum;
@@ -1007,7 +1013,7 @@ uint16 GameBountyHunter::pickDeathScene() {
 
 uint16 GameBountyHunter::timeForGunfight() {
 	if (--_gunfightCount <= 0) {
-		int index = (_difficulty * 5) + (_numLevelsDone);
+		int index = (_difficulty * 5) + (_numLevelsDone * 2);
 		_gunfightCount = _gunfightCountDown[index];
 		return pickGunfightScene();
 	}
@@ -1016,7 +1022,7 @@ uint16 GameBountyHunter::timeForGunfight() {
 
 void GameBountyHunter::waitingForShootout(uint32 drawFrame) {
 	if (drawFrame != 0) {
-		for (uint i = 0; i < _numPlayers; i++) {
+		for (uint8 i = 0; i < _numPlayers; i++) {
 			_firstDrawFrame = drawFrame;
 			_playerShots[i] = 0;
 			_playerGun[i] = 0;
@@ -1056,9 +1062,8 @@ void GameBountyHunter::rectSave(Rect *rect) {
 void GameBountyHunter::rectLoad(Rect *rect) {
 	if (loadState()) {
 		playSound(_loadSound);
+		_restartScene = 0;
 	}
-	setNextScene(_restartScene);
-	_restartScene = 0;
 }
 
 void GameBountyHunter::rectContinue(Rect *rect) {
@@ -1214,7 +1219,7 @@ void GameBountyHunter::scenePsoSetCurrentScene(Scene *scene) {
 	if (sceneId == 0) {
 		uint8 index = (_currentLevel * 24) + (_numLevelsDone * 6) + _numSubLevelsDone;
 		uint8 subLevel = _subLevelOrder[index];
-		uint16 picked = (_currentLevel * 10) + subLevel;
+		uint16 picked = (_currentLevel * 5) + subLevel;
 		_currentSubLevelSceneId = _subLevelSceneIds[picked];
 	}
 }
@@ -1227,7 +1232,7 @@ void GameBountyHunter::sceneIsoShootout(Scene *scene) {
 void GameBountyHunter::sceneIsoGivemoney(Scene *scene) {
 	const int moneyFrames[] = {0x1E8F, 0x3BB4, 0x7814, 0xA287};
 	const int woundBits[] = {2, 4, 8, 16};
-	for (int i = 0; i < _numPlayers; i++) {
+	for (uint8 i = 0; i < _numPlayers; i++) {
 		if (_currentLevel <= 3) {
 			uint32 moneyFrame = moneyFrames[_currentLevel];
 			if (moneyFrame == _currentFrame && !_moneyGiven) {
@@ -1254,8 +1259,8 @@ void GameBountyHunter::sceneNxtscnLoseALife(Scene *scene) {
 	uint16 picked = 0;
 	uint8 deadCount = 0;
 	(void)scene;
-	for (int i = 0; i < _numPlayers; i++) {
-		if (!_debug_godMode) {
+	for (uint8 i = 0; i < _numPlayers; i++) {
+		if (!_debug_godMode && !_vm->isDemo()) {
 			_playerLives[i]--;
 		}
 		displayLivesLeft(i);
@@ -1289,7 +1294,9 @@ void GameBountyHunter::sceneNxtscnDidNotContinue(Scene *scene) {
 
 void GameBountyHunter::sceneNxtscnKillInnocentMan(Scene *scene) {
 	uint16 picked = 0;
-	_playerLives[_player]--;
+	if (!_debug_godMode) {
+		_playerLives[_player]--;
+	}
 	if (_playerLives[_player]) {
 		picked = pickInnocentScene();
 	} else {
@@ -1307,7 +1314,7 @@ void GameBountyHunter::sceneNxtscnKillInnocentWoman(Scene *scene) {
 }
 
 void GameBountyHunter::sceneNxtscnAfterDie(Scene *scene) {
-	for (int i = 0; i < _numPlayers; i++) {
+	for (uint8 i = 0; i < _numPlayers; i++) {
 		if (_playerLives[i] <= 0) {
 			_playerLives[i] = 3;
 			displayLivesLeft(i);
@@ -1320,7 +1327,7 @@ void GameBountyHunter::sceneNxtscnGotoLevelSelect(Scene *scene) {
 	iconReset();
 	uint16 picked = 0;
 	if ((_levelDoneMask & 0x1E) != 0x1E) {
-		picked = 0x17B;
+		picked = _selectOpponentScreen;
 	} else if (!(_levelDoneMask & 0x80)) {
 		picked = 0x66;
 	} else {
@@ -1351,11 +1358,10 @@ void GameBountyHunter::sceneNxtscnInitRandomHarry2(Scene *scene) {
 
 void GameBountyHunter::sceneNxtscnInitRandomDan1(Scene *scene) {
 	uint16 picked = 0;
-	uint8 picks = _randomScenesPicks[2] + _numPlayers;
 	if (_numPlayers == 2) {
-		picked = pickRandomScene(_randomDan1TwoPlayer, picks);
+		picked = pickRandomScene(_randomDan1TwoPlayer, 5);
 	} else {
-		picked = pickRandomScene(_randomScenes[2], picks);
+		picked = pickRandomScene(_randomScenes[2], _randomScenesPicks[2]);
 	}
 	_currentSubLevelSceneId = 0x0174;
 	setNextScene(picked);
@@ -1390,7 +1396,7 @@ void GameBountyHunter::sceneNxtscnInitRandomKid2(Scene *scene) {
 void GameBountyHunter::sceneNxtscnNextSubLevel(Scene *scene) {
 	iconReset();
 	_numSubLevelsDone++;
-	int subLevelIndex = (_currentLevel * 24) + (_numLevelsDone * 6) + _numSubLevelsDone;
+	uint8 subLevelIndex = (_currentLevel * 24) + (_numLevelsDone * 6) + _numSubLevelsDone;
 	uint8 subLevel = _subLevelOrder[subLevelIndex];
 	uint16 sceneIndex = (_currentLevel * 5) + subLevel;
 	uint16 picked = _subLevelSceneIds[sceneIndex];
@@ -1399,8 +1405,8 @@ void GameBountyHunter::sceneNxtscnNextSubLevel(Scene *scene) {
 	if (gunfightScene != 0) {
 		picked = gunfightScene;
 	}
-	else if (subLevel != 2) {
-		if (_currentLevel == 0) {
+	else if (subLevel == 2) {
+		if (_currentLevel != 0) {
 			picked = _clueLevels[_currentLevel];
 		}
 	}
@@ -1409,8 +1415,8 @@ void GameBountyHunter::sceneNxtscnNextSubLevel(Scene *scene) {
 
 void GameBountyHunter::sceneNxtscnGotoBadGuy(Scene *scene) {
 	iconReset();
-	uint8 index = (_currentLevel * 24) + (_numLevelsDone * 6) + _numSubLevelsDone;
-	uint8 subLevel = _subLevelOrder[index];
+	uint8 subLevelIndex = (_currentLevel * 24) + (_numLevelsDone * 6) + _numSubLevelsDone;
+	uint8 subLevel = _subLevelOrder[subLevelIndex];
 	uint16 sceneIndex = (_currentLevel * 5) + subLevel;
 	uint16 picked = _subLevelSceneIds[sceneIndex];
 	setNextScene(picked);
@@ -1429,7 +1435,7 @@ void GameBountyHunter::sceneNxtscnAutoSelectLevel(Scene *scene) {
 }
 
 void GameBountyHunter::sceneNxtscnSelectScenario(Scene *scene) {
-	setNextScene(_currentLevel);
+	// do nothing
 }
 
 void GameBountyHunter::sceneNxtscnFinishScenario(Scene *scene) {
@@ -1443,12 +1449,12 @@ void GameBountyHunter::sceneNxtscnGameWon(Scene *scene) {
 
 void GameBountyHunter::sceneNxtscnKilledMain(Scene *scene) {
 	_wounded = false;
-	_subScene = "scene379";
+	_currentSubLevelSceneId = _selectOpponentScreen;
 }
 
 void GameBountyHunter::sceneNxtscnWoundedMain(Scene *scene) {
 	_wounded = true;
-	_subScene = "scene379";
+	_currentSubLevelSceneId = _selectOpponentScreen;
 }
 
 void GameBountyHunter::sceneNxtscnEndLevel(Scene *scene) {
@@ -1463,7 +1469,7 @@ void GameBountyHunter::sceneNxtscnEndLevel(Scene *scene) {
 		setNextScene(0x66);
 		return;
 	}
-	setNextScene(0x017B);
+	setNextScene(_selectOpponentScreen);
 }
 
 void GameBountyHunter::sceneNxtscnEndGame(Scene *scene) {
@@ -1495,8 +1501,8 @@ void GameBountyHunter::sceneNxtscnDiedRefed(Scene *scene) {
 	uint16 picked = 0;
 	uint8 deadCount = 0;
 	(void)scene;
-	for (uint i = 0; i < _numPlayers; i++) {
-		if (!_debug_godMode) {
+	for (uint8 i = 0; i < _numPlayers; i++) {
+		if (!_debug_godMode && !_vm->isDemo()) {
 			_playerLives[i]--;
 		}
 		displayLivesLeft(i);
@@ -1516,7 +1522,7 @@ void GameBountyHunter::sceneNxtscnDiedRefed(Scene *scene) {
 
 void GameBountyHunter::sceneNxtscnGiveShotgun(Scene *scene) {
 	(void)scene;
-	for (uint i = 0; i < _numPlayers; i++) {
+	for (uint8 i = 0; i < _numPlayers; i++) {
 		_playerShots[i] = 5;
 		_playerGun[i] = 2;
 		displayShotsLeft(i);
@@ -1579,8 +1585,41 @@ void GameBountyHunter::debug_drawZoneRects() {
 	}
 }
 
-void GameBountyHunter::debugWarpTo(int val) {
-	// TODO implement
+void GameBountyHunter::debug_warpTo(int val) {
+	if (_vm->isDemo()) {
+		return;
+	}
+	startMyGame();
+	switch (val) {
+	case 0:
+		setNextScene(69);
+		break;
+	case 1:
+		setNextScene(_selectOpponentScreen);
+		break;
+	case 2:
+		_levelDoneMask = 2;
+		_numLevelsDone = 1;
+		setNextScene(_selectOpponentScreen);
+		break;
+	case 3:
+		_levelDoneMask = 2 | 4;
+		_numLevelsDone = 2;
+		setNextScene(_selectOpponentScreen);
+		break;
+	case 4:
+		_levelDoneMask = 2 | 4 | 8;
+		_numLevelsDone = 3;
+		setNextScene(_selectOpponentScreen);
+		break;
+	case 5:
+		_levelDoneMask = 2 | 4 | 8 | 0x10;
+		_numLevelsDone = 4;
+		setNextScene(0x66);
+		break;
+	default:
+		break;
+	}
 }
 
 // Debugger methods
@@ -1595,11 +1634,11 @@ DebuggerBountyHunter::DebuggerBountyHunter(GameBountyHunter *game) {
 
 bool DebuggerBountyHunter::cmdWarpTo(int argc, const char **argv) {
 	if (argc != 2) {
-		debugPrintf("Usage: warp <int>");
+		debugPrintf("Usage: warp <int>\n");
 		return true;
 	} else {
 		int val = atoi(argv[1]);
-		_game->debugWarpTo(val);
+		_game->debug_warpTo(val);
 		return false;
 	}
 }
diff --git a/engines/alg/logic/game_bountyhunter.h b/engines/alg/logic/game_bountyhunter.h
index cbd1c79a2de..72d16bfb1ea 100644
--- a/engines/alg/logic/game_bountyhunter.h
+++ b/engines/alg/logic/game_bountyhunter.h
@@ -55,7 +55,7 @@ public:
 	GameBountyHunter(AlgEngine *vm, const AlgGameDescription *gd);
 	~GameBountyHunter() override;
 	Common::Error run() override;
-	void debugWarpTo(int val);
+	void debug_warpTo(int val);
 
 private:
 	void init() override;
@@ -133,7 +133,7 @@ private:
 	const uint16 _deathScenarios[9] = {0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0};
 	const uint16 _onePlayerOfTwoDead[2] = {0x0109, 0x010A};
 	const uint16 _allPlayersDead = 0x108;
-
+	const uint16 _selectOpponentScreen = 0x017B;
 	// gamestate
 	uint8 _continuesUsed = 0;
 	uint8 _currentLevel = 0;
@@ -194,8 +194,6 @@ private:
 	bool _shotgunGiven = false;
 	bool _bagDropped = false;
 
-	Common::String _subScene;
-
 	// base functions
 	void updateScreen() override;
 	uint16 startMyGame();
diff --git a/engines/alg/logic/game_crimepatrol.cpp b/engines/alg/logic/game_crimepatrol.cpp
index 20c106832d3..9003fcdcfea 100644
--- a/engines/alg/logic/game_crimepatrol.cpp
+++ b/engines/alg/logic/game_crimepatrol.cpp
@@ -1365,8 +1365,18 @@ void GameCrimePatrol::sceneDefaultScore(Scene *scene) {
 }
 
 // Debug methods
-void GameCrimePatrol::debugWarpTo(int val) {
-	// TODO implement
+void GameCrimePatrol::debug_warpTo(int val) {
+	if (_vm->isDemo()) {
+		return;
+	}
+	resetParams();
+	if (val > 0 && val <= 14) {
+		for (uint8 i = 0; i < val; i++) {
+			sceneNxtscnGeneric(i);
+		}
+	} else if (val == 0) {
+		_curScene = Common::String::format("scene%d", _stageStartScenes[0]);
+	}
 }
 
 void GameCrimePatrol::debugDrawPracticeRects() {
@@ -1401,11 +1411,11 @@ DebuggerCrimePatrol::DebuggerCrimePatrol(GameCrimePatrol *game) {
 
 bool DebuggerCrimePatrol::cmdWarpTo(int argc, const char **argv) {
 	if (argc != 2) {
-		debugPrintf("Usage: warp <int>");
+		debugPrintf("Usage: warp <int>\n");
 		return true;
 	} else {
 		int val = atoi(argv[1]);
-		_game->debugWarpTo(val);
+		_game->debug_warpTo(val);
 		return false;
 	}
 }
diff --git a/engines/alg/logic/game_crimepatrol.h b/engines/alg/logic/game_crimepatrol.h
index 628a181fd60..619aa2c6280 100644
--- a/engines/alg/logic/game_crimepatrol.h
+++ b/engines/alg/logic/game_crimepatrol.h
@@ -53,7 +53,7 @@ public:
 	GameCrimePatrol(AlgEngine *vm, const AlgGameDescription *gd);
 	~GameCrimePatrol() override;
 	void init() override;
-	void debugWarpTo(int val);
+	void debug_warpTo(int val);
 
 private:
 	Common::Error run() override;
diff --git a/engines/alg/logic/game_drugwars.cpp b/engines/alg/logic/game_drugwars.cpp
index 87595d136ae..8700c0ccbca 100644
--- a/engines/alg/logic/game_drugwars.cpp
+++ b/engines/alg/logic/game_drugwars.cpp
@@ -688,41 +688,6 @@ uint16 GameDrugWars::pickDeathScene() {
 	return _diedScenesByStage[_stage][_deathPicked];
 }
 
-void GameDrugWars::sceneNxtscnGeneric(uint8 index) {
-	uint16 nextSceneId = 0;
-	_gotTo[index] = 0;
-	if (_gotTo[0] || _gotTo[1] || _gotTo[3] || _gotTo[2]) {
-		nextSceneId = 0x26;
-	} else if (_gotTo[4] || _gotTo[5] || _gotTo[6]) {
-		if (_stage == 1) {
-			nextSceneId = 0x52;
-		} else {
-			_stage = 1;
-			nextSceneId = 0x50;
-		}
-	} else if (_gotTo[7] || _gotTo[8] || _gotTo[9]) {
-		if (_stage == 2) {
-			nextSceneId = 0x9A;
-		} else {
-			_stage = 2;
-			nextSceneId = 0x81;
-		}
-	} else if (_gotTo[10] || _gotTo[11] || _gotTo[12]) {
-		if (_stage == 3) {
-			nextSceneId = 0xDF;
-		} else {
-			_stage = 3;
-			nextSceneId = 0x14B;
-		}
-	} else if (_gotTo[13]) {
-		_stage = 4;
-		nextSceneId = 0x18F;
-	} else {
-		nextSceneId = 0x21;
-	}
-	_curScene = Common::String::format("scene%d", nextSceneId);
-}
-
 void GameDrugWars::rectSelectGeneric(uint8 index) {
 	if (_gotTo[index] > 0) {
 		_curScene = Common::String::format("scene%d", _gotTo[index]);
@@ -1062,8 +1027,19 @@ void GameDrugWars::sceneDefaultScore(Scene *scene) {
 }
 
 // Debug methods
-void GameDrugWars::debugWarpTo(int val) {
-	// TODO implement
+void GameDrugWars::debug_warpTo(int val) {
+	if (_vm->isDemo()) {
+		return;
+	}
+	resetParams();
+	if (val > 0 && val <= 14) {
+		for (uint8 i = 0; i < val; i++) {
+			_gotTo[i] = 0;
+			sceneNxtscnFinishScenario(nullptr);
+		}
+	} else if (val == 0) {
+		_curScene = Common::String::format("scene%d", _stageStartScenes[0]);
+	}
 }
 
 // Debugger methods
@@ -1078,11 +1054,11 @@ DebuggerDrugWars::DebuggerDrugWars(GameDrugWars *game) {
 
 bool DebuggerDrugWars::cmdWarpTo(int argc, const char **argv) {
 	if (argc != 2) {
-		debugPrintf("Usage: warp <int>");
+		debugPrintf("Usage: warp <int>\n");
 		return true;
 	} else {
 		int val = atoi(argv[1]);
-		_game->debugWarpTo(val);
+		_game->debug_warpTo(val);
 		return false;
 	}
 }
diff --git a/engines/alg/logic/game_drugwars.h b/engines/alg/logic/game_drugwars.h
index da67b8760b9..43a077a2244 100644
--- a/engines/alg/logic/game_drugwars.h
+++ b/engines/alg/logic/game_drugwars.h
@@ -53,7 +53,7 @@ public:
 	GameDrugWars(AlgEngine *vm, const AlgGameDescription *gd);
 	~GameDrugWars() override;
 	Common::Error run() override;
-	void debugWarpTo(int val);
+	void debug_warpTo(int val);
 
 private:
 	void init() override;
@@ -170,7 +170,6 @@ private:
 	uint16 randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude);
 	uint16 pickRandomScene(uint8 index, uint8 max);
 	uint16 pickDeathScene();
-	void sceneNxtscnGeneric(uint8 index);
 	void rectSelectGeneric(uint8 index);
 
 	// Script functions: RectHit
diff --git a/engines/alg/logic/game_johnnyrock.cpp b/engines/alg/logic/game_johnnyrock.cpp
index 0cf0903543c..cc20fcd65ab 100644
--- a/engines/alg/logic/game_johnnyrock.cpp
+++ b/engines/alg/logic/game_johnnyrock.cpp
@@ -339,7 +339,7 @@ Common::Error GameJohnnyRock::run() {
 		_currentFrame = getFrame(scene);
 		while (_currentFrame <= scene->_endFrame && _curScene == oldscene && !_vm->shouldQuit()) {
 			updateMouse();
-			// TODO: call scene->messageFunc
+			callScriptFunctionScene(SHOWMSG, scene->_scnmsg, scene);
 			callScriptFunctionScene(INSOP, scene->_insop, scene);
 			_holster = weaponDown();
 			if (_holster) {
@@ -1852,8 +1852,18 @@ void GameJohnnyRock::sceneDefaultScore(Scene *scene) {
 }
 
 // Debug methods
-void GameJohnnyRock::debugWarpTo(int val) {
-	// TODO implement
+void GameJohnnyRock::debug_warpTo(int val) {
+	resetParams();
+	switch (val) {
+	case 0:
+		_curScene = _startScene;
+		break;
+	case 1:
+		_curScene = "scene175";
+		break;
+	default:
+		break;
+	}
 }
 
 // Debugger methods
@@ -1868,11 +1878,11 @@ DebuggerJohnnyRock::DebuggerJohnnyRock(GameJohnnyRock *game) {
 
 bool DebuggerJohnnyRock::cmdWarpTo(int argc, const char **argv) {
 	if (argc != 2) {
-		debugPrintf("Usage: warp <int>");
+		debugPrintf("Usage: warp <int>\n");
 		return true;
 	} else {
 		int val = atoi(argv[1]);
-		_game->debugWarpTo(val);
+		_game->debug_warpTo(val);
 		return false;
 	}
 }
diff --git a/engines/alg/logic/game_johnnyrock.h b/engines/alg/logic/game_johnnyrock.h
index 3a92ece666f..ba27a2f4717 100644
--- a/engines/alg/logic/game_johnnyrock.h
+++ b/engines/alg/logic/game_johnnyrock.h
@@ -55,8 +55,8 @@ public:
 	GameJohnnyRock(AlgEngine *vm, const AlgGameDescription *gd);
 	~GameJohnnyRock() override;
 	Common::Error run() override;
-	void debugWarpTo(int val);
 	void runCursorTimer();
+	void debug_warpTo(int val);
 
 private:
 	void init() override;
diff --git a/engines/alg/logic/game_maddog.cpp b/engines/alg/logic/game_maddog.cpp
index ef0723c7b44..98e3cbedecc 100644
--- a/engines/alg/logic/game_maddog.cpp
+++ b/engines/alg/logic/game_maddog.cpp
@@ -360,7 +360,7 @@ Common::Error GameMaddog::run() {
 		_currentFrame = getFrame(scene);
 		while (_currentFrame <= scene->_endFrame && _curScene == oldscene && !_vm->shouldQuit()) {
 			updateMouse();
-			// TODO: call scene->messageFunc
+			callScriptFunctionScene(SHOWMSG, scene->_scnmsg, scene);
 			callScriptFunctionScene(INSOP, scene->_insop, scene);
 			_holster = weaponDown();
 			if (_holster) {
@@ -1616,21 +1616,26 @@ void GameMaddog::sceneDefaultScore(Scene *scene) {
 }
 
 // Debug methods
-void GameMaddog::debugWarpTo(int val) {
+void GameMaddog::debug_warpTo(int val) {
+	resetParams();
 	switch (val) {
 	case 0:
 		_beenTo = 0;
-		_curScene = "scene28";
+		_curScene = _startScene;
 		break;
 	case 1:
+		_beenTo = 0;
+		_curScene = "scene28";
+		break;
+	case 2:
 		_beenTo = 1;
 		_curScene = pickTown();
 		break;
-	case 2:
+	case 3:
 		_beenTo = 15;
 		_curScene = pickTown();
 		break;
-	case 3:
+	case 4:
 		_beenTo = 575;
 		// always go right
 		_map0 = -1;
@@ -1638,16 +1643,16 @@ void GameMaddog::debugWarpTo(int val) {
 		_map2 = -1;
 		_curScene = pickTown();
 		break;
-	case 4:
+	case 5:
 		_beenTo = 575;
 		_hideOutFront = true; // go to front
 		_curScene = "scene210";
 		break;
-	case 5:
+	case 6:
 		_beenTo = 639;
 		_curScene = "scene227";
 		break;
-	case 6:
+	case 7:
 		_beenTo = 1023;
 		_curScene = "scene250";
 		break;
@@ -1668,11 +1673,19 @@ DebuggerMaddog::DebuggerMaddog(GameMaddog *game) {
 
 bool DebuggerMaddog::cmdWarpTo(int argc, const char **argv) {
 	if (argc != 2) {
-		debugPrintf("Usage: warp <int>");
+		debugPrintf("Usage: warp <int>\n");
 		return true;
 	} else {
 		int val = atoi(argv[1]);
-		_game->debugWarpTo(val);
+		_game->debug_warpTo(val);
+		if (val == 4) {
+			debugPrintf("Hint: Always go right\n");
+			return true;
+		}
+		if (val == 5) {
+			debugPrintf("Hint: Go to front of hideout\n");
+			return true;
+		}
 		return false;
 	}
 }
diff --git a/engines/alg/logic/game_maddog.h b/engines/alg/logic/game_maddog.h
index 5a1c87435c7..7d0751726b9 100644
--- a/engines/alg/logic/game_maddog.h
+++ b/engines/alg/logic/game_maddog.h
@@ -55,8 +55,8 @@ public:
 	GameMaddog(AlgEngine *vm, const AlgGameDescription *gd);
 	~GameMaddog() override;
 	Common::Error run() override;
-	void debugWarpTo(int val);
 	void runCursorTimer();
+	void debug_warpTo(int val);
 
 private:
 	void init() override;
diff --git a/engines/alg/logic/game_maddog2.cpp b/engines/alg/logic/game_maddog2.cpp
index 5d3eed11aed..f6868f9b351 100644
--- a/engines/alg/logic/game_maddog2.cpp
+++ b/engines/alg/logic/game_maddog2.cpp
@@ -368,7 +368,7 @@ Common::Error GameMaddog2::run() {
 		_currentFrame = getFrame(scene);
 		while (_currentFrame <= scene->_endFrame && _curScene == oldscene && !_vm->shouldQuit()) {
 			updateMouse();
-			// TODO: call scene->messageFunc
+			callScriptFunctionScene(SHOWMSG, scene->_scnmsg, scene);
 			callScriptFunctionScene(INSOP, scene->_insop, scene);
 			_holster = weaponDown();
 			if (_holster) {
@@ -1806,8 +1806,30 @@ void GameMaddog2::sceneDefaultScore(Scene *scene) {
 }
 
 // Debug methods
-void GameMaddog2::debugWarpTo(int val) {
-	// TODO implement
+void GameMaddog2::debug_warpTo(int val) {
+	resetParams();
+	switch (val) {
+	case 0:
+		_curScene = _startScene;
+		break;
+	case 1:
+		_curScene = "scene50";
+		break;
+	case 2:
+		_doneGuide = 1;
+		_curScene = "scene50";
+		break;
+	case 3:
+		_doneGuide = 1 | 2;
+		_curScene = "scene50";
+		break;
+	case 4:
+		_doneGuide = 1 | 2 | 4;
+		_curScene = "scene290";
+		break;
+	default:
+		break;
+	}
 }
 
 // Debugger methods
@@ -1822,11 +1844,11 @@ DebuggerMaddog2::DebuggerMaddog2(GameMaddog2 *game) {
 
 bool DebuggerMaddog2::cmdWarpTo(int argc, const char **argv) {
 	if (argc != 2) {
-		debugPrintf("Usage: warp <int>");
+		debugPrintf("Usage: warp <int>\n");
 		return true;
 	} else {
 		int val = atoi(argv[1]);
-		_game->debugWarpTo(val);
+		_game->debug_warpTo(val);
 		return false;
 	}
 }
diff --git a/engines/alg/logic/game_maddog2.h b/engines/alg/logic/game_maddog2.h
index 4113347cb7c..b0f575b55fd 100644
--- a/engines/alg/logic/game_maddog2.h
+++ b/engines/alg/logic/game_maddog2.h
@@ -55,8 +55,8 @@ public:
 	GameMaddog2(AlgEngine *vm, const AlgGameDescription *gd);
 	~GameMaddog2() override;
 	Common::Error run() override;
-	void debugWarpTo(int val);
 	void runCursorTimer();
+	void debug_warpTo(int val);
 
 private:
 	void init() override;
diff --git a/engines/alg/logic/game_spacepirates.cpp b/engines/alg/logic/game_spacepirates.cpp
index 6c50e2a7b12..329c4c7885e 100644
--- a/engines/alg/logic/game_spacepirates.cpp
+++ b/engines/alg/logic/game_spacepirates.cpp
@@ -425,6 +425,9 @@ Common::Error GameSpacePirates::run() {
 			int32 remainingMillis = _nextFrameTime - getMsTime();
 			if (remainingMillis < 10) {
 				if (_videoDecoder->getCurrentFrame() > 0) {
+					if (_videoDecoder->isFinished()) {
+						break;
+					}
 					_videoDecoder->getNextFrame();
 				}
 				remainingMillis = _nextFrameTime - getMsTime();
@@ -1267,6 +1270,7 @@ void GameSpacePirates::rectShotBlueCrystal(Rect *rect) {
 }
 
 void GameSpacePirates::rectShotGreenCrystal(Rect *rect) {
+	debug("rectShotGreenCrystal");
 	uint16 picked = 0;
 	displayShotFiredImage();
 	playSound(_shotSound);
@@ -1296,6 +1300,7 @@ void GameSpacePirates::rectShotGreenCrystal(Rect *rect) {
 		}
 	}
 	_nextSceneFound = true;
+	debug("rectShotGreenCrystal picked: %d", picked);
 	_curScene = Common::String::format("scene%d", picked);
 }
 
@@ -1336,8 +1341,6 @@ void GameSpacePirates::rectSkipScene(Rect *rect) {
 	displayShotFiredImage();
 	playSound(_shotSound);
 	_nextSceneFound = true;
-	Scene *scene = _sceneInfo->findScene(_curScene);
-	_curScene = scene->_next;
 }
 
 void GameSpacePirates::rectHitPirateShip(Rect *rect) {
@@ -1989,8 +1992,56 @@ void GameSpacePirates::sceneDefaultScore(Scene *scene) {
 }
 
 // Debug methods
-void GameSpacePirates::debugWarpTo(int val) {
-	// TODO implement
+void GameSpacePirates::debug_warpTo(int val) {
+	if (_vm->isDemo()) {
+		return;
+	}
+	resetParams();
+	switch (val) {
+	case 0:
+		_curScene = "scene187";
+		break;
+	case 1:
+		_curScene = "scene110";
+		_pickedStartSplitter = 0x6A;
+		break;
+	case 2:
+		_worldDone[0] = true;
+		_pickedStartSplitter = 0x6A;
+		_curScene = "scene110";
+		break;
+	case 3:
+		_worldDone[0] = true;
+		_worldDone[1] = true;
+		_pickedStartSplitter = 0x6A;
+		_curScene = "scene110";
+		break;
+	case 4:
+		_worldDone[0] = true;
+		_worldDone[1] = true;
+		_worldDone[2] = true;
+		_pickedStartSplitter = 0x6A;
+		_curScene = "scene110";
+		break;
+	case 5:
+		_worldDone[0] = true;
+		_worldDone[1] = true;
+		_worldDone[2] = true;
+		_worldDone[3] = true;
+		_pickedStartSplitter = 0x6A;
+		_curScene = "scene192";
+		break;
+	case 6:
+		_worldDone[0] = true;
+		_worldDone[1] = true;
+		_worldDone[2] = true;
+		_worldDone[3] = true;
+		_pickedStartSplitter = 0x6A;
+		_curScene = "scene334";
+		break;
+	default:
+		break;
+	}
 }
 
 // Debugger methods
@@ -2005,11 +2056,15 @@ DebuggerSpacePirates::DebuggerSpacePirates(GameSpacePirates *game) {
 
 bool DebuggerSpacePirates::cmdWarpTo(int argc, const char **argv) {
 	if (argc != 2) {
-		debugPrintf("Usage: warp <int>");
+		debugPrintf("Usage: warp <int>\n");
 		return true;
 	} else {
 		int val = atoi(argv[1]);
-		_game->debugWarpTo(val);
+		_game->debug_warpTo(val);
+		if (val >= 1 && val <= 5) {
+			debugPrintf("Hint: Crystal shoot order: red, green, blue\n");
+			return true;
+		}
 		return false;
 	}
 }
diff --git a/engines/alg/logic/game_spacepirates.h b/engines/alg/logic/game_spacepirates.h
index 054cb9759e9..3faed264848 100644
--- a/engines/alg/logic/game_spacepirates.h
+++ b/engines/alg/logic/game_spacepirates.h
@@ -54,7 +54,7 @@ public:
 	GameSpacePirates(AlgEngine *vm, const AlgGameDescription *gd);
 	~GameSpacePirates() override;
 	Common::Error run() override;
-	void debugWarpTo(int val);
+	void debug_warpTo(int val);
 
 private:
 	void init() override;
diff --git a/engines/alg/scene.cpp b/engines/alg/scene.cpp
index 3e447c7180b..0e82b38ab94 100644
--- a/engines/alg/scene.cpp
+++ b/engines/alg/scene.cpp
@@ -250,7 +250,7 @@ void SceneInfo::addZonesToScenes() {
 		if (!scene->_zonesStart.empty()) {
 			Zone *zone = findZone(scene->_zonesStart);
 			while (true) {
-				if (zone) {
+				if (zone != nullptr) {
 					zone = zone->clone();
 					zone->_difficulty = 0;
 					scene->_zones.push_back(zone);
@@ -259,13 +259,16 @@ void SceneInfo::addZonesToScenes() {
 					break;
 				} else {
 					zone = findZone(zone->_next);
+					if (zone == nullptr) {
+						break;
+					}
 				}
 			}
 		}
 		if (!scene->_zonesStart2.empty()) {
 			Zone *zone = findZone(scene->_zonesStart);
 			while (true) {
-				if (zone) {
+				if (zone != nullptr) {
 					zone = zone->clone();
 					zone->_difficulty = 1;
 					scene->_zones.push_back(zone);
@@ -274,13 +277,16 @@ void SceneInfo::addZonesToScenes() {
 					break;
 				} else {
 					zone = findZone(zone->_next);
+					if (zone == nullptr) {
+						break;
+					}
 				}
 			}
 		}
 		if (!scene->_zonesStart3.empty()) {
 			Zone *zone = findZone(scene->_zonesStart);
 			while (true) {
-				if (zone) {
+				if (zone != nullptr) {
 					zone = zone->clone();
 					zone->_difficulty = 2;
 					scene->_zones.push_back(zone);
@@ -289,6 +295,9 @@ void SceneInfo::addZonesToScenes() {
 					break;
 				} else {
 					zone = findZone(zone->_next);
+					if (zone == nullptr) {
+						break;
+					}
 				}
 			}
 		}


Commit: 90a83d7322208c5b2782d1dffd7cdaa63bcce255
    https://github.com/scummvm/scummvm/commit/90a83d7322208c5b2782d1dffd7cdaa63bcce255
Author: loki (loki at localhost)
Date: 2026-03-01T13:20:13+01:00

Commit Message:
ALG: minor style cleanup

Changed paths:
    engines/alg/logic/game_bountyhunter.cpp
    engines/alg/logic/game_crimepatrol.cpp
    engines/alg/logic/game_drugwars.cpp


diff --git a/engines/alg/logic/game_bountyhunter.cpp b/engines/alg/logic/game_bountyhunter.cpp
index 81e8f643a44..65a01d01f49 100644
--- a/engines/alg/logic/game_bountyhunter.cpp
+++ b/engines/alg/logic/game_bountyhunter.cpp
@@ -933,7 +933,7 @@ uint16 GameBountyHunter::randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude
 	}
 	uint16 randomNum = 0;
 	// find an unused random number
-	while (1) {
+	while (true) {
 		randomNum = _rnd->getRandomNumber(max - 1);
 		// check if bit is already used
 		uint16 bit = 1 << randomNum;
diff --git a/engines/alg/logic/game_crimepatrol.cpp b/engines/alg/logic/game_crimepatrol.cpp
index 9003fcdcfea..e773eaaa2f7 100644
--- a/engines/alg/logic/game_crimepatrol.cpp
+++ b/engines/alg/logic/game_crimepatrol.cpp
@@ -688,7 +688,7 @@ uint16 GameCrimePatrol::randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude)
 	}
 	uint16 randomNum = 0;
 	// find an unused random number
-	while (1) {
+	while (true) {
 		randomNum = _rnd->getRandomNumber(max - 1);
 		// check if bit is already used
 		uint16 bit = 1 << randomNum;
diff --git a/engines/alg/logic/game_drugwars.cpp b/engines/alg/logic/game_drugwars.cpp
index 8700c0ccbca..664f593a886 100644
--- a/engines/alg/logic/game_drugwars.cpp
+++ b/engines/alg/logic/game_drugwars.cpp
@@ -640,7 +640,7 @@ uint16 GameDrugWars::randomUnusedInt(uint8 max, uint16 *mask, uint16 exclude) {
 	}
 	uint16 randomNum = 0;
 	// find an unused random number
-	while (1) {
+	while (true) {
 		randomNum = _rnd->getRandomNumber(max - 1);
 		// check if bit is already used
 		uint16 bit = 1 << randomNum;


Commit: 597d22e7f05b8e358849167f50e4a9e944c7ceb6
    https://github.com/scummvm/scummvm/commit/597d22e7f05b8e358849167f50e4a9e944c7ceb6
Author: loki (loki at localhost)
Date: 2026-03-01T13:20:13+01:00

Commit Message:
ALG: fix clang warnings of unused fields

Changed paths:
    engines/alg/logic/game_bountyhunter.h
    engines/alg/logic/game_johnnyrock.h


diff --git a/engines/alg/logic/game_bountyhunter.h b/engines/alg/logic/game_bountyhunter.h
index 72d16bfb1ea..eb962a1e376 100644
--- a/engines/alg/logic/game_bountyhunter.h
+++ b/engines/alg/logic/game_bountyhunter.h
@@ -139,7 +139,6 @@ private:
 	uint8 _currentLevel = 0;
 	uint16 _currentSubLevelSceneId = 0;
 	uint8 _difficulty = 0;
-	uint8 _oldDifficulty = 1;
 	uint32 _firstDrawFrame = 0;
 	int8 _gunfightCount = 0;
 	uint8 _kill3Count = 0;
diff --git a/engines/alg/logic/game_johnnyrock.h b/engines/alg/logic/game_johnnyrock.h
index ba27a2f4717..c9e66b7c336 100644
--- a/engines/alg/logic/game_johnnyrock.h
+++ b/engines/alg/logic/game_johnnyrock.h
@@ -119,8 +119,6 @@ private:
 	bool _holster = false;
 	uint8 _oldDifficulty = 1;
 	uint8 _inHolster = 0;
-	int8 _lives = 0;
-	int8 _oldLives = 0;
 	int32 _score = 0;
 	int32 _oldScore = -1;
 	bool _shotFired = false;


Commit: 854c9aa33b267765d2c4f07265a1ac69e714e04e
    https://github.com/scummvm/scummvm/commit/854c9aa33b267765d2c4f07265a1ac69e714e04e
Author: loki (loki at localhost)
Date: 2026-03-01T13:20:13+01:00

Commit Message:
ALG: bugfix, do not assign int to string

Changed paths:
    engines/alg/scene.cpp


diff --git a/engines/alg/scene.cpp b/engines/alg/scene.cpp
index 0e82b38ab94..ce7f44b9f19 100644
--- a/engines/alg/scene.cpp
+++ b/engines/alg/scene.cpp
@@ -147,8 +147,8 @@ void SceneInfo::parseScene(const Common::String &sceneName, uint32 startFrame, u
 			scene->_dataParam2 = atoi(tokenizer.nextToken().c_str());
 			scene->_dataParam3 = atoi(tokenizer.nextToken().c_str());
 			scene->_dataParam4 = atoi(tokenizer.nextToken().c_str());
-			scene->_dataParam5 = atoi(tokenizer.nextToken().c_str());
-			scene->_dataParam6 = atoi(tokenizer.nextToken().c_str());
+			scene->_dataParam5 = tokenizer.nextToken().c_str();
+			scene->_dataParam6 = tokenizer.nextToken().c_str();
 			break;
 		case 11: // DIFF
 			scene->_diff = atoi(tokenizer.nextToken().c_str());


Commit: 8e49de7a26555f34fb6fcabe9e913d8f6c67c315
    https://github.com/scummvm/scummvm/commit/8e49de7a26555f34fb6fcabe9e913d8f6c67c315
Author: loki (loki at localhost)
Date: 2026-03-01T13:20:13+01:00

Commit Message:
ALG: add GUIO docs

Changed paths:
    doc/docportal/settings/game.rst


diff --git a/doc/docportal/settings/game.rst b/doc/docportal/settings/game.rst
index d40311c520d..7ecdda0d6a4 100644
--- a/doc/docportal/settings/game.rst
+++ b/doc/docportal/settings/game.rst
@@ -204,6 +204,22 @@ High Quality
 
 ,,,,,,
 
+.. _ALG:
+
+ALG
+******************
+
+.. _single_speed_videos:
+
+Use lower quality single speed CD-ROM video
+	Some games come with videos in single and double CD-ROM speed.
+	The double CD-ROM speed videos are of slightly higher quality and bandwith.
+	By default the double speed videos are used, this toggle changes to single speed videos.
+
+	*single_speed_videos*
+
+,,,,,,
+
 .. _BLADERUNNER:
 
 Bladerunner


Commit: d3b5625ea2c22e260585df53e55a19bd766e96b4
    https://github.com/scummvm/scummvm/commit/d3b5625ea2c22e260585df53e55a19bd766e96b4
Author: loki (loki at localhost)
Date: 2026-03-01T13:20:13+01:00

Commit Message:
ALG: enable build-by-default

Changed paths:
    engines/alg/configure.engine


diff --git a/engines/alg/configure.engine b/engines/alg/configure.engine
index a9e02e7404c..eba92b7709f 100644
--- a/engines/alg/configure.engine
+++ b/engines/alg/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
-add_engine alg "American Laser Games" no
+add_engine alg "American Laser Games" yes


Commit: 7a5326ad82f3a5991d4435d1f2003641481132e5
    https://github.com/scummvm/scummvm/commit/7a5326ad82f3a5991d4435d1f2003641481132e5
Author: loki (loki at localhost)
Date: 2026-03-01T13:20:13+01:00

Commit Message:
NEWS: add ALG games

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index d1d5f076656..dc48febd1fb 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -3,6 +3,12 @@ For a more comprehensive changelog of the latest experimental code, see:
 
 #### 2026.1.1 (2026-XX-XX)
 
+ New games:
+   - Added ALG engine for DOS versions of American Laser Games:
+     Crime Patrol, Crime Patrol 2: Drug Wars, The Last Bounty Hunter,
+     Mad Dog McCree, Mad Dog II: The Lost Gold, Space Pirates
+     and Who Shot Johnny Rock?.
+
  General:
    - Improved PC-Speaker emulation.
    - Implemented multiselect in the GUI launcher games list.




More information about the Scummvm-git-logs mailing list