[Scummvm-git-logs] scummvm master -> 69be41bc2e05081d8889483c18989a8a82099d35

neuromancer noreply at scummvm.org
Wed Mar 25 20:27:15 UTC 2026


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

Summary:
f71240750d FREESCAPE: implemented temporary messages in castle for amiga/atari
eb35932c37 FREESCAPE: fixed original typos in castle dos spanish
4098630c93 FREESCAPE: avoid crash in castle zx
2813abb296 FREESCAPE: better touchscreen integration and debug code
0c102e65e2 FREESCAPE: added pos and area commands into the debugger
69be41bc2e FREESCAPE: presort by distance from camera (furthest first) to provide a stable initial ordering in depth computation


Commit: f71240750d183d98a21ed296de2c625ff459d706
    https://github.com/scummvm/scummvm/commit/f71240750d183d98a21ed296de2c625ff459d706
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-25T21:12:21+01:00

Commit Message:
FREESCAPE: implemented temporary messages in castle for amiga/atari

Changed paths:
    engines/freescape/games/castle/amiga.cpp
    engines/freescape/games/castle/castle.cpp
    engines/freescape/games/castle/castle.h
    engines/freescape/games/castle/dos.cpp


diff --git a/engines/freescape/games/castle/amiga.cpp b/engines/freescape/games/castle/amiga.cpp
index 1d51207ba19..6e6795138a4 100644
--- a/engines/freescape/games/castle/amiga.cpp
+++ b/engines/freescape/games/castle/amiga.cpp
@@ -474,9 +474,30 @@ void CastleEngine::drawAmigaAtariSTUI(Graphics::Surface *surface) {
 	drawLiftingGate(surface);
 	drawDroppingGate(surface);
 
-	drawStringInSurface(_currentArea->_name, 97, 182, 0, 0, surface);
+	uint8 r, g, b;
+	_gfx->readFromPalette(15, r, g, b);
+	uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
 	uint32 black = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0x00, 0x00, 0x00);
 
+	Common::Rect backRect(97, 181, 232, 190);
+	surface->fillRect(backRect, black);
+
+	Common::String message;
+	int deadline = -1;
+	getLatestMessages(message, deadline);
+	if (deadline > 0 && deadline <= _countdown) {
+		drawStringInSurface(message, 97, 182, front, black, surface);
+		_temporaryMessages.push_back(message);
+		_temporaryMessageDeadlines.push_back(deadline);
+	} else {
+		if (_gameStateControl != kFreescapeGameStateEnd) {
+			if (ghostInArea())
+				drawStringInSurface(_ghostInAreaMessage, 97, 182, front, black, surface);
+			else
+				drawStringInSurface(_currentArea->_name, 97, 182, front, black, surface);
+		}
+	}
+
 	// TODO: Draw collected keys - key sprites location in binary still unknown
 
 	// Draw flag animation at (288, 5)
diff --git a/engines/freescape/games/castle/castle.cpp b/engines/freescape/games/castle/castle.cpp
index c28eea204bc..46ea7687a0c 100644
--- a/engines/freescape/games/castle/castle.cpp
+++ b/engines/freescape/games/castle/castle.cpp
@@ -59,6 +59,8 @@ CastleEngine::CastleEngine(OSystem *syst, const ADGameDescription *gd) : Freesca
 	else if (isCPC())
 		initCPC();
 
+	// Messages are assigned after loading in initGameState()
+
 	_playerHeightNumber = 1;
 	_playerHeightMaxNumber = 1;
 	_lastTenSeconds = -1;
@@ -676,6 +678,23 @@ void CastleEngine::initGameState() {
 	FreescapeEngine::initGameState();
 	_playerHeightNumber = 1;
 
+	// Platform-specific message strings (indices differ between DOS and Amiga)
+	if (isAmiga() || isAtariST()) {
+		_notEnoughRoomMessage = _messagesList[21];
+		_tooWeakMessage = _messagesList[22];
+		_crawlSelectedMessage = _messagesList[23];
+		_walkSelectedMessage = _messagesList[24];
+		_runSelectedMessage = _messagesList[25];
+		_ghostInAreaMessage = _messagesList[126];
+	} else {
+		_notEnoughRoomMessage = _messagesList[11];
+		_tooWeakMessage = _messagesList[12];
+		_crawlSelectedMessage = _messagesList[13];
+		_walkSelectedMessage = _messagesList[14];
+		_runSelectedMessage = _messagesList[15];
+		_ghostInAreaMessage = _messagesList[116];
+	}
+
 	_gameStateVars[k8bitVariableShield] = 16;
 	_gameStateVars[k8bitVariableEnergy] = 1;
 	_gameStateVars[8] = 128; // -1
@@ -750,30 +769,30 @@ void CastleEngine::pressedKey(const int keycode) {
 	} else if (keycode == kActionRunMode) {
 		if (_playerHeightNumber == 0) {
 			if (_gameStateVars[k8bitVariableShield] <= 3) {
-				insertTemporaryMessage(_messagesList[12], _countdown - 2);
+				insertTemporaryMessage(_tooWeakMessage, _countdown - 2);
 				return;
 			}
 
 			if (!rise()) {
 				_playerStepIndex = 0;
-				insertTemporaryMessage(_messagesList[11], _countdown - 2);
+				insertTemporaryMessage(_notEnoughRoomMessage, _countdown - 2);
 				return;
 			}
 			_gameStateVars[k8bitVariableCrawling] = 0;
 		}
 		// TODO: raising can fail if there is no room, so the action should fail
 		_playerStepIndex = 2;
-		insertTemporaryMessage(_messagesList[15], _countdown - 2);
+		insertTemporaryMessage(_runSelectedMessage, _countdown - 2);
 	} else if (keycode == kActionWalkMode) {
 		if (_playerHeightNumber == 0) {
 			if (_gameStateVars[k8bitVariableShield] <= 3) {
-				insertTemporaryMessage(_messagesList[12], _countdown - 2);
+				insertTemporaryMessage(_tooWeakMessage, _countdown - 2);
 				return;
 			}
 
 			if (!rise()) {
 				_playerStepIndex = 0;
-				insertTemporaryMessage(_messagesList[11], _countdown - 2);
+				insertTemporaryMessage(_notEnoughRoomMessage, _countdown - 2);
 				return;
 			}
 			_gameStateVars[k8bitVariableCrawling] = 0;
@@ -781,14 +800,14 @@ void CastleEngine::pressedKey(const int keycode) {
 
 		// TODO: raising can fail if there is no room, so the action should fail
 		_playerStepIndex = 1;
-		insertTemporaryMessage(_messagesList[14], _countdown - 2);
+		insertTemporaryMessage(_walkSelectedMessage, _countdown - 2);
 	} else if (keycode == kActionCrawlMode) {
 		if (_playerHeightNumber == 1) {
 			lower();
 			_gameStateVars[k8bitVariableCrawling] = 128;
 		}
 		_playerStepIndex = 0;
-		insertTemporaryMessage(_messagesList[13], _countdown - 2);
+		insertTemporaryMessage(_crawlSelectedMessage, _countdown - 2);
 	} else if (keycode == kActionRunModifier) {
 		// Shift-to-run: save current mode, switch to run while held
 		if (_playerStepIndex == 2)
@@ -1343,6 +1362,9 @@ void CastleEngine::executePrint(FCLInstruction &instruction) {
 		drawFullscreenRiddleAndWait(index);
 		return;
 	}
+	if (isAmiga() || isAtariST()) {
+		index = index + 10;
+	}
 	debugC(1, kFreescapeDebugCode, "Printing message %d: \"%s\"", index, _messagesList[index].c_str());
 	insertTemporaryMessage(_messagesList[index], _countdown - 3);
 }
diff --git a/engines/freescape/games/castle/castle.h b/engines/freescape/games/castle/castle.h
index 207c0ec9b5f..8a0d150b367 100644
--- a/engines/freescape/games/castle/castle.h
+++ b/engines/freescape/games/castle/castle.h
@@ -158,6 +158,13 @@ public:
 	Common::Array<Graphics::ManagedSurface *> loadFramesWithHeaderCPCIndexed(Common::SeekableReadStream *file, int pos, int numFrames);
 	void updateCPCSpritesPalette();
 
+	Common::String _notEnoughRoomMessage;
+	Common::String _tooWeakMessage;
+	Common::String _crawlSelectedMessage;
+	Common::String _walkSelectedMessage;
+	Common::String _runSelectedMessage;
+	Common::String _ghostInAreaMessage;
+
 	Common::Array<byte> _modData; // Embedded ProTracker module (Amiga demo)
 	Common::Array<int> _keysCollected;
 	bool _useRockTravel;
diff --git a/engines/freescape/games/castle/dos.cpp b/engines/freescape/games/castle/dos.cpp
index 19bccb8e23f..19c543f8e2f 100644
--- a/engines/freescape/games/castle/dos.cpp
+++ b/engines/freescape/games/castle/dos.cpp
@@ -466,7 +466,7 @@ void CastleEngine::drawDOSUI(Graphics::Surface *surface) {
 	} else {
 		if (_gameStateControl != kFreescapeGameStateEnd) {
 			if (ghostInArea())
-				drawStringInSurface(_messagesList[116], 97, 182, front, back, surface);
+				drawStringInSurface(_ghostInAreaMessage, 97, 182, front, back, surface);
 			else
 				drawStringInSurface(_currentArea->_name, 97, 182, front, back, surface);
 		}


Commit: eb35932c375cecc1abdbcc518337f19e965e0811
    https://github.com/scummvm/scummvm/commit/eb35932c375cecc1abdbcc518337f19e965e0811
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-25T21:12:21+01:00

Commit Message:
FREESCAPE: fixed original typos in castle dos spanish

Changed paths:
    engines/freescape/games/castle/castle.cpp


diff --git a/engines/freescape/games/castle/castle.cpp b/engines/freescape/games/castle/castle.cpp
index 46ea7687a0c..44eaa4cf2c3 100644
--- a/engines/freescape/games/castle/castle.cpp
+++ b/engines/freescape/games/castle/castle.cpp
@@ -695,6 +695,14 @@ void CastleEngine::initGameState() {
 		_ghostInAreaMessage = _messagesList[116];
 	}
 
+	// Fix typos in the original Spanish releases
+	if (_language == Common::ES_ESP) {
+		for (uint i = 0; i < _messagesList.size(); i++) {
+			Common::replace(_messagesList[i], "ELIGIDO", "ELEGIDO");
+			Common::replace(_messagesList[i], "BRILLIANTE", "BRILLANTE");
+		}
+	}
+
 	_gameStateVars[k8bitVariableShield] = 16;
 	_gameStateVars[k8bitVariableEnergy] = 1;
 	_gameStateVars[8] = 128; // -1


Commit: 4098630c938652503f5e57bd83415f69d8da2092
    https://github.com/scummvm/scummvm/commit/4098630c938652503f5e57bd83415f69d8da2092
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-25T21:12:21+01:00

Commit Message:
FREESCAPE: avoid crash in castle zx

Changed paths:
    engines/freescape/games/castle/castle.cpp


diff --git a/engines/freescape/games/castle/castle.cpp b/engines/freescape/games/castle/castle.cpp
index 44eaa4cf2c3..11d6ade40d7 100644
--- a/engines/freescape/games/castle/castle.cpp
+++ b/engines/freescape/games/castle/castle.cpp
@@ -686,13 +686,21 @@ void CastleEngine::initGameState() {
 		_walkSelectedMessage = _messagesList[24];
 		_runSelectedMessage = _messagesList[25];
 		_ghostInAreaMessage = _messagesList[126];
-	} else {
+	} else if (isDOS()) {
 		_notEnoughRoomMessage = _messagesList[11];
 		_tooWeakMessage = _messagesList[12];
 		_crawlSelectedMessage = _messagesList[13];
 		_walkSelectedMessage = _messagesList[14];
 		_runSelectedMessage = _messagesList[15];
 		_ghostInAreaMessage = _messagesList[116];
+	} else {
+		// ZX/CPC: same indices for movement messages, no ghost warning message
+		_notEnoughRoomMessage = _messagesList[11];
+		_tooWeakMessage = _messagesList[12];
+		_crawlSelectedMessage = _messagesList[13];
+		_walkSelectedMessage = _messagesList[14];
+		_runSelectedMessage = _messagesList[15];
+		_ghostInAreaMessage = "";
 	}
 
 	// Fix typos in the original Spanish releases


Commit: 2813abb296f601945befb1103c8bf9ef9352f2e6
    https://github.com/scummvm/scummvm/commit/2813abb296f601945befb1103c8bf9ef9352f2e6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-25T21:12:21+01:00

Commit Message:
FREESCAPE: better touchscreen integration and debug code

Changed paths:
    engines/freescape/freescape.cpp
    engines/freescape/freescape.h
    engines/freescape/games/castle/castle.cpp
    engines/freescape/games/driller/driller.cpp
    engines/freescape/ui.cpp


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index d8ee03c26bc..d97a10aa3de 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -135,6 +135,8 @@ FreescapeEngine::FreescapeEngine(OSystem *syst, const ADGameDescription *gd)
 	if (ConfMan.hasKey("wasd_controls"))
 		Common::parseBool(ConfMan.get("wasd_controls"), _useWASDControls);
 
+	_debugSimulateTouchscreen = ConfMan.hasKey("debug_touchscreen") && ConfMan.getBool("debug_touchscreen");
+
 	if (isDriller() || isSpaceStationOblivion() || isDark())
 		_smoothMovement = false;
 
@@ -280,6 +282,10 @@ FreescapeEngine::FreescapeEngine(OSystem *syst, const ADGameDescription *gd)
 	setDebugger(g_debugger);
 }
 
+bool FreescapeEngine::isTouchscreenActive() const {
+	return _debugSimulateTouchscreen || g_system->hasFeature(OSystem::kFeatureTouchscreen);
+}
+
 FreescapeEngine::~FreescapeEngine() {
 	removeTimers();
 	delete _rnd;
@@ -843,13 +849,8 @@ Common::Error FreescapeEngine::run() {
 	loadAssets();
 	loadColorPalette();
 
-	if (g_system->getFeatureState(OSystem::kFeatureTouchscreen)) {
-		g_system->showMouse(true);
-		g_system->lockMouse(false);
-	} else {
-		g_system->showMouse(false);
-		g_system->lockMouse(true);
-	}
+	g_system->showMouse(false);
+	g_system->lockMouse(true);
 
 	// Simple main event loop
 	int saveSlot = ConfMan.getInt("save_slot");
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 6f191661109..61ad126f2a4 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -354,6 +354,8 @@ public:
 
 	bool _smoothMovement;
 	bool _useWASDControls;
+	bool _debugSimulateTouchscreen;
+	bool isTouchscreenActive() const;
 	// Player movement state
 	bool _moveForward;
 	bool _moveBackward;
diff --git a/engines/freescape/games/castle/castle.cpp b/engines/freescape/games/castle/castle.cpp
index 11d6ade40d7..4ba98a56eda 100644
--- a/engines/freescape/games/castle/castle.cpp
+++ b/engines/freescape/games/castle/castle.cpp
@@ -1574,7 +1574,7 @@ void CastleEngine::drawFullscreenRiddleAndWait(uint16 riddle) {
 			case Common::EVENT_RBUTTONDOWN:
 				// fallthrough
 			case Common::EVENT_LBUTTONDOWN:
-				if (g_system->hasFeature(OSystem::kFeatureTouchscreen))
+				if (isTouchscreenActive())
 					cont = false;
 				break;
 			default:
@@ -1994,10 +1994,47 @@ void CastleEngine::selectCharacterScreen() {
 		drawFullscreenSurface(surface);
 	}
 
+	if (isTouchscreenActive()) {
+		CursorMan.setDefaultArrowCursor();
+		CursorMan.showMouse(true);
+	}
 	_system->lockMouse(false);
 	_system->showMouse(true);
-	Common::Rect princeSelector(82, 100, 163, 109);
-	Common::Rect princessSelector(82, 110, 181, 120);
+
+	// Calculate tap/click rectangles from actual rendered text positions.
+	// lines[5] = prince, lines[6] = princess for ZX/CPC.
+	// For DOS, use riddle text line positions.
+	Common::Rect princeSelector, princessSelector;
+	if (isSpectrum() || isCPC()) {
+		int x = _viewArea.left + 3;
+		int lineHeight = 12; // Castle Master line spacing in drawStringsInSurface
+		int princeY = _viewArea.top + 3 + 5 * lineHeight;
+		int princessY = _viewArea.top + 3 + 6 * lineHeight;
+		// Use the full padded line (what's actually rendered on screen)
+		princeSelector = _font.getBoundingBox(lines[5], x, princeY);
+		princessSelector = _font.getBoundingBox(lines[6], x, princessY);
+	} else {
+		// DOS: text comes from _riddleList[21], calculate from actual riddle line positions
+		Common::Array<RiddleText> selectMessage = _riddleList[21]._lines;
+		int x = 0, y = 0;
+		for (int i = 0; i < int(selectMessage.size()); i++) {
+			x += selectMessage[i]._dx;
+			y += selectMessage[i]._dy;
+			if (i == int(selectMessage.size()) - 2)
+				princeSelector = _font.getBoundingBox(selectMessage[i]._text, x, y);
+			else if (i == int(selectMessage.size()) - 1)
+				princessSelector = _font.getBoundingBox(selectMessage[i]._text, x, y);
+		}
+	}
+
+	// On touchscreen, highlight the tap areas with red outlines (expand 1px for readability)
+	princeSelector.grow(1);
+	princessSelector.grow(1);
+	if (isTouchscreenActive()) {
+		uint32 red = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0xFF, 0x00, 0x00);
+		surface->frameRect(princeSelector, red);
+		surface->frameRect(princessSelector, red);
+	}
 
 	bool selected = false;
 	while (!selected) {
@@ -2010,7 +2047,7 @@ void CastleEngine::selectCharacterScreen() {
 				quitGame();
 				return;
 
-			// Left mouse click
+			// Left mouse click or touchscreen tap
 			case Common::EVENT_LBUTTONDOWN:
 				// fallthrough
 			case Common::EVENT_RBUTTONDOWN:
@@ -2057,6 +2094,7 @@ void CastleEngine::selectCharacterScreen() {
 	}
 	_system->lockMouse(true);
 	_system->showMouse(false);
+	CursorMan.showMouse(false);
 	_gfx->clear(0, 0, 0, true);
 
 }
diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index fa4691b37a5..0075625553c 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -474,7 +474,7 @@ void DrillerEngine::drawInfoMenu() {
 			case Common::EVENT_RBUTTONDOWN:
 			// fallthrough
 			case Common::EVENT_LBUTTONDOWN:
-				if (g_system->hasFeature(OSystem::kFeatureTouchscreen))
+				if (isTouchscreenActive())
 					cont = false;
 				break;
 			default:
diff --git a/engines/freescape/ui.cpp b/engines/freescape/ui.cpp
index d4712ff1898..eff56e7b54a 100644
--- a/engines/freescape/ui.cpp
+++ b/engines/freescape/ui.cpp
@@ -130,7 +130,7 @@ void FreescapeEngine::titleScreen() {
 			case Common::EVENT_RBUTTONDOWN:
 			// fallthrough
 			case Common::EVENT_LBUTTONDOWN:
-				if (g_system->hasFeature(OSystem::kFeatureTouchscreen))
+				if (isTouchscreenActive())
 					maxWait = -1;
 				break;
 			default:
@@ -337,7 +337,7 @@ void FreescapeEngine::drawBorderScreenAndWait(Graphics::Surface *surface, int ma
 			case Common::EVENT_RBUTTONDOWN:
 			// fallthrough
 			case Common::EVENT_LBUTTONDOWN:
-				if (g_system->hasFeature(OSystem::kFeatureTouchscreen))
+				if (isTouchscreenActive())
 					maxWait = -1;
 				break;
 			default:


Commit: 0c102e65e2cb375b3d3d4156900a7d4b7714a655
    https://github.com/scummvm/scummvm/commit/0c102e65e2cb375b3d3d4156900a7d4b7714a655
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-25T21:12:21+01:00

Commit Message:
FREESCAPE: added pos and area commands into the debugger

Changed paths:
    engines/freescape/area.h
    engines/freescape/debugger.cpp
    engines/freescape/debugger.h


diff --git a/engines/freescape/area.h b/engines/freescape/area.h
index 30d5406fb55..fba98d02378 100644
--- a/engines/freescape/area.h
+++ b/engines/freescape/area.h
@@ -51,6 +51,8 @@ public:
 	ObjectArray getSensors();
 	uint16 getAreaID();
 	Common::Array<Object *> &getSortedObjects() { return _sortedObjects; }
+	ObjectMap *getObjectsByID() { return _objectsByID; }
+	ObjectMap *getEntrancesByID() { return _entrancesByID; }
 	uint16 getAreaFlags();
 	uint8 getScale();
 	void remapColor(int index, int color);
diff --git a/engines/freescape/debugger.cpp b/engines/freescape/debugger.cpp
index 7f05fa6433e..15dd7d0bff9 100644
--- a/engines/freescape/debugger.cpp
+++ b/engines/freescape/debugger.cpp
@@ -42,6 +42,8 @@ Debugger::Debugger(FreescapeEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("goto", WRAP_METHOD(Debugger, cmdGoto)); // teleport to a position
 	registerCmd("sort_order", WRAP_METHOD(Debugger, cmdSortOrder)); // print current draw order of objects
 	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
 }
 
 Debugger::~Debugger() {}
@@ -184,8 +186,8 @@ bool Debugger::cmdSortOrder(int argc, const char** argv) {
 
 	Common::Array<Object *> &objs = _vm->_currentArea->getSortedObjects();
 
-	debugPrintf("Current Draw Order:\n");
-	debugPrintf("Total Objects: %d\n", objs.size());
+	debugPrintf("Draw order (back-to-front, first drawn first):\n");
+	debugPrintf("Total visible objects: %d\n", objs.size());
 	for (uint i = 0; i < objs.size(); i++) {
 		Object *obj = objs[i];
 		debugPrintf("%d ", obj->getObjectID());
@@ -203,4 +205,86 @@ bool Debugger::cmdShowOcclusion(int argc, const char **argv) {
 	return true;
 }
 
+static const char *objectTypeNames[] = {
+	"Entrance",
+	"Cube",
+	"Sensor",
+	"Rectangle",
+	"EastPyramid",
+	"WestPyramid",
+	"UpPyramid",
+	"DownPyramid",
+	"NorthPyramid",
+	"SouthPyramid",
+	"Line",
+	"Triangle",
+	"Quadrilateral",
+	"Pentagon",
+	"Hexagon",
+	"Group"
+};
+
+bool Debugger::cmdArea(int argc, const char **argv) {
+	if (!_vm->_currentArea) {
+		debugPrintf("No area loaded.\n");
+		return true;
+	}
+
+	Area *area = _vm->_currentArea;
+	debugPrintf("Area ID: %d\n", area->getAreaID());
+	debugPrintf("Area name: %s\n", area->_name.c_str());
+	debugPrintf("Area flags: %04x\n", area->getAreaFlags());
+	debugPrintf("Scale: %d\n", area->getScale());
+	debugPrintf("Sky color: %d | Ground color: %d\n", area->_skyColor, area->_groundColor);
+	debugPrintf("Ink: %d | Paper: %d\n", area->_inkColor, area->_paperColor);
+	debugPrintf("Color cycling: %s\n", area->_colorCycling ? "yes" : "no");
+	debugPrintf("Outside: %s\n", area->isOutside() ? "yes" : "no");
+	debugPrintf("\n");
+
+	ObjectMap *objectsByID = area->getObjectsByID();
+	debugPrintf("Objects (%d):\n", objectsByID->size());
+	for (auto &it : *objectsByID) {
+		Object *obj = it._value;
+		int type = obj->getType();
+		const char *typeName = (type >= 0 && type <= 15) ? objectTypeNames[type] : "Unknown";
+		debugPrintf("  ID: %3d | Type: %-14s | Flags: %04x", obj->getObjectID(), typeName, obj->getObjectFlags());
+		if (obj->isInvisible()) {
+			debugPrintf(" [invisible]");
+		}
+		if (obj->isDestroyed()) {
+			debugPrintf(" [destroyed]");
+		}
+		if (obj->isGeometric()) {
+			Math::Vector3d origin = obj->getOrigin();
+			Math::Vector3d size = obj->getSize();
+			debugPrintf(" | Pos: (%.0f, %.0f, %.0f) Size: (%.0f, %.0f, %.0f)",
+				origin.x(), origin.y(), origin.z(),
+				size.x(), size.y(), size.z());
+		}
+		debugPrintf("\n");
+	}
+
+	ObjectMap *entrancesByID = area->getEntrancesByID();
+	if (entrancesByID->size() > 0) {
+		debugPrintf("\nEntrances (%d):\n", entrancesByID->size());
+		for (auto &it : *entrancesByID) {
+			Object *obj = it._value;
+			Math::Vector3d origin = obj->getOrigin();
+			debugPrintf("  ID: %3d | Pos: (%.0f, %.0f, %.0f)\n",
+				obj->getObjectID(), origin.x(), origin.y(), origin.z());
+		}
+	}
+
+	return true;
+}
+
+bool Debugger::cmdPos(int argc, const char **argv) {
+	Math::Vector3d pos = _vm->_position;
+	Math::Vector3d front = _vm->_cameraFront;
+	debugPrintf("Position: (%.2f, %.2f, %.2f)\n", pos.x(), pos.y(), pos.z());
+	debugPrintf("Direction: (%.2f, %.2f, %.2f)\n", front.x(), front.y(), front.z());
+	debugPrintf("Yaw: %.2f | Pitch: %.2f | Roll: %d\n", _vm->_yaw, _vm->_pitch, _vm->_roll);
+	return true;
+}
+
 }
diff --git a/engines/freescape/debugger.h b/engines/freescape/debugger.h
index 1c7c549e166..fd10ae47d33 100644
--- a/engines/freescape/debugger.h
+++ b/engines/freescape/debugger.h
@@ -46,6 +46,8 @@ private:
 	bool cmdSetObjPos(int argc, const char **argv);
 	bool cmdSortOrder(int argc, const char **argv);
 	bool cmdShowOcclusion(int argc, const char **argv);
+	bool cmdArea(int argc, const char **argv);
+	bool cmdPos(int argc, const char **argv);
 };
 
 }


Commit: 69be41bc2e05081d8889483c18989a8a82099d35
    https://github.com/scummvm/scummvm/commit/69be41bc2e05081d8889483c18989a8a82099d35
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-25T21:12:21+01:00

Commit Message:
FREESCAPE: presort by distance from camera (furthest first) to provide a stable initial ordering in depth computation

Changed paths:
    engines/freescape/area.cpp


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index e25e31cffc0..cfa95dd4fbc 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -327,6 +327,17 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 	// It is only applied to the vertices during the projection phase (L850f/L9177).
 	int n = _sortedObjects.size();
 	if (n > 1 && sort) {
+		// Pre-sort by distance from camera (furthest first) to provide a stable initial
+		// ordering for the non-transitive bubble sort below. The original game achieves
+		// this by culling off-screen objects via a rendering volume check (L8bb7/L845b)
+		// before sorting, which prevents distant off-screen objects from interfering with
+		// the depth ordering of visible objects through non-transitive comparisons.
+		Common::sort(_sortedObjects.begin(), _sortedObjects.end(),
+			[&camera](Object *a, Object *b) {
+				Math::Vector3d centerA = (a->_occlusionBox.getMin() + a->_occlusionBox.getMax()) * 0.5f;
+				Math::Vector3d centerB = (b->_occlusionBox.getMin() + b->_occlusionBox.getMax()) * 0.5f;
+				return (centerA - camera).getSquareMagnitude() > (centerB - camera).getSquareMagnitude();
+			});
 		for (int i = 0; i < n; i++) { // L9c31_whole_object_pass_loop
 			bool changed = false;
 			for (int j = 0; j < n - 1; j++) { // L9c45_objects_loop




More information about the Scummvm-git-logs mailing list