[Scummvm-git-logs] scummvm master -> 8b4f996cb98fc372deb49de934a3da14ec0d2466

neuromancer noreply at scummvm.org
Sat Mar 28 08:24:07 UTC 2026


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

Summary:
8b4f996cb9 FREESCAPE: added win console command and improved some endgame code


Commit: 8b4f996cb98fc372deb49de934a3da14ec0d2466
    https://github.com/scummvm/scummvm/commit/8b4f996cb98fc372deb49de934a3da14ec0d2466
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-28T09:23:56+01:00

Commit Message:
FREESCAPE: added win console command and improved some endgame code

Changed paths:
    engines/freescape/debugger.cpp
    engines/freescape/debugger.h
    engines/freescape/freescape.cpp
    engines/freescape/freescape.h
    engines/freescape/games/castle/castle.cpp
    engines/freescape/games/castle/castle.h
    engines/freescape/games/dark/dark.cpp
    engines/freescape/games/dark/dark.h
    engines/freescape/games/driller/amiga.cpp
    engines/freescape/games/driller/driller.cpp
    engines/freescape/games/driller/driller.h
    engines/freescape/games/eclipse/eclipse.cpp
    engines/freescape/games/eclipse/eclipse.h
    engines/freescape/ui.cpp


diff --git a/engines/freescape/debugger.cpp b/engines/freescape/debugger.cpp
index 15dd7d0bff9..47b58d3199f 100644
--- a/engines/freescape/debugger.cpp
+++ b/engines/freescape/debugger.cpp
@@ -44,6 +44,7 @@ Debugger::Debugger(FreescapeEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("occ", WRAP_METHOD(Debugger, cmdShowOcclusion)); // toggle occlussion boxes
 	registerCmd("area", WRAP_METHOD(Debugger, cmdArea)); // show current area info
 	registerCmd("pos", WRAP_METHOD(Debugger, cmdPos)); // show camera position and direction
+	registerCmd("win", WRAP_METHOD(Debugger, cmdWin)); // trigger the current game's win condition
 }
 
 Debugger::~Debugger() {}
@@ -287,4 +288,19 @@ bool Debugger::cmdPos(int argc, const char **argv) {
 	return true;
 }
 
+bool Debugger::cmdWin(int argc, const char **argv) {
+	if (argc > 1) {
+		debugPrintf("Usage: win\n");
+		return true;
+	}
+
+	if (!_vm->triggerWinCondition()) {
+		debugPrintf("No win condition is available for this game or platform.\n");
+		return true;
+	}
+
+	debugPrintf("Triggered the win condition.\n");
+	return true;
+}
+
 }
diff --git a/engines/freescape/debugger.h b/engines/freescape/debugger.h
index fd10ae47d33..579a0e57649 100644
--- a/engines/freescape/debugger.h
+++ b/engines/freescape/debugger.h
@@ -48,6 +48,7 @@ private:
 	bool cmdShowOcclusion(int argc, const char **argv);
 	bool cmdArea(int argc, const char **argv);
 	bool cmdPos(int argc, const char **argv);
+	bool cmdWin(int argc, const char **argv);
 };
 
 }
diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 400b9187568..1788c584ed9 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -940,6 +940,10 @@ void FreescapeEngine::endGame() {
 	}
 }
 
+bool FreescapeEngine::triggerWinCondition() {
+	return false;
+}
+
 void FreescapeEngine::loadBorder() {
 	if (_border) {
 		Graphics::Surface *border = _gfx->convertImageFormatIfNecessary(_border);
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index d3ccc01fae9..22c19b9ccab 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -614,6 +614,7 @@ public:
 	StateVars _gameStateVars;
 	uint32 _gameStateBits;
 	void checkIfPlayerWasCrushed();
+	virtual bool triggerWinCondition();
 	virtual bool checkIfGameEnded();
 	virtual void endGame();
 	int _endGameDelayTicks;
diff --git a/engines/freescape/games/castle/castle.cpp b/engines/freescape/games/castle/castle.cpp
index 4bda08d0893..9e696d4ce2a 100644
--- a/engines/freescape/games/castle/castle.cpp
+++ b/engines/freescape/games/castle/castle.cpp
@@ -587,7 +587,7 @@ void CastleEngine::gotoArea(uint16 areaID, int entranceID) {
 		_pitch = -85;
 	} else {
 		// If escaped, play a different sound
-		if (getGameBit(31))
+		if (hasEscaped())
 			playSound(13, true, _soundFxHandle);
 		else
 			playSound(_soundIndexAreaChange, true, _soundFxHandle);
@@ -728,7 +728,7 @@ bool CastleEngine::checkIfGameEnded() {
 				insertTemporaryMessage(_fallenMessage, _countdown - 4);
 			_gameStateControl = kFreescapeGameStateEnd;
 		}
-		if ((isSpectrum() && getGameBit(31)) || (isDOS() && _currentArea->getAreaID() == 74)) { // Escaped!
+		if (hasEscaped()) {
 			_gameStateControl = kFreescapeGameStateEnd;
 			return true;
 		}
@@ -736,12 +736,28 @@ bool CastleEngine::checkIfGameEnded() {
 	return FreescapeEngine::checkIfGameEnded();
 }
 
+bool CastleEngine::triggerWinCondition() {
+	if (isDOS()) {
+		if (!_areaMap.contains(74))
+			return false;
+		gotoArea(74, 0);
+	} else {
+		setGameBit(31);
+	}
+
+	_endGameDelayTicks = 0;
+	_endGameKeyPressed = false;
+	_endGamePlayerEndArea = false;
+	_gameStateControl = kFreescapeGameStateEnd;
+	return true;
+}
+
 void CastleEngine::endGame() {
 	_shootingFrames = 0;
 	_delayedShootObject = nullptr;
 	_endGamePlayerEndArea = true;
 
-	if ((isSpectrum() && getGameBit(31)) || (isDOS() && _currentArea->getAreaID() == 74)) { // Escaped!
+	if (hasEscaped()) {
 		insertTemporaryMessage(_messagesList[5], INT_MIN);
 
 		if (isDOS()) {
@@ -756,6 +772,13 @@ void CastleEngine::endGame() {
 	_endGameKeyPressed = false;
 }
 
+bool CastleEngine::hasEscaped() {
+	if (isDOS())
+		return _currentArea && _currentArea->getAreaID() == 74;
+
+	return getGameBit(31);
+}
+
 void CastleEngine::pressedKey(const int keycode) {
 	// This code is duplicated in the DrillerEngine::pressedKey (except for the J case)
 	if (keycode == Common::KEYCODE_s) {
@@ -1270,7 +1293,7 @@ void CastleEngine::drawFullscreenGameOverAndWait() {
 		playSound(9, false, _soundFxHandle);
 	}
 
-	if (isSpectrum() && getGameBit(31)) {
+	if (!isDOS() && hasEscaped()) {
 		insertTemporaryMessage(_messagesList[5], _countdown - 1);
 	}
 
@@ -1279,7 +1302,7 @@ void CastleEngine::drawFullscreenGameOverAndWait() {
 			insertTemporaryMessage(scoreString, _countdown - 2);
 			insertTemporaryMessage(spiritsDestroyedString, _countdown - 4);
 			insertTemporaryMessage(keysCollectedString, _countdown - 6);
-			if (isSpectrum() && getGameBit(31)) {
+			if (!isDOS() && hasEscaped()) {
 				insertTemporaryMessage(_messagesList[5], _countdown - 8);
 			}
 		}
@@ -2126,7 +2149,7 @@ void CastleEngine::drawLiftingGate(Graphics::Surface *surface) {
 }
 
 void CastleEngine::drawDroppingGate(Graphics::Surface *surface) {
-	if (isSpectrum() && getGameBit(31))
+	if (!isDOS() && hasEscaped())
 		return; // No gate dropping when the player escaped
 
 	if (_droppingGateStartTicks <= 0)
diff --git a/engines/freescape/games/castle/castle.h b/engines/freescape/games/castle/castle.h
index dc19aaf8fb4..62e1a08d44c 100644
--- a/engines/freescape/games/castle/castle.h
+++ b/engines/freescape/games/castle/castle.h
@@ -57,6 +57,7 @@ public:
 	void beforeStarting() override;
 	void initKeymaps(Common::Keymap *engineKeyMap, Common::Keymap *infoScreenKeyMap, const char *target) override;
 	void initGameState() override;
+	bool triggerWinCondition() override;
 	void endGame() override;
 
 	void drawInfoMenu() override;
@@ -185,6 +186,7 @@ private:
 	void drawRiddle(uint16 riddle, uint32 front, uint32 back, Graphics::Surface *surface);
 	void tryToCollectKey();
 	void addGhosts();
+	bool hasEscaped();
 	bool ghostInArea();
 	void updateThunder();
 
diff --git a/engines/freescape/games/dark/dark.cpp b/engines/freescape/games/dark/dark.cpp
index d1115ad8045..5360285998d 100644
--- a/engines/freescape/games/dark/dark.cpp
+++ b/engines/freescape/games/dark/dark.cpp
@@ -559,6 +559,18 @@ bool DarkEngine::checkIfGameEnded() {
 	return false;
 }
 
+bool DarkEngine::triggerWinCondition() {
+	_gameStateVars[kVariableActiveECDs] = 0;
+	_gameStateVars[kVariableDarkECD] = 0;
+	_gameStateVars[kVariableDarkEnding] = kDarkEndingECDsDestroyed;
+	_ticksFromEnd = 0;
+	_endGameDelayTicks = 0;
+	_endGameKeyPressed = false;
+	_endGamePlayerEndArea = false;
+	_gameStateControl = kFreescapeGameStateEnd;
+	return true;
+}
+
 void DarkEngine::endGame() {
 	FreescapeEngine::endGame();
 
diff --git a/engines/freescape/games/dark/dark.h b/engines/freescape/games/dark/dark.h
index 5a030a6d847..6153cca0326 100644
--- a/engines/freescape/games/dark/dark.h
+++ b/engines/freescape/games/dark/dark.h
@@ -61,6 +61,7 @@ public:
 	void initKeymaps(Common::Keymap *engineKeyMap, Common::Keymap *infoScreenKeyMap, const char *target) override;
 	void initGameState() override;
 	void borderScreen() override;
+	bool triggerWinCondition() override;
 	bool checkIfGameEnded() override;
 	void endGame() override;
 
diff --git a/engines/freescape/games/driller/amiga.cpp b/engines/freescape/games/driller/amiga.cpp
index be955ee1e9c..d942963ea4c 100644
--- a/engines/freescape/games/driller/amiga.cpp
+++ b/engines/freescape/games/driller/amiga.cpp
@@ -438,7 +438,9 @@ void DrillerEngine::drawAmigaAtariSTUI(Graphics::Surface *surface) {
 		_temporaryMessages.push_back(message);
 		_temporaryMessageDeadlines.push_back(deadline);
 	} else {
-		if (_currentArea->_gasPocketRadius == 0)
+		if (_gameStateVars[32] == 18)
+			message = _messagesList[19];
+		else if (_currentArea->_gasPocketRadius == 0)
 			message = _messagesList[2];
 		else if (_drillStatusByArea[_currentArea->getAreaID()])
 			message = _messagesList[0];
diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index e1894280cfa..7298f771d48 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -93,6 +93,8 @@ DrillerEngine::DrillerEngine(OSystem *syst, const ADGameDescription *gd) : Frees
 
 	_endArea = 127;
 	_endEntrance = 0;
+	_finalAreaWinConditionIndex = -1;
+	_amigaAtariEndGameStep = -1;
 
 	_borderExtra = nullptr;
 	_borderExtraTexture = nullptr;
@@ -305,6 +307,7 @@ void DrillerEngine::gotoArea(uint16 areaID, int entranceID) {
 
 void DrillerEngine::loadAssets() {
 	FreescapeEngine::loadAssets();
+	_finalAreaWinConditionIndex = -1;
 	if (_areaMap.contains(18)) {
 		/*
 		We are going to inject a small script in the
@@ -328,6 +331,7 @@ void DrillerEngine::loadAssets() {
 		debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
 		_areaMap[18]->_conditions.push_back(instructions);
 		_areaMap[18]->_conditionSources.push_back(conditionSource);
+		_finalAreaWinConditionIndex = _areaMap[18]->_conditions.size() - 1;
 	}
 
 	_timeoutMessage = _messagesList[14];
@@ -876,6 +880,7 @@ void DrillerEngine::initGameState() {
 	FreescapeEngine::initGameState();
 	_quitConfirmCounter = 0;
 	_quitStartTicks = 0;
+	_amigaAtariEndGameStep = -1;
 
 	for (auto &it : _areaMap) {
 		if (_drillStatusByArea[it._key] != kDrillerNoRig)
@@ -910,19 +915,91 @@ bool DrillerEngine::checkIfGameEnded() {
 		if (_demoData[_demoIndex + 1] == 0x5f)
 			return true;
 
+	if ((isAmiga() || isAtariST()) && _gameStateControl == kFreescapeGameStatePlaying &&
+		_currentArea && _currentArea->getAreaID() == _endArea) {
+		stopMovement();
+		_endGameDelayTicks = 0;
+		_endGameKeyPressed = false;
+		_endGamePlayerEndArea = false;
+		_amigaAtariEndGameStep = -1;
+		_gameStateControl = kFreescapeGameStateEnd;
+		return true;
+	}
+
 	FreescapeEngine::checkIfGameEnded();
 	return false;
 }
 
+bool DrillerEngine::triggerWinCondition() {
+	_gameStateVars[32] = 18;
+
+	stopMovement();
+	if (!_currentArea || _currentArea->getAreaID() != 18)
+		gotoArea(18, 20);
+
+	if ((isAmiga() || isAtariST()) && _currentArea && _currentArea->getAreaID() == _endArea) {
+		_endGameDelayTicks = 0;
+		_endGameKeyPressed = false;
+		_endGamePlayerEndArea = false;
+		_amigaAtariEndGameStep = -1;
+		_gameStateControl = kFreescapeGameStateEnd;
+	}
+
+	return true;
+}
+
 void DrillerEngine::endGame() {
-	FreescapeEngine::endGame();
+	if (isAmiga() || isAtariST()) {
+		_shootingFrames = 0;
+		_delayedShootObject = nullptr;
+
+		if (_amigaAtariEndGameStep < 0) {
+			stopMovement();
+			if (!_currentArea || _currentArea->getAreaID() != _endArea)
+				gotoArea(_endArea, _endEntrance);
+
+			// ENDGAME on Amiga/Atari ST switches to set 127 and then runs a 21-step
+			// flythrough. The original Amiga coordinates are stored at twice the engine
+			// position scale, so convert the CMVY/CMVZ deltas before applying them.
+			// The original routine is blocking, so keep the redraw pacing in the
+			// engine's wait loop instead of stretching it across separate frames.
+			const int areaScale = MAX<int>(_currentArea ? _currentArea->getScale() : 1, 1);
+			playSound(11, false, _soundFxHandle);
+			_position.z() -= 8000.0f / areaScale;
+			_position.y() += 3000.0f / areaScale;
+			_lastPosition = _position;
+			_endGamePlayerEndArea = false;
+			_amigaAtariEndGameStep = 0;
+
+			if (_gameStateVars[32] == 18) // All areas are complete
+				insertTemporaryMessage(_messagesList[19], INT_MIN);
+
+			waitInLoop(0); // Initial BT01 after switching to set 127
+			for (int step = 0; step < 21; step++) {
+				_position.z() += 400.0f / areaScale;
+				_position.y() -= 140.0f / areaScale;
+				_lastPosition = _position;
+				waitInLoop(5);
+			}
 
-	if (!_endGamePlayerEndArea)
+			waitInLoop(0); // Final BT01 before LIFTUP
+			waitInLoop(102);
+			_endGamePlayerEndArea = true;
+			waitInLoop(500);
+			_gameStateControl = kFreescapeGameStateRestart;
+		}
+		_endGameKeyPressed = false;
 		return;
+	} else {
+		FreescapeEngine::endGame();
+
+		if (!_endGamePlayerEndArea)
+			return;
 
-	if (_gameStateVars[32] == 18) { // All areas are complete
-		insertTemporaryMessage(_messagesList[19], _countdown - 2);
-		_gameStateVars[32] = 0;  // Avoid repeating the message
+		if (_gameStateVars[32] == 18) { // All areas are complete
+			insertTemporaryMessage(_messagesList[19], _countdown - 2);
+			_gameStateVars[32] = 0;  // Avoid repeating the message
+		}
 	}
 
 	if (_endGameKeyPressed) {
diff --git a/engines/freescape/games/driller/driller.h b/engines/freescape/games/driller/driller.h
index f414ec6990d..059b0bc1ea2 100644
--- a/engines/freescape/games/driller/driller.h
+++ b/engines/freescape/games/driller/driller.h
@@ -62,6 +62,7 @@ public:
 
 	void initKeymaps(Common::Keymap *engineKeyMap, Common::Keymap *infoScreenKeyMap, const char *target) override;
 	void initGameState() override;
+	bool triggerWinCondition() override;
 	bool checkIfGameEnded() override;
 	void endGame() override;
 
@@ -77,6 +78,8 @@ public:
 	Common::Error loadGameStreamExtended(Common::SeekableReadStream *stream) override;
 
 private:
+	int _finalAreaWinConditionIndex;
+	int _amigaAtariEndGameStep;
 	bool drillDeployed(Area *area);
 	GeometricObject *_drillBase;
 	Math::Vector3d drillPosition();
diff --git a/engines/freescape/games/eclipse/eclipse.cpp b/engines/freescape/games/eclipse/eclipse.cpp
index 7ebd1105b91..7a81e20f5dc 100644
--- a/engines/freescape/games/eclipse/eclipse.cpp
+++ b/engines/freescape/games/eclipse/eclipse.cpp
@@ -204,6 +204,15 @@ bool EclipseEngine::checkIfGameEnded() {
 	return false;
 }
 
+bool EclipseEngine::triggerWinCondition() {
+	setGameBit(16);
+	_endGameDelayTicks = 0;
+	_endGameKeyPressed = false;
+	_endGamePlayerEndArea = false;
+	_gameStateControl = kFreescapeGameStateEnd;
+	return true;
+}
+
 void EclipseEngine::endGame() {
 	FreescapeEngine::endGame();
 
diff --git a/engines/freescape/games/eclipse/eclipse.h b/engines/freescape/games/eclipse/eclipse.h
index 5b66b8187fa..9d902e65795 100644
--- a/engines/freescape/games/eclipse/eclipse.h
+++ b/engines/freescape/games/eclipse/eclipse.h
@@ -139,6 +139,7 @@ public:
 	Common::Rect _saveGameArea;
 	Common::Rect _loadGameArea;
 
+	bool triggerWinCondition() override;
 	bool checkIfGameEnded() override;
 	void endGame() override;
 	void loadSoundsFx(Common::SeekableReadStream *file, int offset, int number) override;
diff --git a/engines/freescape/ui.cpp b/engines/freescape/ui.cpp
index 421f8bd917a..9f26f70a4de 100644
--- a/engines/freescape/ui.cpp
+++ b/engines/freescape/ui.cpp
@@ -45,6 +45,8 @@ void FreescapeEngine::waitInLoop(int maxWait) {
 					break;
 				if (isCastle() && isSpectrum() && getGameBit(31)) // Game is finished
 					break;
+				if (isDriller() && _gameStateVars[32] == 18) // Game is finished
+					break;
 				mousePos = event.mouse;
 
 				if (_demoMode)




More information about the Scummvm-git-logs mailing list