[Scummvm-git-logs] scummvm master -> 0befed2f78418f3d30d171d00cabd9ba3b3f656f

neuromancer noreply at scummvm.org
Fri Jun 5 11:24:56 UTC 2026


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

Summary:
4031c5786f COLONY: mouse is less sensitive when turning
efe59024d9 COLONY: do not crash when calling glDepthRangef in desktop
a731f96b70 COLONY: added missing sounds
e3981b3175 COLONY: refined mac b&w interface and refactored some related code
9b0fb92042 COLONY: fixed xor mode in shader rendering
d541fc5d0e COLONY: complete rendering of mountain parallax
0befed2f78 COLONY: make sure the battle coordinates are normalized before starting


Commit: 4031c5786f456add3413cc8071ed1fc9ac6ce718
    https://github.com/scummvm/scummvm/commit/4031c5786f456add3413cc8071ed1fc9ac6ce718
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: mouse is less sensitive when turning

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 2b736f884ab..3748d3d8dda 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -53,6 +53,9 @@ namespace Colony {
 
 namespace {
 
+const float kMouseLookSensitivity = 0.25f;
+const float kKeyboardTurnSpeed = 30.0f; // angle units per second
+
 class OwnedCursor final : public Graphics::Cursor {
 public:
 	OwnedCursor(uint16 width, uint16 height, uint16 hotspotX, uint16 hotspotY, byte keyColor,
@@ -139,7 +142,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_height = 350;
 	_centerX = _width / 2;
 	_centerY = _height / 2;
-	_mouseSensitivity = 1;
+	_mouseSensitivity = kMouseLookSensitivity;
 	_mouseLocked = false;
 	_mousePos = Common::Point(_centerX, _centerY);
 	_showDashBoard = true;
@@ -841,6 +844,7 @@ Common::Error ColonyEngine::run() {
 	updateMouseCapture(true);
 
 	int mouseDX = 0, mouseDY = 0;
+	float mouseLookAccumX = 0.0f, mouseLookAccumY = 0.0f;
 	bool mouseMoved = false;
 	uint32 lastMoveTick = _system->getMillis();
 	uint32 lastColonyTick = lastMoveTick;
@@ -930,6 +934,7 @@ Common::Error ColonyEngine::run() {
 					updateMouseCapture(true);
 					if (_mouseLocked) {
 						mouseDX = mouseDY = 0;
+						mouseLookAccumX = mouseLookAccumY = 0.0f;
 						mouseMoved = false;
 					}
 				}
@@ -995,6 +1000,7 @@ Common::Error ColonyEngine::run() {
 						updateMouseCapture(true);
 						if (_mouseLocked) {
 							mouseDX = mouseDY = 0;
+							mouseLookAccumX = mouseLookAccumY = 0.0f;
 							mouseMoved = false;
 						}
 					}
@@ -1009,6 +1015,9 @@ Common::Error ColonyEngine::run() {
 					openMainMenuDialog();
 					_gfx->computeScreenViewport();
 					updateMouseCapture(true);
+					mouseDX = mouseDY = 0;
+					mouseLookAccumX = mouseLookAccumY = 0.0f;
+					mouseMoved = false;
 					break;
 				default:
 					break;
@@ -1073,13 +1082,27 @@ Common::Error ColonyEngine::run() {
 
 		if (mouseMoved && _mouseLocked) {
 			if (mouseDX != 0) {
-				_me.look = (uint8)((int)_me.look - (mouseDX * _mouseSensitivity));
-				// In battle mode, body always faces look direction
-				if (_gameMode == kModeBattle)
-					_me.ang = _me.look;
+				mouseLookAccumX -= mouseDX * _mouseSensitivity;
+				const int lookDelta = (int)mouseLookAccumX;
+				if (lookDelta != 0) {
+					mouseLookAccumX -= lookDelta;
+					_me.look = (uint8)((int)_me.look + lookDelta);
+					// In battle mode, body always faces look direction
+					if (_gameMode == kModeBattle)
+						_me.ang = _me.look;
+				}
 			}
-			if (mouseDY != 0 && !_fl) {
-				_me.lookY = (int8)CLIP<int>((int)_me.lookY - (mouseDY * _mouseSensitivity), -64, 64);
+			if (mouseDY != 0) {
+				if (!_fl) {
+					mouseLookAccumY -= mouseDY * _mouseSensitivity;
+					const int lookYDelta = (int)mouseLookAccumY;
+					if (lookYDelta != 0) {
+						mouseLookAccumY -= lookYDelta;
+						_me.lookY = (int8)CLIP<int>((int)_me.lookY + lookYDelta, -64, 64);
+					}
+				} else {
+					mouseLookAccumY = 0.0f;
+				}
 			}
 			// Warp back to center and purge remaining mouse events
 			// to prevent the warp from generating phantom deltas (Freescape pattern)
@@ -1145,7 +1168,7 @@ Common::Error ColonyEngine::run() {
 			}
 
 			if (_rotateLeft || _rotateRight) {
-				const float rotSpeed = (float)(1 << (_speedShift - 1)) * 15.0f;
+				const float rotSpeed = kKeyboardTurnSpeed;
 				_rotAccum += (_rotateLeft ? rotSpeed : -rotSpeed) * dt;
 				const int rint = (int)_rotAccum;
 				_rotAccum -= rint;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 5e41698308a..c933b6ec992 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -507,7 +507,7 @@ private:
 	int _cost[256];
 	int _centerX, _centerY;
 	int _width, _height;
-	int _mouseSensitivity;
+	float _mouseSensitivity;
 	bool _mouseLocked;
 	bool _soundOn = true;
 	bool _showDashBoard;


Commit: efe59024d91aa031c0d1369530498dc86e03a968
    https://github.com/scummvm/scummvm/commit/efe59024d91aa031c0d1369530498dc86e03a968
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: do not crash when calling glDepthRangef in desktop

Changed paths:
    engines/colony/renderer_opengl_shaders.cpp


diff --git a/engines/colony/renderer_opengl_shaders.cpp b/engines/colony/renderer_opengl_shaders.cpp
index eab77613daa..0639d28f5da 100644
--- a/engines/colony/renderer_opengl_shaders.cpp
+++ b/engines/colony/renderer_opengl_shaders.cpp
@@ -106,6 +106,7 @@ private:
 	void applyLineWidth(GLenum mode);
 
 	void uploadSolid3D(const float *positions, int vertCount);
+	void setGLDepthRange(float nearVal, float farVal);
 	// allowStipple=true on the fill pass picks up _stippleActive; lines
 	// must always render unstippled (matches the fixed-function path,
 	// which only stipples GL_QUADS / GL_POLYGON, never lines).
@@ -692,8 +693,18 @@ void OpenGLShaderRenderer::setDepthState(bool testEnabled, bool writeEnabled) {
 	glDepthMask(writeEnabled ? GL_TRUE : GL_FALSE);
 }
 
-void OpenGLShaderRenderer::setDepthRange(float nearVal, float farVal) {
+void OpenGLShaderRenderer::setGLDepthRange(float nearVal, float farVal) {
+#if USE_FORCED_GLES || USE_FORCED_GLES2
 	glDepthRangef(nearVal, farVal);
+#else
+	// glDepthRangef is only core in desktop OpenGL 4.1+. The double-precision
+	// entry point is core since OpenGL 1.0, so use it for desktop GL contexts.
+	glDepthRange(nearVal, farVal);
+#endif
+}
+
+void OpenGLShaderRenderer::setDepthRange(float nearVal, float farVal) {
+	setGLDepthRange(nearVal, farVal);
 }
 
 void OpenGLShaderRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY,
@@ -767,7 +778,7 @@ void OpenGLShaderRenderer::begin3D(int camX, int camY, int camZ, int angle, int
 void OpenGLShaderRenderer::end3D() {
 	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
-	glDepthRangef(0.0f, 1.0f);
+	setGLDepthRange(0.0f, 1.0f);
 	glDisable(GL_SCISSOR_TEST);
 
 	// Restore the 2D viewport so subsequent overlay draws (dashboard, menu,


Commit: a731f96b706d0dd933c9d0d811ce86bd858294b5
    https://github.com/scummvm/scummvm/commit/a731f96b706d0dd933c9d0d811ce86bd858294b5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: added missing sounds

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/movement.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 43efac8971c..180ae0df359 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -657,10 +657,9 @@ void ColonyEngine::playAnimation() {
 	_system->getEventManager()->purgeMouseEvents();
 	_system->getEventManager()->purgeKeyboardEvents();
 
-	// Suppress collision sound on the first few wall hits after animation exit.
+	// Suppress collision sound on the first wall hit after animation exit.
 	// The player is at a door/wall boundary and held movement keys will
-	// immediately trigger checkwall collisions that play kBang — which sounds
-	// like a spurious gunshot. The flag auto-clears on the first successful move.
+	// immediately trigger a collision sound. The flag auto-clears in cCommand().
 	_suppressCollisionSound = true;
 
 	deleteAnimation();
@@ -1254,6 +1253,9 @@ void ColonyEngine::handleTeleshowClick(int item) {
 }
 
 void ColonyEngine::handleKeypadClick(int item) {
+	if (item > 0 && item <= 12)
+		_sound->play(Sound::kDit);
+
 	if (item >= 1 && item <= 10) {
 		for (int i = 5; i >= 1; i--)
 			_animDisplay[i] = _animDisplay[i - 1];
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 3748d3d8dda..c6b5c121211 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -690,6 +690,7 @@ void ColonyEngine::startNewGame() {
 	_orbit = 0;
 	_allGrow = false;
 	_suppressCollisionSound = false;
+	_lastCollisionSoundTime = 0;
 	_action0 = 0;
 	_action1 = 0;
 	_creature = 0;
@@ -977,14 +978,17 @@ Common::Error ColonyEngine::run() {
 					_me.lookY = 0;
 					break;
 				case kActionToggleDashboard:
+					_sound->play(Sound::kDit);
 					_showDashBoard = !_showDashBoard;
 					break;
 				case kActionToggleWireframe:
+					_sound->play(Sound::kDit);
 					_wireframe = !_wireframe;
 					debugC(1, kColonyDebugRender, "Polyfill: %s", _wireframe ? "off (wireframe)" : "on (filled)");
 					break;
 				case kActionToggleFullscreen:
 					if (_macMenu) {
+						_sound->play(Sound::kDit);
 						_fullscreen = !_fullscreen;
 						_menuBarHeight = _fullscreen ? 0 : 20;
 						updateViewportLayout();
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index c933b6ec992..84a8d14ad0e 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -558,6 +558,7 @@ private:
 	uint32 _lastHotfootTime = 0;  // Time-gate for HOTFOOT damage (~8fps)
 	uint32 _lastAnimUpdate = 0;
 	uint32 _lastWarningChimeTime = 0;
+	uint32 _lastCollisionSoundTime = 0;
 	int _action0 = 0, _action1 = 0;
 	int _creature = 0;
 	bool _allGrow = false;
@@ -815,6 +816,7 @@ private:
 	Common::Rect readRect(Common::SeekableReadStreamEndian &file);
 	int whichSprite(const Common::Point &p);
 	void handleAnimationClick(int item);
+	void playCollisionSound();
 	void handleDeskClick(int item);
 	void handleVanityClick(int item);
 	void handleSlidesClick(int item);
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 9e7cfcbd87f..84c40706357 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -411,8 +411,6 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 					return r;
 			}
 			debugC(1, kColonyDebugMove, "Collision South at x=%d y=%d", pobject->xindex, yind2);
-			if (!_suppressCollisionSound)
-				_sound->play(Sound::kBang);
 			return -1;
 
 		}
@@ -427,8 +425,6 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 				return r;
 		}
 		debugC(1, kColonyDebugMove, "Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
-		if (!_suppressCollisionSound)
-			_sound->play(Sound::kBang);
 		return -1;
 
 	}
@@ -445,8 +441,6 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 					return r;
 			}
 			debugC(1, kColonyDebugMove, "Collision East at x=%d y=%d", xind2, pobject->yindex);
-			if (!_suppressCollisionSound)
-				_sound->play(Sound::kBang);
 			return -1;
 
 		}
@@ -461,8 +455,6 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 				return r;
 		}
 		debugC(1, kColonyDebugMove, "Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
-		if (!_suppressCollisionSound)
-			_sound->play(Sound::kBang);
 		return -1;
 
 	}
@@ -1128,13 +1120,33 @@ void ColonyEngine::checkCenter() {
 	}
 }
 
+void ColonyEngine::playCollisionSound() {
+	if (_suppressCollisionSound)
+		return;
+
+	const uint32 now = _system->getMillis();
+	if (_lastCollisionSoundTime != 0 && now - _lastCollisionSoundTime < 175)
+		return;
+
+	_lastCollisionSoundTime = now;
+	_sound->play(Sound::kBonk);
+}
+
 void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 		_robotArray[_me.xindex][_me.yindex] = 0;
 
+	const int oldXIndex = _me.xindex;
+	const int oldYIndex = _me.yindex;
+	const bool sameCellAttempt = ((xnew >> 8) == oldXIndex && (ynew >> 8) == oldYIndex);
 	const int robot = checkwall(xnew, ynew, &_me);
 	if (robot > 0 && allowInteraction)
 		interactWithObject(robot);
+	else if (robot)
+		playCollisionSound();
+	else if (sameCellAttempt && _me.xindex == oldXIndex && _me.yindex == oldYIndex &&
+			(_me.xloc != xnew || _me.yloc != ynew))
+		playCollisionSound();
 
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 		_robotArray[_me.xindex][_me.yindex] = kMeNum;


Commit: e3981b3175121858b0b3fdac1ae674ae61eb9dd8
    https://github.com/scummvm/scummvm/commit/e3981b3175121858b0b3fdac1ae674ae61eb9dd8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: refined mac b&w interface and refactored some related code

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/interaction.cpp
    engines/colony/intro.cpp
    engines/colony/movement.cpp
    engines/colony/render.cpp
    engines/colony/render_features.cpp
    engines/colony/render_objects.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 180ae0df359..e04f5c6629b 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -398,7 +398,7 @@ void ColonyEngine::playAnimation() {
 	_system->lockMouse(false);
 	warpMouseLogical(_centerX, _centerY);
 	const char *cursorName = "default arrow cursor";
-	if (_renderMode == Common::kRenderMacintosh && _macArrowCursor) {
+	if (isMacRenderMode() && _macArrowCursor) {
 		cursorName = "Mac arrow cursor";
 		CursorMan.replaceCursor(_macArrowCursor);
 	} else {
@@ -713,7 +713,7 @@ void ColonyEngine::drawAnimation() {
 	ox = (ox / 8) * 8;
 	int oy = _screenR.top + (_screenR.height() - 264) / 2;
 
-	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh
+	const bool useColor = (isMacColorMode()
 		&& !_animBMColors.empty());
 
 	// Fill background patterns (416x264 area).
@@ -728,7 +728,7 @@ void ColonyEngine::drawAnimation() {
 	// did 416*264 = 109,824 individual setPixel calls per drawAnimation, each
 	// issuing its own glBegin/glEnd; on the OpenGL backend that took tens of
 	// milliseconds per frame and starved the cursor of refreshes.
-	const int patternMode = useColor ? 2 : (_renderMode == Common::kRenderMacintosh ? 1 : 0);
+	const int patternMode = useColor ? 2 : (isMacRenderMode() ? 1 : 0);
 	uint32 topColor = 0, botColor = 0;
 	if (useColor) {
 		const bool powered = (_corePower[_coreIndex] > 0);
@@ -833,7 +833,7 @@ void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
 
 	// Resolve fill color from BMColor[index+2] (ganimate.c DrawlSprite).
 	uint32 fillColor = 0xFFFFFFFF; // B&W default: white
-	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh
+	const bool useColor = (isMacColorMode()
 		&& !_animBMColors.empty());
 	if (useColor) {
 		int bmIdx = index + 2;
@@ -857,8 +857,8 @@ void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y, uin
 	//   fg bit=0   -> BackColor (fillColor from BMColor)
 	// Mac B&W: same — fg bit=1 is black (0), fg bit=0 is white (15).
 	// DOS MetaWINDOW: OPPOSITE — fg bit=1 is white (15), fg bit=0 is black (0).
-	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh);
-	const bool isMacMode = (_renderMode == Common::kRenderMacintosh);
+	const bool useColor = isMacColorMode();
+	const bool isMacMode = isMacRenderMode();
 
 	// Pixels written into the alpha-keyed RGBA cache. mask=0 → alpha 0
 	// (transparent), so drawSurface's alpha-blend skips them naturally.
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c6b5c121211..abdeed7de21 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -166,7 +166,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 			_renderMode = Common::kRenderEGA;
 	}
 
-	_wireframe = (_renderMode != Common::kRenderMacintosh);
+	_wireframe = !isMacRenderMode();
 	_fullscreen = false;
 	_speedShift = 2; // DOS default: speedshift=1, but 2 feels better with our frame rate
 	_moveForward = false;
@@ -487,7 +487,7 @@ void ColonyEngine::updateMouseCapture(bool recenter) {
 
 	int cursorMode = 0;
 
-	if (!_mouseLocked && _renderMode == Common::kRenderMacintosh && _wm) {
+	if (!_mouseLocked && isMacRenderMode() && _wm) {
 		if (_macCrossCursor) {
 			cursorMode = 1;
 			_wm->replaceCursor(Graphics::kMacCursorCustom, _macCrossCursor);
@@ -585,7 +585,7 @@ void ColonyEngine::handleMenuAction(int action) {
 }
 
 void ColonyEngine::initMacMenus() {
-	if (_renderMode != Common::kRenderMacintosh) {
+	if (!isMacRenderMode()) {
 		_menuBarHeight = 0;
 		return;
 	}
@@ -775,7 +775,7 @@ Common::Error ColonyEngine::run() {
 	// Original Mac Colony: rScreen capped at 640x480 (inits.c lines 111-112).
 	// DOS EGA: 640x350 with non-square pixels displayed at 4:3.
 	// Mac uses square pixels at native 640x480.
-	if (_renderMode == Common::kRenderMacintosh) {
+	if (isMacRenderMode()) {
 		_width = 640;
 		_height = 480;
 	} else {
@@ -791,6 +791,7 @@ Common::Error ColonyEngine::run() {
 	if (!_gfx)
 		return Common::kUserCanceled;
 
+	loadMacColors();
 	updateViewportLayout();
 	const Graphics::PixelFormat format = _system->getScreenFormat();
 	debugC(1, kColonyDebugRender, "Screen format: %d bytesPerPixel. Actual size: %dx%d", format.bytesPerPixel, _width, _height);
@@ -814,8 +815,7 @@ Common::Error ColonyEngine::run() {
 		pal[i * 3 + 2] = i;
 	}
 
-	loadMacColors();
-	if (_hasMacColors) {
+	if (isMacColorMode()) {
 		for (int i = 0; i < 145; i++) {
 			pal[(100 + i) * 3 + 0] = _macColors[i].fg[0] >> 8;
 			pal[(100 + i) * 3 + 1] = _macColors[i].fg[1] >> 8;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 84a8d14ad0e..b4be8d94fb9 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -451,6 +451,8 @@ public:
 	Common::Platform getPlatform() const { return _gameDescription->platform; }
 	bool isSoundEnabled() const { return _soundOn; }
 	const Graphics::Surface *getSavedScreen() const { return _savedScreen; }
+	bool isMacRenderMode() const { return _renderMode == Common::kRenderMacintosh || _renderMode == Common::kRenderMacintoshBW; }
+	bool isMacColorMode() const { return _renderMode == Common::kRenderMacintosh && _hasMacColors; }
 
 	void initTrig();
 	void loadMacColors();
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index ca543bca7e0..3f791c564d4 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -475,7 +475,7 @@ void ColonyEngine::destroyRobot(int num) {
 // bottom-left and bottom-right corners of the viewport converging at the aim
 // point, plus a small filled oval at center. Simulates a rifle-barrel perspective.
 void ColonyEngine::doShootCircles(int cx, int cy) {
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	if (isMac) {
 		// Mac shoot.c CShoot(): patXor diagonal lines radiating from aim point
@@ -541,7 +541,7 @@ void ColonyEngine::doShootCircles(int cx, int cy) {
 // SHOOT.C doBurnHole(): expanding concentric random rays in blue/yellow/white
 // when a robot is hit. Creates an "explosion" effect at the hit location.
 void ColonyEngine::doBurnHole(int cx, int cy, int radius) {
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	if (isMac) {
 		// Mac: InvertOval at robot bounds
@@ -605,7 +605,7 @@ void ColonyEngine::meGetShot() {
 	if (vw <= 0 || vh <= 0)
 		return;
 
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	if (isMac) {
 		// Mac shoot.c: InvertRect(&Clip) — full viewport flash
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 78a93b8b985..e345cf30a5f 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -140,7 +140,7 @@ public:
 };
 
 int ColonyEngine::runMacEndgameDialog(const Common::String &message) {
-	if (_renderMode != Common::kRenderMacintosh || !_wm || !_menuSurface || !_gfx)
+	if (!isMacRenderMode() || !_wm || !_menuSurface || !_gfx)
 		return Graphics::kMacDialogQuitRequested;
 
 	if (_macMenu && _wm->isMenuActive())
@@ -502,7 +502,7 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 	// Set up gradient palette entries (200-213) for story text
 	// Mac original: tColor.blue starts at 0xFFFF and decreases by 4096 per visible line
 	// B&W Mac: white gradient instead of blue
-	const bool bwMac = (macFont && !_hasMacColors);
+	const bool bwMac = (macFont && !isMacColorMode());
 	byte pal[14 * 3]; // storyLength entries
 	memset(pal, 0, sizeof(pal));
 	for (int i = 0; i < storyLength; i++) {
@@ -968,7 +968,7 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 
 	int centery = _height / 2 - 10;
 
-	const bool bwMac = (macFont && !_hasMacColors);
+	const bool bwMac = (macFont && !isMacColorMode());
 	const bool macStyle = (macFont != nullptr);
 	const uint32 grayIndex = 160;
 	const uint32 textIndex = 176;
@@ -1214,7 +1214,7 @@ void ColonyEngine::terminateGame(bool blowup) {
 	_centerX = savedCenterX;
 	_centerY = savedCenterY;
 
-	if (_renderMode == Common::kRenderMacintosh) {
+	if (isMacRenderMode()) {
 		while (!shouldQuit()) {
 			switch (runMacEndgameDialog(_("You have been terminated."))) {
 			case 0:
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 84c40706357..08649bf0b99 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -786,7 +786,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 
 void ColonyEngine::playTunnelAirlockEffect() {
 	const Common::Rect effectRect(0, _menuBarHeight, _width, _height);
-	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const bool macColor = isMacColorMode();
 	const int tunnelColor = 24; // c_tunnel
 	const uint32 fillFg = macColor ? packMacColor(_macColors[tunnelColor].fg) : 0;
 	const uint32 fillBg = macColor ? packMacColor(_macColors[tunnelColor].bg) : 0;
@@ -883,7 +883,7 @@ void ColonyEngine::playTunnelEffect(bool falling) {
 	// Original TUNNEL.C: falling into the reactor reuses the tunnel renderer
 	// with the falling flag set, which removes the tracks and shortens the run.
 	const Common::Rect effectRect(0, _menuBarHeight, _width, _height);
-	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const bool macColor = isMacColorMode();
 	const int tunnelColor = 24; // c_tunnel
 	const int tunnelFrames = falling ? 10 : 49;
 	const uint32 fillFg = macColor ? packMacColor(_macColors[tunnelColor].fg) : 0;
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index d40a6de5edf..32cf7e59df8 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -522,7 +522,7 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 				continue;
 			}
 
-			if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+			if (isMacColorMode()) {
 				// Mac color rendering: follows SuperPoly() from calcrobo.c:429-505.
 				int pattern;
 				uint32 fg;
@@ -574,7 +574,7 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 					_gfx->setStippleData(nullptr);
 				}
 			} else if (lit) {
-				if (_renderMode == Common::kRenderMacintosh) {
+				if (isMacRenderMode()) {
 					// Mac B&W: stipple dither pattern fill + black outline
 					int pattern;
 					if (colorIdx == kColorCorridorWall) {
@@ -632,10 +632,10 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 	const bool lit = (_corePower[_coreIndex] > 0);
 	// Mac color: c_plant bg; Mac B&W: black; EGA: green; unlit: white/black
 	uint32 color;
-	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+	if (isMacColorMode()) {
 		color = lit ? packMacColor(_macColors[63].bg) : 0xFF000000; // c_plant
 	} else {
-		color = lit ? (_renderMode == Common::kRenderMacintosh ? 0 : 2) : 15;
+		color = lit ? (isMacRenderMode() ? 0 : 2) : 15;
 	}
 
 	for (int i = 0; i < def.surfaceCount; i++) {
@@ -740,7 +740,7 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 		}
 	}
 
-	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+	if (isMacColorMode()) {
 		// Mac color: map fillColor to Mac color index and use RGB
 		// fillColor is an ObjColor enum value passed by the caller
 		const int fillIdx = mapObjColorToMacColor((int)fillColor, _level);
@@ -760,7 +760,7 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 		if (stipple)
 			_gfx->setStippleData(nullptr);
 	} else if (lit) {
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			int pattern = lookupMacPattern((int)fillColor, _level);
 			if (pattern == kPatternClear)
 				pattern = kPatternGray;
@@ -872,11 +872,11 @@ void ColonyEngine::renderCorridor3D() {
 	computeVisibleCells();
 
 	bool lit = (_corePower[_coreIndex] > 0);
-	bool macMode = (_renderMode == Common::kRenderMacintosh);
+	bool macMode = isMacRenderMode();
 
 	uint32 wallFill, wallLine, floorColor, ceilColor;
 
-	if (macMode && _hasMacColors) {
+	if (isMacColorMode()) {
 		if (lit) {
 			// Mac Display(): wallColor = cColor[c_char0+level-1].f (level-specific color).
 			// SuperPoly(c_lwall) uses wallColor as fill, giving all walls the level tint.
@@ -928,7 +928,7 @@ void ColonyEngine::renderCorridor3D() {
 	// Mac color mode: original corridor renderer only showed ceiling edges at wall
 	// boundaries (via 2D perspective), not a full-map grid. Wall tops from draw3DWall
 	// already provide the ceiling lines where walls exist.
-	if (!(macMode && _hasMacColors)) {
+	if (!isMacColorMode()) {
 		for (int i = 0; i <= 32; i++) {
 			float d = i * 256.0f;
 			float maxD = 32.0f * 256.0f;
diff --git a/engines/colony/render_features.cpp b/engines/colony/render_features.cpp
index 8534a898db8..ec211480f47 100644
--- a/engines/colony/render_features.cpp
+++ b/engines/colony/render_features.cpp
@@ -252,8 +252,8 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 	if (!data || data[0] == 0)
 		return;
 
-	const bool macMode = (_renderMode == Common::kRenderMacintosh);
-	const bool macColors = (macMode && _hasMacColors);
+	const bool macMode = isMacRenderMode();
+	const bool macColors = isMacColorMode();
 	const uint32 fillColor = macColors ? packMacColor(_macColors[8 + _level - 1].bg) : 0;
 	const uint32 lineColor = macColors ? (uint32)0xFF000000 : 0;
 
@@ -354,7 +354,7 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 	}
 
 	if (macMode) {
-		const uint32 wallFill = _hasMacColors
+		const uint32 wallFill = macColors
 			? packMacColor(_macColors[8 + _level - 1].fg)
 			: (uint32)255;
 		_gfx->setWireframe(true, wallFill);
@@ -393,8 +393,8 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 	bool lit = (_corePower[_coreIndex] > 0);
 	uint32 holeColor = lit ? 0 : 7;
 
-	const bool macMode = (_renderMode == Common::kRenderMacintosh);
-	const bool macColors = (macMode && _hasMacColors);
+	const bool macMode = isMacRenderMode();
+	const bool macColors = isMacColorMode();
 
 	// Helper lambda: draw a filled hole polygon with Mac color or B&W fallback
 	auto drawHolePoly = [&](const float *u, const float *v, int cnt, int macIdx) {
@@ -484,8 +484,8 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 
 	float corners[4][3];
 	getWallFace3D(cellX, cellY, direction, corners);
-	const bool macMode = (_renderMode == Common::kRenderMacintosh);
-	const bool macColors = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const bool macMode = isMacRenderMode();
+	const bool macColors = isMacColorMode();
 	const bool lit = (_corePower[_coreIndex] > 0);
 	const uint32 wallFeatureFill = macColors
 		? packMacColor(lit ? _macColors[8 + _level - 1].fg : _macColors[6].bg)
@@ -653,7 +653,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
 
 		// Mac: fill entire wall face (c_upstairs)
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 			float vf2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
 			if (macColors) {
@@ -726,7 +726,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
 
 		// Mac: fill entire wall face (c_dnstairs)
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 			float vf2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
 			if (macColors) {
@@ -830,7 +830,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		// Tunnel: hexagonal opening from Grid (0,0 0,5 1,6 5,6 6,5 6,0)
 		const float uT[6] = { 0.0f,    0.0f,    1/6.0f,  5/6.0f,  1.0f,    1.0f };
 		const float vT[6] = { 0.0f,    0.750f,  0.875f,  0.875f,  0.750f,  0.0f };
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			if (macColors) {
 				macFillPoly(uT, vT, 6, 24); // c_tunnel
 			} else {
@@ -894,7 +894,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		// map[1..4] = pattern ID per band (0=WHITE, 1=LTGRAY, 2=GRAY, 3=DKGRAY, 4=BLACK).
 		// Values >= 5 trigger animation: color = (map[i+1] + _displayCount) % 5.
 		// Band 0 (top): v=0.75..1.0, Band 1: v=0.5..0.75, Band 2: v=0.25..0.5, Band 3: v=0..0.25.
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			if (macColors) {
 				// Mac drawColor: map[i+1] selects color (0→c_color0..3→c_color3, 4→BLACK).
 				// Values >= 5: animated = (map[i+1] + _displayCount) % 5.
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index a13f4076449..a93feac780c 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1271,7 +1271,7 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 		pz[i] = centerZ + ca * axisHZ + sa * axisVZ;
 	}
 
-	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+	if (isMacColorMode()) {
 		const int macColorIdx = mapEyeOverlayColorToMacColor(fillColorIdx, _level);
 		int pattern = _macColors[macColorIdx].pattern;
 		uint32 fg = packEyeOverlayMacColor(_macColors[macColorIdx].fg);
@@ -1288,7 +1288,7 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 		if (stipple)
 			_gfx->setStippleData(nullptr);
 	} else if (lit) {
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			int pattern = mapEyeOverlayColorToMacPattern(fillColorIdx);
 			if (pattern == kPatternClear)
 				return;
@@ -1375,13 +1375,13 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kObjFWall:
-		if (_renderMode == Common::kRenderMacintosh)
+		if (isMacRenderMode())
 			draw3DPrism(obj, kFWallPart, false, kColorCorridorWall, true, true);
 		else
 			draw3DPrism(obj, kFWallPart, false, -1, true, true);
 		break;
 	case kObjCWall:
-		if (_renderMode == Common::kRenderMacintosh)
+		if (isMacRenderMode())
 			draw3DPrism(obj, kCWallPart, false, kColorCorridorWall, true, true);
 		else
 			draw3DPrism(obj, kCWallPart, false, -1, true, true);
@@ -1715,7 +1715,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			Thing &nearEye = leftFirst ? rightEye : leftEye;
 			const PrismPartDef &farWing = leftFirst ? kQLWingDef : kQRWingDef;
 			const PrismPartDef &nearWing = leftFirst ? kQRWingDef : kQLWingDef;
-			const int wingColor = (_renderMode != Common::kRenderMacintosh && _level == 7) ? kColorQueenWingRed : kColorClear;
+			const int wingColor = (!isMacRenderMode() && _level == 7) ? kColorQueenWingRed : kColorClear;
 
 			// DOS draweyes(): queen eyeball is RED fill + WHITE outline
 			// (hardcoded, not from color table). Iris is GREEN.
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index e73ce82bc1c..5b0cf0516d2 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -161,18 +161,16 @@ bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
 	return true;
 }
 
-// Load a PICT resource from the Mac resource fork, returning a new RGB surface.
-// Try Color Colony first (has color dashboard PICTs), then fall back to B&W Colony.
+// Load a dashboard PICT resource from the Mac resource fork, returning a new
+// RGB surface. The companion Color Colony resource fork contains the complete
+// dashboard PICT set, so prefer it when available and fall back to the base app.
 // Caller owns the returned surface. Returns nullptr on failure.
 Graphics::Surface *ColonyEngine::loadPictSurface(int resID) {
 	Common::SeekableReadStream *pictStream = nullptr;
 
-	// Try Color Colony resource fork first
-	if (_colorResMan && _colorResMan->hasResFork()) {
+	if (_colorResMan && _colorResMan->hasResFork())
 		pictStream = _colorResMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
-	}
 
-	// Fall back to B&W Colony resource fork
 	if (!pictStream && _resMan && (_resMan->isMacFile() || _resMan->hasResFork())) {
 		pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
 	}
@@ -235,8 +233,7 @@ Graphics::Surface *ColonyEngine::loadPictSurface(int resID) {
 
 // Draw a PICT surface at a specific destination position. The surface was
 // created by loadPictSurface() in the exact ARGB layout the renderer's
-// drawSurface() consumes, so this is a single textured-quad blit — no need
-// to walk pixels.
+// drawSurface() consumes, so this is a single textured-quad blit.
 void ColonyEngine::drawPictAt(Graphics::Surface *surf, int destX, int destY) {
 	if (!surf)
 		return;
@@ -252,7 +249,7 @@ void ColonyEngine::updateViewportLayout() {
 		return Common::Rect(left, top, right, bottom);
 	};
 
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	// Original IBM_INIT.C: pix_per_Qinch = pixResX/4, pixResY/4
 	// MetaWINDOW EGA 640x350: pixResX=96, pixResY=72 → pQx=24, pQy=18
@@ -293,12 +290,35 @@ void ColonyEngine::updateViewportLayout() {
 	if (isMac) {
 		// Original Mac layout from inits.c/compass.c/power.c:
 		// screenR.left = 96  sidebar is 96px wide.
-		// Two floating windows centered in sidebar over gray desktop.
+		// Two floating windows over gray desktop.
 		// moveWindow: compRect = (0,0, 2*CCENTER, 3*CCENTER) = (0,0, 70, 105)
 		//   floorRect (minimap) = (8,8)-(62,62)  54x54 inside moveWindow
 		//   compass dish below at (19,66)-(51,98), needle center at (35,82)
-		// infoWindow: sized from PICT resource, positioned above moveWindow
+		// infoWindow: original WIND 10930 rect (9,26)-(79,189).
 		const int CCENTER = 35;
+		auto loadWindowRect = [this, &makeSafeRect](int resID, const Common::Rect &fallback) {
+			Common::SeekableReadStream *windStream = nullptr;
+			if (_resMan && (_resMan->isMacFile() || _resMan->hasResFork()))
+				windStream = _resMan->getResource(MKTAG('W', 'I', 'N', 'D'), (int16)resID);
+			if (!windStream && _colorResMan && _colorResMan->hasResFork())
+				windStream = _colorResMan->getResource(MKTAG('W', 'I', 'N', 'D'), (int16)resID);
+
+			if (!windStream)
+				return fallback;
+
+			Common::Rect r = fallback;
+			if (windStream->size() >= 8) {
+				const int top = windStream->readSint16BE();
+				const int left = windStream->readSint16BE();
+				const int bottom = windStream->readSint16BE();
+				const int right = windStream->readSint16BE();
+				r = makeSafeRect(left, top, right, bottom);
+			}
+			delete windStream;
+			return r;
+		};
+
+		const Common::Rect infoWindow = loadWindowRect(10930, Common::Rect(9, 26, 79, 189));
 
 		// Load PICT surfaces (cached after first load)
 		if (!_pictCompass)
@@ -312,23 +332,24 @@ void ColonyEngine::updateViewportLayout() {
 			if (_armor > 0)
 				wantID = -32755;
 			else
-				wantID = _hasMacColors ? -32761 : -32752;
+				wantID = isMacColorMode() ? -32761 : -32752;
 			_pictPower = loadPictSurface(wantID);
 			if (!_pictPower && _armor > 0 && wantID != -32755)
 				_pictPower = loadPictSurface(-32755);
 			_pictPowerID = _pictPower ? wantID : 0;
 		}
 
-		// moveWindow dimensions from original constants
+		// Keep the ScummVM dashboard's bottom moveWindow placement. The original
+		// Mac window resource is for a compact 512x342 desktop and should not
+		// move this panel upward on our larger Mac render surface.
 		const int moveW = 2 * CCENTER; // 70
 		const int moveH = 3 * CCENTER; // 105
-		const int infoW = _pictPower ? _pictPower->w : moveW;
-		const int infoH = _pictPower ? _pictPower->h : moveH;
+		const int infoW = infoWindow.width();
+		const int infoH = infoWindow.height();
 
-		// Center panels horizontally in sidebar
 		const int centerX = dashWidth / 2;
 
-		// Position moveWindow at the bottom of the sidebar
+		// Position moveWindow at the bottom of the sidebar.
 		const int moveLeft = MAX(0, centerX - moveW / 2);
 		const int moveTop = _height - pad - moveH;
 
@@ -340,11 +361,12 @@ void ColonyEngine::updateViewportLayout() {
 		// _compassRect = entire moveWindow (used for compass dish drawing)
 		_compassRect = makeSafeRect(moveLeft, moveTop, moveLeft + moveW, moveTop + moveH);
 
-		// Position infoWindow below the Mac menu bar.
-		// Original PICT is drawn at (-2,-2) in window-local coords, so offset
-		// the panel by 2px to prevent the PICT from overlapping the menu bar.
-		const int infoLeft = MAX(0, centerX - infoW / 2);
-		const int infoTop = menuTop + pad;
+		int infoLeft = infoWindow.left;
+		int infoTop = infoWindow.top;
+		if (infoLeft + infoW > dashWidth)
+			infoLeft = MAX(0, dashWidth - infoW);
+		if (infoTop + infoH > _height)
+			infoTop = MAX(menuTop, _height - pad - infoH);
 		_powerRect = makeSafeRect(infoLeft, infoTop, infoLeft + infoW, infoTop + infoH);
 	} else {
 		// DASHBOAR.C RCompass(): compOval before shrink
@@ -428,7 +450,7 @@ void ColonyEngine::drawDashboardStep1() {
 	if (_dashBoardRect.width() <= 0 || _dashBoardRect.height() <= 0)
 		return;
 
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	if (isMac) {
 		drawDashboardMac();
@@ -521,7 +543,7 @@ void ColonyEngine::drawDashboardStep1() {
 // Original Mac had two floating windows (infoWindow + moveWindow) over gray desktop.
 
 void ColonyEngine::drawDashboardMac() {
-	const bool macColor = _hasMacColors;
+	const bool macColor = isMacColorMode();
 	const uint32 colBlack = packRGB(0, 0, 0);
 	const uint32 colWhite = packRGB(255, 255, 255);
 	const uint32 colWinBg = macColor ? packMacColor(_macColors[7].bg) : colWhite;
@@ -580,27 +602,44 @@ void ColonyEngine::drawDashboardMac() {
 		// In the original B&W game, GetPicture(-32752) returns null when !armor,
 		// so DrawPicture is a no-op — the window just shows white fill.
 		if (_pictPower)
-			drawPictAt(_pictPower, _powerRect.left - 2, _powerRect.top - 2);
+			drawPictAt(_pictPower, _powerRect.left + 1, _powerRect.top + 1);
+
+		if (!macColor) {
+			// Match the B&W Window Manager shadow as pixels. The shadow is
+			// staggered over the checkerboard desktop; a solid rect is too wide.
+			_gfx->fillRect(Common::Rect(_powerRect.right, _powerRect.top - 1,
+				_powerRect.right + 1, _powerRect.bottom + 3), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.right + 1, _powerRect.top,
+				_powerRect.right + 2, _powerRect.bottom + 4), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.right + 2, _powerRect.top - 1,
+				_powerRect.right + 3, _powerRect.top), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.right + 2, _powerRect.top + 1,
+				_powerRect.right + 3, _powerRect.bottom + 3), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.left - 2, _powerRect.bottom,
+				_powerRect.right + 3, _powerRect.bottom + 1), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.left + 1, _powerRect.bottom + 1,
+				_powerRect.right + 4, _powerRect.bottom + 2), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.left, _powerRect.bottom + 2,
+				_powerRect.right + 3, _powerRect.bottom + 3), colBlack);
+		}
 
 		// Blue bars only when armored (power.c: if(armor) { ... ForeColor(blueColor) ... })
 		if (_armor > 0 && _pictPower) {
-			// power.c info rect adjustments: left+=3,right+=3,top-=3,bottom-=3, then ++/--
-			// Net effect: info is adjusted relative to PICT position
-			const int infoLeft = _powerRect.left - 2 + 2;  // -2 (PICT offset) +3-1 = 0, +2 net
-			const int infoBottom = _powerRect.top - 2 + _pictPower->h - 2; // PICT bottom adjusted
-			const int bot = infoBottom - 27; // power.c: bot = info.bottom - 27
+			// power.c draws the bars with QuickDraw MoveTo/LineTo after shifting
+			// the PICT rect. GL line rasterization does not cover the same pixels,
+			// so draw the observed 18x2 bar strips explicitly.
+			const int infoLeft = _powerRect.left - 1;
+			const int bot = _powerRect.bottom - 27; // power.c: bot = info.bottom - 27
 
 			for (int i = 0; i < 3; i++) {
-				// power.c: lft = 3 + info.left + i*23
+				// Reference B&W frame: columns start at x 10, 33, 56; bars are
+				// inset by 2 pixels on each side: x 12..29, 35..52, 58..75.
 				const int lft = 3 + infoLeft + i * 23;
 				for (int j = 0; j < ePower[i] && j < 20; j++) {
 					const int ln = bot - 3 * j;
 					if (ln <= _powerRect.top)
 						break;
-					// power.c: MoveTo(lft+1,ln); LineTo(lft+16,ln);  16px wide, 2 lines
-					_gfx->drawLine(lft + 1, ln, lft + 16, ln, colBlue);
-					if (ln - 1 > _powerRect.top)
-						_gfx->drawLine(lft + 1, ln - 1, lft + 16, ln - 1, colBlue);
+					_gfx->fillRect(Common::Rect(lft + 1, ln - 1, lft + 19, ln + 1), colBlue);
 				}
 			}
 		}
@@ -694,7 +733,7 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 			_gfx->drawLine(x1, y1, x2, y2, color);
 	};
 
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	int lExt, sExt, xloc, yloc, ccenterx, ccentery;
 	if (isMac) {
@@ -921,8 +960,8 @@ void ColonyEngine::drawAutomap() {
 		return;
 
 	const int lv = _level - 1;
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
-	const bool macColor = isMac && _hasMacColors;
+	const bool isMac = isMacRenderMode();
+	const bool macColor = isMacColorMode();
 
 	const Common::Rect vp(0, _menuBarHeight, _width, _height);
 	const int vpW = vp.width();
@@ -1014,7 +1053,7 @@ void ColonyEngine::drawForkliftOverlay() {
 	const int w = _screenR.width();
 	const int h = _screenR.height();
 	const int cx = w / 2;
-	const uint32 color = (_renderMode == Common::kRenderMacintosh) ? packRGB(0, 0, 0) : 0;
+	const uint32 color = isMacRenderMode() ? packRGB(0, 0, 0) : 0;
 
 	const int tx2 = cx >> 2;  // centerX/4
 	const int tx1 = cx >> 1;  // centerX/2
@@ -1031,7 +1070,7 @@ void ColonyEngine::drawCrosshair() {
 	if (!_crosshair || _screenR.width() <= 0 || _screenR.height() <= 0)
 		return;
 
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 	if (isMac && _cursorShoot && !_mouseLocked && _weapons > 0)
 		return;
 
@@ -1039,7 +1078,7 @@ void ColonyEngine::drawCrosshair() {
 	if (isMac) {
 		// Mac: black when powered, gray when no weapons, white when armed but no power
 		// B&W: no gray, so powered=black, else white
-		if (_hasMacColors)
+		if (isMacColorMode())
 			color = (_corePower[_coreIndex] > 0) ? packRGB(0, 0, 0)
 				: (_weapons > 0) ? packRGB(255, 255, 255) : packRGB(128, 128, 128);
 		else
@@ -1109,8 +1148,8 @@ void ColonyEngine::printMessage(const char *text[], bool hold) {
 		numLines++;
 	}
 
-	if (_renderMode == Common::kRenderMacintosh && drawMacTextPopup(_wm, _gfx,
-			_width, _height, _centerX, _centerY, lines, Graphics::kTextAlignCenter, _hasMacColors)) {
+	if (isMacRenderMode() && drawMacTextPopup(_wm, _gfx,
+			_width, _height, _centerX, _centerY, lines, Graphics::kTextAlignCenter, isMacColorMode())) {
 		if (hold)
 			waitForInput();
 		return;
@@ -1226,13 +1265,13 @@ void ColonyEngine::doText(int entry, int center) {
 	if (maxlines > (int)lineArray.size())
 		maxlines = lineArray.size();
 
-	if (_renderMode == Common::kRenderMacintosh) {
+	if (isMacRenderMode()) {
 		Common::Array<Common::String> popupLines;
 		for (int i = 0; i < maxlines; ++i)
 			popupLines.push_back(lineArray[i]);
 		popupLines.push_back((int)lineArray.size() > maxlines ? kmore : kpress);
 		if (drawMacTextPopup(_wm, _gfx, _width, _height, _centerX, _centerY, popupLines,
-				center == 1 ? Graphics::kTextAlignCenter : Graphics::kTextAlignLeft, _hasMacColors)) {
+				center == 1 ? Graphics::kTextAlignCenter : Graphics::kTextAlignLeft, isMacColorMode())) {
 			waitForInput();
 			delete[] page;
 			return;


Commit: 9b0fb9204204cb43b003148c5f8740c682c8519a
    https://github.com/scummvm/scummvm/commit/9b0fb9204204cb43b003148c5f8740c682c8519a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: fixed xor mode in shader rendering

Changed paths:
    engines/colony/renderer_opengl_shaders.cpp


diff --git a/engines/colony/renderer_opengl_shaders.cpp b/engines/colony/renderer_opengl_shaders.cpp
index 0639d28f5da..9315a5c8f9b 100644
--- a/engines/colony/renderer_opengl_shaders.cpp
+++ b/engines/colony/renderer_opengl_shaders.cpp
@@ -45,8 +45,6 @@
 namespace Colony {
 
 // Phase 3: 2D primitives + the 3D corridor draw path are programmable.
-// XOR mode and polygon stipple are intentionally left stubbed — see the
-// renderer audit for why those are deferred.
 class OpenGLShaderRenderer : public Renderer {
 public:
 	OpenGLShaderRenderer(OSystem *system, int width, int height);
@@ -77,7 +75,7 @@ public:
 
 	void copyToScreen() override;
 	void setWireframe(bool enable, int64_t fillColor) override;
-	void setXorMode(bool enable) override {}
+	void setXorMode(bool enable) override;
 	void setStippleData(const byte *data) override;
 	void setMacColors(uint32 fg, uint32 bg) override;
 	void setDepthState(bool testEnabled, bool writeEnabled) override;
@@ -147,6 +145,7 @@ private:
 
 	bool _wireframe = true;
 	int64_t _wireframeFillColor = 0; // -1 = no fill, else color (palette idx or ARGB)
+	bool _xorMode = false;
 
 	// Stipple state. The shader emulates glPolygonStipple via a 128-int
 	// uniform array (Freescape pattern, GLES2-safe). _stippleActive is
@@ -348,6 +347,17 @@ void OpenGLShaderRenderer::drawSolid(GLenum mode, const float *positions, int ve
 		const float rgba[4]) {
 	if (vertCount <= 0)
 		return;
+	if (_xorMode) {
+		// QuickDraw's patXor effects are drawn with white mask pixels in the
+		// reimplementation. This blend state turns those pixels into
+		// 1 - destination, which matches the visible Mac B&W shoot/invert flash
+		// behavior and works on shader renderers without GL logic ops.
+		glEnable(GL_BLEND);
+		glBlendEquation(GL_FUNC_ADD);
+		glBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
+	} else {
+		glDisable(GL_BLEND);
+	}
 	uploadSolid(positions, vertCount);
 	// "projection" is set once by uploadProjectionUniform() — it persists in
 	// the program object across draws. Shader stays bound between draws so
@@ -665,6 +675,12 @@ void OpenGLShaderRenderer::setWireframe(bool enable, int64_t fillColor) {
 	_wireframeFillColor = fillColor;
 }
 
+void OpenGLShaderRenderer::setXorMode(bool enable) {
+	_xorMode = enable;
+	if (!enable)
+		glDisable(GL_BLEND);
+}
+
 void OpenGLShaderRenderer::setStippleData(const byte *data) {
 	const bool nowActive = (data != nullptr);
 	if (nowActive) {


Commit: d541fc5d0e6d2d7385cd4a036d8229de5d948840
    https://github.com/scummvm/scummvm/commit/d541fc5d0e6d2d7385cd4a036d8229de5d948840
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: complete rendering of mountain parallax

Changed paths:
    engines/colony/battle.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 5ec5031274b..6dd4f82cf06 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -458,8 +458,9 @@ void ColonyEngine::battleInit() {
 	_projon = false;
 	_pcount = 0;
 
-	// Mountain parallax
-	_battledx = _width / 59;
+	// Mountain parallax. battleBackdrop() recomputes this once the viewport
+	// layout is final; keep a sane initial value for freshly initialized state.
+	_battledx = MAX<int>(1, _screenR.width() / 59);
 
 	// Generate mountain height profile (smoothed random)
 	int temp[257];
@@ -548,7 +549,13 @@ void ColonyEngine::battleBackdrop() {
 	// Mountain silhouette
 	uint32 mtColor = 0xFF606060;
 	uint8 ang = _me.look;
-	int xloc = -_battledx;
+	// Original battle.c draws the mountain profile under ClipRect(&Clip).
+	// Align the first sample to the active viewport, not to logical x=0
+	// where the Mac dashboard/sidebar lives in ScummVM.
+	_battledx = MAX<int>(1, _screenR.width() / 59);
+	if (!isMacRenderMode())
+		_battledx++;
+	int xloc = _screenR.left - _battledx;
 	if (ang & 0x01) {
 		xloc += _battledx;
 		ang--;
@@ -569,7 +576,12 @@ void ColonyEngine::battleBackdrop() {
 			sunon = true;
 		}
 		int curY = horizonY - _mountains[ang];
-		_gfx->drawLine(prevX, prevY, xloc, curY, mtColor);
+		int x1 = prevX;
+		int y1 = prevY;
+		int x2 = xloc;
+		int y2 = curY;
+		if (clipLineToRect(x1, y1, x2, y2, _screenR))
+			_gfx->drawLine(x1, y1, x2, y2, mtColor);
 		prevX = xloc;
 		prevY = curY;
 	}


Commit: 0befed2f78418f3d30d171d00cabd9ba3b3f656f
    https://github.com/scummvm/scummvm/commit/0befed2f78418f3d30d171d00cabd9ba3b3f656f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: make sure the battle coordinates are normalized before starting

Changed paths:
    engines/colony/battle.cpp
    engines/colony/colony.h
    engines/colony/movement.cpp
    engines/colony/savegame.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 6dd4f82cf06..cc870b5d272 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -515,6 +515,16 @@ void ColonyEngine::battleSet() {
 	}
 }
 
+void ColonyEngine::normalizeBattlePlayerPosition() {
+	// The original stored battle coordinates in 16-bit ints. Airlock map
+	// targets such as 253<<8 wrap to negative world coordinates there; keep
+	// the same representation before battle culling and camera math run.
+	_me.xloc = battleNormalizeCoord(_me.xloc);
+	_me.yloc = battleNormalizeCoord(_me.yloc);
+	_me.xindex = wrapBattleCoord(_me.xloc) >> 8;
+	_me.yindex = wrapBattleCoord(_me.yloc) >> 8;
+}
+
 // =====================================================================
 // battleBackdrop: Draw 2D sky gradient and ground fill.
 // Called before 3D rendering begins.
@@ -1187,6 +1197,7 @@ void ColonyEngine::battleProjCommand(int xcheck, int ycheck) {
 // Called from the main loop when _gameMode == kModeBattle.
 // =====================================================================
 void ColonyEngine::renderBattle() {
+	normalizeBattlePlayerPosition();
 	_battleMaxP = 0;
 
 	// Phase 1: 2D backdrop (sky gradient, mountains, sun) follows camera pitch
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index b4be8d94fb9..f3de2a2e2df 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -693,6 +693,7 @@ private:
 	void battleInit();
 	void battleSet();
 	void battleThink();
+	void normalizeBattlePlayerPosition();
 	void enterColonyFromBattle(int mapNum, int xloc, int yloc);
 	void battleCommand(int xnew, int ynew);
 	void battleShoot();
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 08649bf0b99..fd550ab45ed 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -599,6 +599,7 @@ int ColonyEngine::goToDestination(const uint8 *map, Locate *pobject) {
 		_me.yloc = targetY << 8;
 		_me.xindex = targetX;
 		_me.yindex = targetY;
+		normalizeBattlePlayerPosition();
 		return 2;
 	}
 
diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
index 8d7df774b93..1f423247887 100644
--- a/engines/colony/savegame.cpp
+++ b/engines/colony/savegame.cpp
@@ -581,6 +581,8 @@ Common::Error ColonyEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	_lastWarningChimeTime = 0;
 	_battledx = _width / 59;
 	updateViewportLayout();
+	if (_gameMode == kModeBattle)
+		normalizeBattlePlayerPosition();
 
 	return Common::kNoError;
 }




More information about the Scummvm-git-logs mailing list