[Scummvm-git-logs] scummvm master -> 2fd664020d16e5d6e9e687791ed4978d98b7ab90

neuromancer noreply at scummvm.org
Sun Jul 5 09:43:06 UTC 2026


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

Summary:
f0b2600883 FREESCAPE: fixed incorrect color in ZX UI for driller/dark
a92e02d1b4 FREESCAPE: fixed incorrect sky color in Amiga/Atari for dark
453681fb76 FREESCAPE: added a workaround for wall/camera clipping issues in castle
2fd664020d FREESCAPE: fixed isOutside for castle


Commit: f0b2600883d792774ec929ba52d39b198e6a554e
    https://github.com/scummvm/scummvm/commit/f0b2600883d792774ec929ba52d39b198e6a554e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-05T11:29:58+02:00

Commit Message:
FREESCAPE: fixed incorrect color in ZX UI for driller/dark

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


diff --git a/engines/freescape/games/dark/dark.cpp b/engines/freescape/games/dark/dark.cpp
index 1bb05238581..c1eab0dc025 100644
--- a/engines/freescape/games/dark/dark.cpp
+++ b/engines/freescape/games/dark/dark.cpp
@@ -926,6 +926,9 @@ void DarkEngine::drawHorizontalCompass(int x, int y, float angle, uint32 front,
 		uint8 r, g, b;
 		_gfx->selectColorFromFourColorPalette(3, r, g, b);
 		green = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
+	} else if (isSpectrum()) {
+		// The ZX HUD uses a single ink color for all the text, including the compass.
+		green = front;
 	}
 
 	int delta = (angle - 180) / 5.5;


Commit: a92e02d1b444efcdc90bc0151aaa5cec459bb95b
    https://github.com/scummvm/scummvm/commit/a92e02d1b444efcdc90bc0151aaa5cec459bb95b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-05T11:29:58+02:00

Commit Message:
FREESCAPE: fixed incorrect sky color in Amiga/Atari for dark

Changed paths:
    engines/freescape/loaders/8bitBinaryLoader.cpp


diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index a604be0ebc0..0e8391c5e34 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -747,6 +747,10 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 			name = name + char(readField(file, 8));
 			i++;
 		}
+
+		// The 16-bit renderer keeps the hardware background black; the sky nibble is 8-bit legacy data.
+		if (isDark() && (isAmiga() || isAtariST()))
+			skyColor = 0;
 	} else if (isCastle()) {
 		byte idx = readField(file, 8);
 		if (isAmiga() || isAtariST())


Commit: 453681fb7678ce7e553f7c24053bdbe3b61bfc27
    https://github.com/scummvm/scummvm/commit/453681fb7678ce7e553f7c24053bdbe3b61bfc27
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-05T11:29:58+02:00

Commit Message:
FREESCAPE: added a workaround for wall/camera clipping issues in castle

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


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 144db15a0f9..7b6894c7c32 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -836,6 +836,47 @@ Math::Vector3d Area::separateFromWall(const Math::Vector3d &_position) {
 	return position;
 }
 
+// Render-only: nudge the eye at least `separation` away from wall sides so they never cross the near plane.
+Math::Vector3d Area::separateCameraFromWall(const Math::Vector3d &eye, float separation) {
+	Math::Vector3d cam = eye;
+	for (int pass = 0; pass < 4; pass++) { // corners: leaving one wall's band can enter another's
+		bool adjusted = false;
+		for (auto &obj : _drawableObjects) {
+			if (obj->isDestroyed() || obj->isInvisible() || !obj->isGeometric())
+				continue;
+			const Math::AABB &box = ((GeometricObject *)obj)->_boundingBox;
+			if (!box.isValid())
+				continue;
+			Math::Vector3d mn = box.getMin();
+			Math::Vector3d mx = box.getMax();
+			if (cam.y() <= mn.y() || cam.y() >= mx.y()) // only walls at eye level can clip the view
+				continue;
+
+			float dx = cam.x() - CLIP<float>(cam.x(), mn.x(), mx.x());
+			float dz = cam.z() - CLIP<float>(cam.z(), mn.z(), mx.z());
+			float dist = sqrtf(dx * dx + dz * dz);
+			if (dist >= separation)
+				continue;
+
+			if (dist > 0.0001f) { // in the band: push straight out
+				cam.x() += dx / dist * (separation - dist);
+				cam.z() += dz / dist * (separation - dist);
+			} else { // inside the footprint: eject through the nearest side
+				float left = cam.x() - mn.x(), right = mx.x() - cam.x();
+				float back = cam.z() - mn.z(), front = mx.z() - cam.z();
+				if (MIN(left, right) <= MIN(back, front))
+					cam.x() += (left < right) ? -(left + separation) : (right + separation);
+				else
+					cam.z() += (back < front) ? -(back + separation) : (front + separation);
+			}
+			adjusted = true;
+		}
+		if (!adjusted)
+			break;
+	}
+	return cam;
+}
+
 Math::Vector3d Area::resolveCollisions(const Math::Vector3d &lastPosition_, const Math::Vector3d &newPosition_, int playerHeight) {
 	Math::Vector3d position = newPosition_;
 	Math::Vector3d lastPosition = lastPosition_;
diff --git a/engines/freescape/area.h b/engines/freescape/area.h
index 18604f84736..45b42c949db 100644
--- a/engines/freescape/area.h
+++ b/engines/freescape/area.h
@@ -72,6 +72,7 @@ public:
 	Object *checkCollisionRay(const Math::Ray &ray, int raySize, bool skipTransparent = false);
 	bool checkInSight(const Math::Ray &ray, float maxDistance);
 	Math::Vector3d separateFromWall(const Math::Vector3d &position);
+	Math::Vector3d separateCameraFromWall(const Math::Vector3d &eye, float separation);
 	ObjectArray checkCollisions(const Math::AABB &boundingBox);
 	bool checkIfPlayerWasCrushed(const Math::AABB &boundingBox);
 	Math::Vector3d resolveCollisions(Math::Vector3d const &lastPosition, Math::Vector3d const &newPosition, int playerHeight);
diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 81d5fb51276..e435cbc80b6 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -703,6 +703,13 @@ static float getStereoSeparation(FreescapeEngine *engine, float fov, float aspec
 	return MIN(maxSeparation, maxDisparity / denominator);
 }
 
+Math::Vector3d FreescapeEngine::getCameraRenderPosition() {
+	// Keep the eye off wall surfaces so the near plane never crosses them; collisions still use _position.
+	if (isCastle() && !_noClipMode)
+		return _currentArea->separateCameraFromWall(_position, _nearClipPlane + 1.0f);
+	return _position;
+}
+
 void FreescapeEngine::drawFrame() {
 	_gfx->updateColorCycling();
 	int farClipPlane = _farClipPlane;
@@ -716,8 +723,11 @@ void FreescapeEngine::drawFrame() {
 
 	const float fov = 75.0f;
 	float aspectRatio = isCastle() ? 1.6 : 2.18;
+
+	Math::Vector3d renderPosition = getCameraRenderPosition();
+
 	_gfx->updateProjectionMatrix(fov, aspectRatio, _nearClipPlane, farClipPlane);
-	_gfx->positionCamera(_position, _position + _cameraFront, _roll);
+	_gfx->positionCamera(renderPosition, renderPosition + _cameraFront, _roll);
 
 	if (_underFireFrames > 0) {
 		int underFireColor = _currentArea->_underFireBackgroundColor;
@@ -737,7 +747,7 @@ void FreescapeEngine::drawFrame() {
 
 	drawBackground();
 	if (_avoidRenderingFrames == 0) { // Avoid rendering inside objects
-		_currentArea->draw(_gfx, _ticks / 10, _position, _cameraFront, false, fov, aspectRatio, _nearClipPlane, farClipPlane);
+		_currentArea->draw(_gfx, _ticks / 10, renderPosition, _cameraFront, false, fov, aspectRatio, _nearClipPlane, farClipPlane);
 		if (_gameStateControl == kFreescapeGameStatePlaying &&
 		    _currentArea->hasActiveGroups() && _ticks % 50 == 0) {
 			executeMovementConditions();
@@ -779,6 +789,9 @@ void FreescapeEngine::drawFrame() {
 void FreescapeEngine::drawFrameStereo(int farClipPlane) {
 	const float fov = 75.0f;
 	float aspectRatio = isCastle() ? 1.6 : 2.18;
+
+	Math::Vector3d renderPosition = getCameraRenderPosition();
+
 	const float stereoConvergence = getStereoConvergence(this);
 	const float stereoMaxSeparation = getStereoMaxSeparation(this, stereoConvergence);
 	const float stereoForegroundDistance = getStereoForegroundDistance(this, stereoConvergence);
@@ -804,21 +817,21 @@ void FreescapeEngine::drawFrameStereo(int farClipPlane) {
 	_gfx->clear(0, 0, 0, true);
 	_gfx->setStereoEye(Renderer::kStereoEyeFlatAnaglyph);
 	_gfx->updateProjectionMatrix(fov, aspectRatio, _nearClipPlane, farClipPlane);
-	_gfx->positionCamera(_position, _position + _cameraFront, _roll);
+	_gfx->positionCamera(renderPosition, renderPosition + _cameraFront, _roll);
 
 	drawBackground();
 	if (_avoidRenderingFrames == 0)
-		_currentArea->drawDepthLayer(_gfx, _ticks / 10, _position, _cameraFront, false, Area::kRenderDepthBackground, stereoForegroundDistance, fov, aspectRatio, _nearClipPlane, farClipPlane);
+		_currentArea->drawDepthLayer(_gfx, _ticks / 10, renderPosition, _cameraFront, false, Area::kRenderDepthBackground, stereoForegroundDistance, fov, aspectRatio, _nearClipPlane, farClipPlane);
 
 	for (int pass = 0; pass < 2; pass++) {
 		_gfx->setStereoEye(pass == 0 ? Renderer::kStereoEyeLeft : Renderer::kStereoEyeRight);
 		_gfx->updateProjectionMatrix(fov, aspectRatio, _nearClipPlane, farClipPlane);
-		_gfx->positionCamera(_position, _position + _cameraFront, _roll);
+		_gfx->positionCamera(renderPosition, renderPosition + _cameraFront, _roll);
 
 		_gfx->clearDepthBuffer();
 
 		if (_avoidRenderingFrames == 0) // Avoid rendering inside objects
-			_currentArea->drawDepthLayer(_gfx, _ticks / 10, _position, _cameraFront, false, Area::kRenderDepthForeground, stereoForegroundDistance, fov, aspectRatio, _nearClipPlane, farClipPlane);
+			_currentArea->drawDepthLayer(_gfx, _ticks / 10, renderPosition, _cameraFront, false, Area::kRenderDepthForeground, stereoForegroundDistance, fov, aspectRatio, _nearClipPlane, farClipPlane);
 
 		if (_underFireFrames > 0) {
 			for (auto &it : _sensors) {
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 092ff619940..6ab4fa73526 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -563,6 +563,7 @@ public:
 	int _shootingFrames;
 	GeometricObject *_delayedShootObject;
 	void drawFrame();
+	Math::Vector3d getCameraRenderPosition();
 	void drawFrameStereo(int farClipPlane);
 
 	// Red/blue anaglyph 3D ("two eyes") effect, toggled with the 3 key.


Commit: 2fd664020d16e5d6e9e687791ed4978d98b7ab90
    https://github.com/scummvm/scummvm/commit/2fd664020d16e5d6e9e687791ed4978d98b7ab90
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-05T11:29:58+02:00

Commit Message:
FREESCAPE: fixed isOutside for castle

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


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 7b6894c7c32..61894ad188c 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -68,6 +68,7 @@ Area::Area(uint16 areaID_, uint16 areaFlags_, ObjectMap *objectsByID_, ObjectMap
 	_isCastle = isCastle_;
 
 	_scale = 0;
+	_hasSyntheticFloor = false;
 	_skyColor = 255;
 	_groundColor = 255;
 	_usualBackgroundColor = 255;
@@ -1021,6 +1022,7 @@ void Area::addGroupFromArea(int16 id, Area *global) {
 
 
 void Area::addFloor() {
+	_hasSyntheticFloor = true;
 	int id = 0;
 	assert(!_objectsByID->contains(id));
 	Common::Array<uint8> *gColors = new Common::Array<uint8>;
@@ -1071,6 +1073,9 @@ void Area::changeObjectID(uint16 objectID, uint16 newObjectID) {
 
 
 bool Area::isOutside() {
+	// Castle outdoor areas are exactly the ones that get the synthetic floor (Wilderness and Courtyard).
+	if (_isCastle)
+		return _hasSyntheticFloor;
 	return _skyColor < 255 && _groundColor < 255;
 }
 
diff --git a/engines/freescape/area.h b/engines/freescape/area.h
index 45b42c949db..0e1d908cc7c 100644
--- a/engines/freescape/area.h
+++ b/engines/freescape/area.h
@@ -135,6 +135,7 @@ private:
 
 	uint16 _areaID;
 	uint16 _areaFlags;
+	bool _hasSyntheticFloor;
 	ObjectMap *_objectsByID;
 	ObjectMap *_entrancesByID;
 	ObjectArray _drawableObjects;




More information about the Scummvm-git-logs mailing list