[Scummvm-git-logs] scummvm master -> e9896e53a9af49da32ae605750105b4810fda2f2

neuromancer noreply at scummvm.org
Tue Aug 18 10:34:59 UTC 2026


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

Summary:
fd4c90d9e9 COLONY: improved rendering of enemies, according to the original source
5814ae9fc3 COLONY: improved minimap symbols for enemies and objects
9df0a7e21e COLONY: corrected enemy behavior when facing the player
34a8840458 COLONY: fixed egg rendering artifact
e9896e53a9 COLONY: fixed some core related code conditions in different controls


Commit: fd4c90d9e9a94db5c41d6df98eda0fa6f3a3e32a
    https://github.com/scummvm/scummvm/commit/fd4c90d9e9a94db5c41d6df98eda0fa6f3a3e32a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-18T11:30:24+02:00

Commit Message:
COLONY: improved rendering of enemies, according to the original source

Changed paths:
    engines/colony/battle.cpp
    engines/colony/colony.h
    engines/colony/render.cpp
    engines/colony/render_objects.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 95c33f1f27a..6503b0553c2 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -721,8 +721,8 @@ void ColonyEngine::battleDrawTanks() {
 
 		// Build animated left pincer vertices
 		int lPincerPts[4][3];
-		int nabs_lookx = (drone.lookx > 0) ? -drone.lookx : drone.lookx; // nabs
-		int lLook = nabs_lookx - 32;
+		// BATTLE.C's -32 is the phase-shifted table's, not an angle.
+		int lLook = (drone.lookx > 0) ? -drone.lookx : drone.lookx; // nabs
 		if (lLook < 0)
 			lLook += 256;
 		for (int j = 0; j < 4; j++) {
@@ -740,7 +740,7 @@ void ColonyEngine::battleDrawTanks() {
 
 		// Build animated right pincer vertices
 		int rPincerPts[4][3];
-		int rLook = ABS(drone.lookx) - 32;
+		int rLook = ABS(drone.lookx);
 		if (rLook < 0)
 			rLook += 256;
 		for (int j = 0; j < 4; j++) {
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 1776207a826..fe14962fc9e 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -644,6 +644,8 @@ private:
 	int _front = 0, _side = 0;
 	int _direction = 0;
 
+	float _eyeDepthPull = 0.0f; // world units the eye parts are pulled at the camera
+
 	Common::Rect _clip;
 	Common::Rect _screenR;
 	Common::Rect _dashBoardRect;
@@ -687,6 +689,9 @@ private:
 	void drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride, bool forceVisible = false);
 	void drawEyeOverlays3D(Thing &thing, const PrismPartDef &irisDef, int irisColorOverride,
 		const PrismPartDef &pupilDef, int pupilColorOverride, bool useLook);
+	void drawBodyEye3D(Thing &obj, int eyeballColor, int pupilColor, float pull);
+	void drawEnemyEye3D(Thing &obj, Thing &eye, int eyeballColor, int irisColor, int pupilColor);
+	void pullTowardCamera(float *px, float *py, float *pz, int count) const;
 	float growRenderTickFraction() const;
 	bool drawInterpolatedGrowRobot(Thing &obj, int eyeballColor, int pupilColor);
 	void drawInterpolatedGrowPrism(Thing &obj, const PrismPartDef &fromDef, const PrismPartDef &toDef, float progress);
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 6f23dda727b..eaca25a9ead 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -251,7 +251,7 @@ int mapObjColorToMacColor(int colorIdx, int level) {
 	case kColorDroneEye:  return 51;  // c_edrone
 	case kColorSoldierBody: return 52; // c_soldier
 	case kColorSoldierEye: return 53; // c_esoldier
-	case kColorQueenBody: return 43 + CLIP(level - 2, 0, 4); // c_queen1..c_queen5
+	case kColorQueenBody: return 43 + CLIP(level - 2, 0, 5); // c_queen1..c_queenP
 	case kColorQueenEye:  return 49;  // c_equeen
 	case kColorQueenWingRed: return 48; // unused in Mac mode
 	case kColorTopSnoop:  return 56;  // c_snooper1
@@ -669,6 +669,23 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 	}
 }
 
+// Each vertex stays on its own view ray, so only the depth test moves.
+void ColonyEngine::pullTowardCamera(float *px, float *py, float *pz, int count) const {
+	if (_eyeDepthPull <= 0.0f)
+		return;
+	for (int i = 0; i < count; i++) {
+		const float dx = px[i] - (float)_me.xloc;
+		const float dy = py[i] - (float)_me.yloc;
+		const float d = sqrtf(dx * dx + dy * dy + pz[i] * pz[i]);
+		if (d <= _eyeDepthPull)
+			continue;
+		const float k = (d - _eyeDepthPull) / d;
+		px[i] = (float)_me.xloc + dx * k;
+		py[i] = (float)_me.yloc + dy * k;
+		pz[i] *= k;
+	}
+}
+
 void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 	int pt1x, int pt1y, int pt1z,
 	uint32 fillColor, uint32 outlineColor, bool accumulateBounds) {
@@ -742,6 +759,8 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 		}
 	}
 
+	pullTowardCamera(px, py, pz, N);
+
 	if (isMacColorMode()) {
 		// Mac color: map fillColor to Mac color index and use RGB
 		// fillColor is an ObjColor enum value passed by the caller
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 948a37520b4..dd7828673bf 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1267,8 +1267,10 @@ EnemyEyePair buildEnemyEyePair(const Common::Rect &screenR, const Colony::Thing
 	eyes.left = obj;
 	eyes.right = obj;
 
-	const int32 s1 = sint[obj.where.ang] >> 1;
-	const int32 c1 = cost[obj.where.ang] >> 1;
+	// +32 for the table phase, as in the prism rotation the irises follow.
+	const uint8 eyeAng = obj.where.ang + 32;
+	const int32 s1 = sint[eyeAng] >> 1;
+	const int32 c1 = cost[eyeAng] >> 1;
 	const int32 s2 = s1 >> 1;
 	const int32 c2 = c1 >> 1;
 	const int32 eyeBaseX = obj.where.xloc + c1;
@@ -1393,6 +1395,8 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 		pz[i] = centerZ + ca * axisHZ + sa * axisVZ;
 	}
 
+	pullTowardCamera(px, py, pz, kSegments);
+
 	if (isMacColorMode()) {
 		const int macColorIdx = mapEyeOverlayColorToMacColor(fillColorIdx, _level);
 		int pattern = _macColors[macColorIdx].pattern;
@@ -1449,6 +1453,28 @@ void ColonyEngine::drawEyeOverlays3D(Thing &thing, const PrismPartDef &irisDef,
 	drawPrismOval3D(thing, pupilDef, useLook, pupilColorOverride, true);
 }
 
+// draweyes() of Queen/Drone/Soldier. Writes off: a turned eye leaves the iris
+// quad behind the camera-facing ball, which would then clip it.
+void ColonyEngine::drawEnemyEye3D(Thing &obj, Thing &eye, int eyeballColor, int irisColor, int pupilColor) {
+	_gfx->setDepthState(true, false);
+	draw3DSphere(eye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+	drawEyeOverlays3D(eye, kQIrisDef, irisColor, kQPupilDef, pupilColor, true);
+	_gfx->setDepthState(true, true);
+	mergeObjectBounds(obj.where, eye.where);
+}
+
+// Shared draweye() of Pyramid/UPyramid/Cube. A depth-range bias shifts by a
+// fraction of the distance, so up close a tip punched through the eye; pull the
+// eye past the body's bounding radius instead.
+void ColonyEngine::drawBodyEye3D(Thing &obj, int eyeballColor, int pupilColor, float pull) {
+	_eyeDepthPull = pull;
+	_gfx->setDepthState(true, false);
+	draw3DSphere(obj, 0, 0, 175, 0, 0, 200, eyeballColor, kColorBlack, true);
+	drawEyeOverlays3D(obj, kPIrisDef, -1, kPPupilDef, pupilColor, false);
+	_gfx->setDepthState(true, true);
+	_eyeDepthPull = 0.0f;
+}
+
 int interpolatedRobotPoint(int from, int to, float progress) {
 	return (int)roundf((float)from + ((float)to - (float)from) * progress);
 }
@@ -1560,6 +1586,7 @@ void ColonyEngine::drawInterpolatedGrowEye(Thing &obj, int fromStage, int toStag
 
 	const int z0 = interpolatedRobotPoint(sphereZ[fromStage][0], sphereZ[toStage][0], progress);
 	const int z1 = interpolatedRobotPoint(sphereZ[fromStage][1], sphereZ[toStage][1], progress);
+	_gfx->setDepthState(true, false);
 	draw3DSphere(obj, 0, 0, z0, 0, 0, z1, eyeballColor, kColorBlack, true);
 
 	const PrismPartDef &fromIris = growEyeIrisDefForStage(fromStage);
@@ -1579,6 +1606,7 @@ void ColonyEngine::drawInterpolatedGrowEye(Thing &obj, int fromStage, int toStag
 	const PrismPartDef pupilDef = {4, pupilPoints, fromPupil.surfaceCount, fromPupil.surfaces};
 	const int irisColor = (toStage == 3 && progress >= 0.5f) ? kColorMiniEyeIris : -1;
 	drawEyeOverlays3D(obj, irisDef, irisColor, pupilDef, pupilColor, false);
+	_gfx->setDepthState(true, true);
 }
 
 bool ColonyEngine::drawInterpolatedGrowRobot(Thing &obj, int eyeballColor, int pupilColor) {
@@ -1904,29 +1932,24 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			(obj.where.yloc - _me.yloc) * (obj.where.yloc - _me.yloc) <= 64 * 64) {
 			break;
 		}
+		// Writes off, as in drawEnemyEye3D().
+		_gfx->setDepthState(true, false);
 		draw3DSphere(obj, 0, 0, 100, 0, 0, 200, eyeballColor, kColorBlack, true);
 		drawEyeOverlays3D(obj, kEyeIrisDef, -1, kEyePupilDef, pupilColor, false);
+		_gfx->setDepthState(true, true);
 		break;
 	case kRobPyramid:
 		_gfx->setDepthRange(0.030f, 1.0f);
 		draw3DPrism(obj, kPShadowDef, false, -1, true, false);
 		_gfx->setDepthRange(0.020f, 1.0f);
 		draw3DPrism(obj, kPyramidBodyDef, false, -1, true, false);
-		_gfx->setDepthRange(0.004f, 1.0f);
-		_gfx->setDepthState(true, false);
-		_gfx->setDepthRange(0.0f, 1.0f);
-		draw3DSphere(obj, 0, 0, 175, 0, 0, 200, eyeballColor, kColorBlack, true);
-		drawEyeOverlays3D(obj, kPIrisDef, -1, kPPupilDef, pupilColor, false);
-		_gfx->setDepthState(true, true);
 		_gfx->setDepthRange(0.0f, 1.0f);
+		drawBodyEye3D(obj, eyeballColor, pupilColor, 106.0f);
 		break;
 	case kRobCube:
 		// DOS CUBE.C: body + draweye() (same shared eye as Pyramid/UPyramid)
 		draw3DPrism(obj, kCubeBodyDef, false, -1, true, false);
-		_gfx->setDepthState(true, false);
-		draw3DSphere(obj, 0, 0, 175, 0, 0, 200, eyeballColor, kColorBlack, true);
-		drawEyeOverlays3D(obj, kPIrisDef, -1, kPPupilDef, pupilColor, false);
-		_gfx->setDepthState(true, true);
+		drawBodyEye3D(obj, eyeballColor, pupilColor, 106.0f);
 		break;
 	case kRobUPyramid:
 		// DOS UPYRAMID.C: draweye() drawn first, then shadow, then body.
@@ -1935,19 +1958,17 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		draw3DPrism(obj, kUPShadowDef, false, -1, true, false);
 		_gfx->setDepthRange(0.020f, 1.0f);
 		draw3DPrism(obj, kUPyramidBodyDef, false, -1, true, false);
-		_gfx->setDepthRange(0.004f, 1.0f);
-		_gfx->setDepthState(true, false);
-		_gfx->setDepthRange(0.0f, 1.0f);
-		draw3DSphere(obj, 0, 0, 175, 0, 0, 200, eyeballColor, kColorBlack, true);
-		drawEyeOverlays3D(obj, kPIrisDef, -1, kPPupilDef, pupilColor, false);
-		_gfx->setDepthState(true, true);
 		_gfx->setDepthRange(0.0f, 1.0f);
+		// No pull: this eye sits in the top face and the near rim should clip it.
+		drawBodyEye3D(obj, eyeballColor, pupilColor, 0.0f);
 		break;
 	case kRobFEye:
 		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
 			break;
+		_gfx->setDepthState(true, false);
 		draw3DSphere(obj, 0, 0, 0, 0, 0, 100, eyeballColor, kColorBlack, true);
 		drawEyeOverlays3D(obj, kFEyeIrisDef, -1, kFEyePupilDef, pupilColor, false);
+		_gfx->setDepthState(true, true);
 		break;
 	case kRobFPyramid:
 		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
@@ -1967,8 +1988,10 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	case kRobSEye:
 		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
 			break;
+		_gfx->setDepthState(true, false);
 		draw3DSphere(obj, 0, 0, 0, 0, 0, 50, eyeballColor, kColorBlack, true);
 		drawEyeOverlays3D(obj, kSEyeIrisDef, -1, kSEyePupilDef, pupilColor, false);
+		_gfx->setDepthState(true, true);
 		break;
 	case kRobSPyramid:
 		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
@@ -1988,8 +2011,10 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	case kRobMEye:
 		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
 			break;
+		_gfx->setDepthState(true, false);
 		draw3DSphere(obj, 0, 0, 0, 0, 0, 25, eyeballColor, kColorBlack, true);
 		drawEyeOverlays3D(obj, kMEyeIrisDef, kColorMiniEyeIris, kMEyePupilDef, pupilColor, false);
+		_gfx->setDepthState(true, true);
 		break;
 	case kRobMPyramid:
 		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
@@ -2021,9 +2046,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			};
 
 			auto drawEye = [&](Thing &eye) {
-				draw3DSphere(eye, 0, 0, 130, 0, 0, 155, enemyEyeballColor, kColorBlack, true);
-				drawEyeOverlays3D(eye, kQIrisDef, kColorQueenEye, kQPupilDef, pupilColor, true);
-				mergeObjectBounds(obj.where, eye.where);
+				drawEnemyEye3D(obj, eye, enemyEyeballColor, kColorQueenEye, pupilColor);
 			};
 			auto drawWingEye = [&](Thing &eye, const PrismPartDef &wing) {
 				setNextDepthRange();
@@ -2081,9 +2104,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			};
 			auto drawEye = [&](Thing &eye) {
 				setNextDepthRange();
-				draw3DSphere(eye, 0, 0, 130, 0, 0, 155, enemyEyeballColor, kColorBlack, true);
-				drawEyeOverlays3D(eye, kQIrisDef, kColorDroneEye, kQPupilDef, pupilColor, true);
-				mergeObjectBounds(obj.where, eye.where);
+				drawEnemyEye3D(obj, eye, enemyEyeballColor, kColorDroneEye, pupilColor);
 			};
 
 			const int body = droneBodyOrder(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
@@ -2103,8 +2124,9 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			int leftPincerPts[4][3];
 			int rightPincerPts[4][3];
 			const int lookAmount = (obj.where.lookx < 0) ? -obj.where.lookx : obj.where.lookx;
-			const int leftLook = wrapAngle256(-lookAmount - 32);
-			const int rightLook = wrapAngle256(lookAmount - 32);
+			// DRONE.C's -32 is the phase-shifted table's, not an angle.
+			const int leftLook = wrapAngle256(-lookAmount);
+			const int rightLook = wrapAngle256(lookAmount);
 
 			for (int i = 0; i < 4; ++i) {
 				rotatePoint(leftLook, kDLLPincerPts[i], leftPincerPts[i], _cost, _sint);
@@ -2132,9 +2154,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			};
 			auto drawEye = [&](Thing &eye) {
 				setNextDepthRange();
-				draw3DSphere(eye, 0, 0, 130, 0, 0, 155, enemyEyeballColor, kColorBlack, true);
-				drawEyeOverlays3D(eye, kQIrisDef, kColorSoldierEye, kQPupilDef, pupilColor, true);
-				mergeObjectBounds(obj.where, eye.where);
+				drawEnemyEye3D(obj, eye, enemyEyeballColor, kColorSoldierEye, pupilColor);
 			};
 			auto drawPincers = [&]() {
 				if (leftPincerDepth < rightPincerDepth) {


Commit: 5814ae9fc30208feb85843bb49e010991506e639
    https://github.com/scummvm/scummvm/commit/5814ae9fc30208feb85843bb49e010991506e639
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-18T11:30:24+02:00

Commit Message:
COLONY: improved minimap symbols for enemies and objects

Changed paths:
    engines/colony/colony.h
    engines/colony/interaction.cpp
    engines/colony/think.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index fe14962fc9e..9fca4e9253e 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -722,6 +722,8 @@ private:
 	int findAimedObject(const Common::Point &aim, bool *isBlocker = nullptr, int *targetDist = nullptr) const;
 	bool hasAimedRobotTarget() const;
 	void destroyRobot(int num);
+	void explodeFlash(int silentFlips);
+	void invertViewport();
 	void doShootCircles(int cx, int cy);
 	void doBurnHole(int cx, int cy, int radius);
 	void meGetShot();
@@ -779,6 +781,11 @@ private:
 	void drawAutomapCryoMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
 	void drawAutomapTeleportMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
 	void drawAutomapForkliftMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
+	void drawAutomapQueenMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
+	void drawAutomapSnoopMarker(int x, int y, int halfSize, int dirX, int dirY, uint32 color, const Common::Rect &clip);
+	void drawAutomapDroneMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
+	void drawAutomapRobotMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
+	void drawAutomapObjectMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
 	void markVisited();
 	void automapCellCorner(int dx, int dy, int xloc, int yloc, int lExt, int tsin, int tcos, int ccx, int ccy, int &sx, int &sy);
 	void automapDrawWall(const Common::Rect &vp, int x1, int y1, int x2, int y2, uint32 color);
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index b0d89a3c6da..de43762fe9a 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -458,6 +458,39 @@ void ColonyEngine::cShoot() {
 	}
 }
 
+void ColonyEngine::invertViewport() {
+	_gfx->setXorMode(true);
+	_gfx->fillRect(_screenR, 0xFFFFFF);
+	_gfx->setXorMode(false);
+	_gfx->copyToScreen();
+	_system->updateScreen();
+}
+
+// InvertRect(&Clip) for as long as the explosion sound runs, or silentFlips
+// times when sound is off.
+void ColonyEngine::explodeFlash(int silentFlips) {
+	const uint32 kFlipMs = 40;
+	int flips = 0;
+	if (!_soundOn) {
+		for (; flips < silentFlips && !shouldQuit(); flips++) {
+			invertViewport();
+			_system->delayMillis(kFlipMs);
+		}
+	} else {
+		_sound->play(Sound::kExplode);
+		const uint32 start = _system->getMillis();
+		while (!shouldQuit() && _sound->isPlaying() &&
+				_system->getMillis() - start < 1200) {
+			invertViewport();
+			_system->delayMillis(kFlipMs);
+			flips++;
+		}
+		_sound->stop();
+	}
+	if (flips & 1)
+		invertViewport();
+}
+
 // shoot.c DestroyRobot(): player damages a robot.
 // Damage = (epower[0] * weapons^2) << 1. Robot dies when power[1] <= 0.
 void ColonyEngine::destroyRobot(int num) {
@@ -503,39 +536,12 @@ void ColonyEngine::destroyRobot(int num) {
 					_robotArray[gx][gy] = 0;
 				}
 			}
-			// Mac shoot.c: InvertRect(&Clip) strobe while the explosion sound
-			// plays (16 inversions with sound off). DOS SHOOT.C has no flash.
-			if (isMacRenderMode()) {
-				auto invertViewport = [this]() {
-					_gfx->setXorMode(true);
-					_gfx->fillRect(_screenR, 0xFFFFFF);
-					_gfx->setXorMode(false);
-					_gfx->copyToScreen();
-					_system->updateScreen();
-				};
-				const uint32 kFlipMs = 40;
-				int flips = 0;
-				if (!_soundOn) {
-					for (; flips < 16 && !shouldQuit(); flips++) {
-						invertViewport();
-						_system->delayMillis(kFlipMs);
-					}
-				} else {
-					_sound->play(Sound::kExplode);
-					const uint32 start = _system->getMillis();
-					while (!shouldQuit() && _sound->isPlaying() &&
-							_system->getMillis() - start < 1200) {
-						invertViewport();
-						_system->delayMillis(kFlipMs);
-						flips++;
-					}
-					_sound->stop();
-				}
-				if (flips & 1)
-					invertViewport();
-			} else {
+			// Mac shoot.c strobes the viewport for the explosion; DOS SHOOT.C
+			// only plays the sound.
+			if (isMacRenderMode())
+				explodeFlash(16);
+			else
 				_sound->play(Sound::kExplode);
-			}
 			copyOverflowObjectToSlot(num);
 			debugC(1, kColonyDebugCombat, "Robot %d destroyed!", num);
 		} else {
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index c7f75a1b038..50e8dc59566 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -307,7 +307,8 @@ void ColonyEngine::queenThink(int num) {
 
 	updatedObj.alive = 0;
 	_allGrow = false;
-	_sound->play(Sound::kExplode);
+	// Both builds strobe the viewport here; the silent fallback differs.
+	explodeFlash(isMacRenderMode() ? 16 : 4);
 
 	for (uint i = 0; i < _objects.size(); ++i) {
 		Thing &other = _objects[i];
@@ -337,7 +338,7 @@ void ColonyEngine::droneThink(int num) {
 			_robotArray[obj.where.xindex][obj.where.yindex] == num)
 			_robotArray[obj.where.xindex][obj.where.yindex] = 0;
 		obj.alive = 0;
-		_sound->play(Sound::kExplode);
+		explodeFlash(16);
 		copyOverflowObjectToSlot(num);
 	} else {
 		obj.type = kRobDrone;
@@ -852,9 +853,8 @@ void ColonyEngine::meEat() {
 	_foodArray[_me.xindex][_me.yindex] = 0;
 	obj.alive = 0;
 	_sound->play(Sound::kEat);
-	if (foodNum <= getColonyActiveRobotLimit())
-		copyOverflowObjectToSlot(foodNum);
 
+	// Read the type before CopyMax(), which can move another object into the slot.
 	switch (obj.type) {
 	case kRobMUPyramid:
 	case kRobFUPyramid:
@@ -879,6 +879,9 @@ void ColonyEngine::meEat() {
 	default:
 		break;
 	}
+
+	if (foodNum <= getColonyActiveRobotLimit())
+		copyOverflowObjectToSlot(foodNum);
 }
 
 } // End of namespace Colony
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 229580198ee..c8729abbd50 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -1047,6 +1047,88 @@ void ColonyEngine::drawAutomapForkliftMarker(int x, int y, int halfSize, uint32
 	}
 }
 
+// display.c: the queen's glyph is a box with one side left open.
+void ColonyEngine::drawAutomapQueenMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip) {
+	if (x < clip.left || x >= clip.right || y < clip.top || y >= clip.bottom)
+		return;
+
+	const int r = MAX(halfSize, 2);
+	const int seg[3][4] = {
+		{ -r, -r,  r, -r },
+		{ -r,  r,  r,  r },
+		{ -r, -r, -r,  r }
+	};
+	for (int i = 0; i < 3; i++) {
+		int x1 = x + seg[i][0], y1 = y + seg[i][1];
+		int x2 = x + seg[i][2], y2 = y + seg[i][3];
+		if (clipLineToRect(x1, y1, x2, y2, clip))
+			_gfx->drawLine(x1, y1, x2, y2, color);
+	}
+}
+
+// display.c: the snoop is the one robot plotted at its exact position and with
+// a whisker showing where it is headed.
+void ColonyEngine::drawAutomapSnoopMarker(int x, int y, int halfSize, int dirX, int dirY, uint32 color, const Common::Rect &clip) {
+	if (x < clip.left || x >= clip.right || y < clip.top || y >= clip.bottom)
+		return;
+
+	const int r = MAX(halfSize, 2);
+	const int dx[4] = { 0,  r, 0, -r };
+	const int dy[4] = { -r, 0, r,  0 };
+	for (int i = 0; i < 4; i++) {
+		int x1 = x + dx[i], y1 = y + dy[i];
+		int x2 = x + dx[(i + 1) & 3], y2 = y + dy[(i + 1) & 3];
+		if (clipLineToRect(x1, y1, x2, y2, clip))
+			_gfx->drawLine(x1, y1, x2, y2, color);
+	}
+
+	int hx1 = x, hy1 = y, hx2 = x + dirX, hy2 = y + dirY;
+	if (clipLineToRect(hx1, hy1, hx2, hy2, clip))
+		_gfx->drawLine(hx1, hy1, hx2, hy2, color);
+}
+
+// display.c drawmap(): drones and soldiers are an X, every other robot a cross.
+void ColonyEngine::drawAutomapDroneMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip) {
+	if (x < clip.left || x >= clip.right || y < clip.top || y >= clip.bottom)
+		return;
+
+	const int r = MAX(halfSize, 2);
+	int x1 = x - r, y1 = y - r, x2 = x + r, y2 = y + r;
+	if (clipLineToRect(x1, y1, x2, y2, clip))
+		_gfx->drawLine(x1, y1, x2, y2, color);
+	x1 = x - r; y1 = y + r; x2 = x + r; y2 = y - r;
+	if (clipLineToRect(x1, y1, x2, y2, clip))
+		_gfx->drawLine(x1, y1, x2, y2, color);
+}
+
+void ColonyEngine::drawAutomapRobotMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip) {
+	if (x < clip.left || x >= clip.right || y < clip.top || y >= clip.bottom)
+		return;
+
+	const int r = MAX(halfSize, 2);
+	int x1 = x - r, y1 = y, x2 = x + r, y2 = y;
+	if (clipLineToRect(x1, y1, x2, y2, clip))
+		_gfx->drawLine(x1, y1, x2, y2, color);
+	x1 = x; y1 = y - r; x2 = x; y2 = y + r;
+	if (clipLineToRect(x1, y1, x2, y2, clip))
+		_gfx->drawLine(x1, y1, x2, y2, color);
+}
+
+// Furniture is the most numerous mark, so it stays a quiet solid pip rather than
+// another outline competing with the robot and vehicle glyphs.
+void ColonyEngine::drawAutomapObjectMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip) {
+	if (x < clip.left || x >= clip.right || y < clip.top || y >= clip.bottom)
+		return;
+
+	const int r = MAX(halfSize, 1);
+	const int l = MAX<int>(clip.left, x - r);
+	const int t = MAX<int>(clip.top, y - r);
+	const int rt = MIN<int>(clip.right, x + r + 1);
+	const int b = MIN<int>(clip.bottom, y + r + 1);
+	if (l < rt && t < b)
+		_gfx->fillRect(Common::Rect(l, t, rt, b), color);
+}
+
 void ColonyEngine::drawAutomap() {
 	if (_level < 1 || _level > 8)
 		return;
@@ -1091,6 +1173,9 @@ void ColonyEngine::drawAutomap() {
 	const int cryoR = scaleR(isMac ? 7 : 5, 3);
 	const int teleR = scaleR(isMac ? 6 : 4, 3);
 	const int forkR = scaleR(isMac ? 6 : 4, 3);
+	const int queenR = scaleR(isMac ? 5 : 3, 2);
+	const int snoopR = scaleR(isMac ? 4 : 3, 2);
+	const int objR = scaleR(isMac ? 2 : 2, 1);
 
 	for (int dy = -radius; dy <= radius; dy++) {
 		for (int dx = -radius; dx <= radius; dx++) {
@@ -1118,19 +1203,39 @@ void ColonyEngine::drawAutomap() {
 
 			const int mx = (x0 + x2) >> 1;
 			const int my = (y0 + y2) >> 1;
-			// Cryo pods, teleporters and the forklift are static, so they map
-			// beyond the robot/egg radar range.
 			const bool inRadar = (ABS(dx) <= 6 && ABS(dy) <= 6);
 			const uint8 rnum = _robotArray[cx][cy];
 			if (rnum > 0 && rnum != kMeNum && rnum <= _objects.size() && _objects[rnum - 1].alive) {
-				if (_objects[rnum - 1].type == kObjCryo)
-					drawAutomapCryoMarker(mx, my, cryoR, lineColor, vp);
-				else if (_objects[rnum - 1].type == kObjTeleport)
-					drawAutomapTeleportMarker(mx, my, teleR, lineColor, vp);
-				else if (_objects[rnum - 1].type == kObjForkLift)
-					drawAutomapForkliftMarker(mx, my, forkR, lineColor, vp);
-				else if (inRadar)
-					drawMiniMapMarker(mx, my, markerR, lineColor, isMac, &vp);
+				const Thing &mapObj = _objects[rnum - 1];
+				// Robots and eggs are radar contacts; furniture never moves, so it
+				// keeps its mark in every cell already visited.
+				const bool isRobot = mapObj.type >= kRobEye && mapObj.type <= kRobSnoop;
+				if (!isRobot || inRadar) {
+					if (mapObj.type == kObjCryo)
+						drawAutomapCryoMarker(mx, my, cryoR, lineColor, vp);
+					else if (mapObj.type == kObjTeleport)
+						drawAutomapTeleportMarker(mx, my, teleR, lineColor, vp);
+					else if (mapObj.type == kObjForkLift)
+						drawAutomapForkliftMarker(mx, my, forkR, lineColor, vp);
+					else if (mapObj.type == kRobQueen)
+						drawAutomapQueenMarker(mx, my, queenR, lineColor, vp);
+					else if (mapObj.type == kRobSnoop) {
+						const int32 sox = xloc + ((((int32)dx << 8) + (mapObj.where.xloc - (cx << 8))) * lExt >> 8);
+						const int32 soy = yloc + ((((int32)dy << 8) + (mapObj.where.yloc - (cy << 8))) * lExt >> 8);
+						const int sx = ccx + (int)((sox * tsin - soy * tcos) >> 8);
+						const int sy = ccy - (int)((soy * tsin + sox * tcos) >> 8);
+						const uint8 sa = (uint8)(mapObj.where.ang + 32);
+						const int ux = (_cost[sa] * tsin - _sint[sa] * tcos) >> 8;
+						const int uy = -((_sint[sa] * tsin + _cost[sa] * tcos) >> 8);
+						const int len = MAX(snoopR * 4 / 3, 2);
+						drawAutomapSnoopMarker(sx, sy, snoopR, ux * len / 64, uy * len / 64, lineColor, vp);
+					} else if (mapObj.type == kRobDrone || mapObj.type == kRobSoldier)
+						drawAutomapDroneMarker(mx, my, markerR, lineColor, vp);
+					else if (isRobot)
+						drawAutomapRobotMarker(mx, my, markerR, lineColor, vp);
+					else
+						drawAutomapObjectMarker(mx, my, objR, lineColor, vp);
+				}
 			}
 			if (inRadar && _foodArray[cx][cy] > 0)
 				drawMiniMapMarker(mx, my, foodR, lineColor, isMac, &vp);


Commit: 9df0a7e21e72e6c6c5f571f3a6b28ddd3d10cdb0
    https://github.com/scummvm/scummvm/commit/9df0a7e21e72e6c6c5f571f3a6b28ddd3d10cdb0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-18T11:30:24+02:00

Commit Message:
COLONY: corrected enemy behavior when facing the player

Changed paths:
    engines/colony/battle.cpp
    engines/colony/colony.h
    engines/colony/console.cpp
    engines/colony/interaction.cpp
    engines/colony/movement.cpp
    engines/colony/render_objects.cpp
    engines/colony/think.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 6503b0553c2..9574a8c9b6e 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -884,8 +884,9 @@ void ColonyEngine::battleDrawTanks() {
 
 void ColonyEngine::battleThink() {
 	if (_projon) {
-		const int fx = battleNormalizeCoord(_battleProj.xloc + (_cost[_battleProj.ang] * 4));
-		const int fy = battleNormalizeCoord(_battleProj.yloc + (_sint[_battleProj.ang] * 4));
+		const uint8 pang = objWorldAng(_battleProj.ang);
+		const int fx = battleNormalizeCoord(_battleProj.xloc + (_cost[pang] * 4));
+		const int fy = battleNormalizeCoord(_battleProj.yloc + (_sint[pang] * 4));
 		if (0 == (_pcount--))
 			_projon = false;
 		battleProjCommand(fx, fy);
@@ -924,7 +925,7 @@ void ColonyEngine::battleThink() {
 			tooFar = true;
 		}
 
-		int32 dir = dx * _sint[ang] - dy * _cost[ang];
+		int32 dir = dx * _sint[objWorldAng(ang)] - dy * _cost[objWorldAng(ang)];
 		if (!tooFar) {
 			distance = (int32)sqrt((double)(dx * dx + dy * dy));
 			if (distance > 0) {
@@ -954,8 +955,8 @@ void ColonyEngine::battleThink() {
 				ang += 4;
 		}
 
-		const int fx = _bfight[i].xloc + (_cost[ang] >> 2);
-		const int fy = _bfight[i].yloc + (_sint[ang] >> 2);
+		const int fx = _bfight[i].xloc + (_cost[objWorldAng(ang)] >> 2);
+		const int fy = _bfight[i].yloc + (_sint[objWorldAng(ang)] >> 2);
 		if (distance > 250 || tooFar) {
 			if ((!_orbit) &&
 				fx > _battleShip.xloc - 2 * kBattleSize &&
@@ -984,8 +985,8 @@ void ColonyEngine::battleThink() {
 		_sound->play(Sound::kShoot);
 		_battleProj.ang = _bfight[shooter].ang;
 		_battleProj.look = _bfight[shooter].look;
-		_battleProj.xloc = battleNormalizeCoord(_bfight[shooter].xloc + (_cost[_battleProj.ang] * 2));
-		_battleProj.yloc = battleNormalizeCoord(_bfight[shooter].yloc + (_sint[_battleProj.ang] * 2));
+		_battleProj.xloc = battleNormalizeCoord(_bfight[shooter].xloc + (_cost[objWorldAng(_battleProj.ang)] * 2));
+		_battleProj.yloc = battleNormalizeCoord(_bfight[shooter].yloc + (_sint[objWorldAng(_battleProj.ang)] * 2));
 		debugC(1, kColonyDebugCombat,
 			"battleEnemyShoot: enemy=%d pos=(%d,%d) ang=%d proj=(%d,%d)",
 			shooter, _bfight[shooter].xloc, _bfight[shooter].yloc, _battleProj.ang,
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 9fca4e9253e..b2868f854e4 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -317,6 +317,12 @@ enum MenuIndex {
 	kMenuOptions
 };
 
+// Object and robot angles keep the original's convention, where the sine table's
+// own 45-degree phase supplied the last 32 steps; player angles are already
+// world-absolute. Convert whenever one is used as the other.
+uint8 objWorldAng(uint8 objectAng);
+uint8 objAngFromPlayer(uint8 playerAng);
+
 static const int kBaseObject = 20;
 static const int kMeNum = 101;
 
@@ -645,6 +651,10 @@ private:
 	int _direction = 0;
 
 	float _eyeDepthPull = 0.0f; // world units the eye parts are pulled at the camera
+	// think.c: the snoop's snout bobs while it hunts (sniff/csniff).
+	int _snoopSnoutZ = 0;
+	int _snoopSniff = 5;
+	int _snoopSniffCount = 0;
 
 	Common::Rect _clip;
 	Common::Rect _screenR;
diff --git a/engines/colony/console.cpp b/engines/colony/console.cpp
index 63835093b0b..b5319d50f37 100644
--- a/engines/colony/console.cpp
+++ b/engines/colony/console.cpp
@@ -517,7 +517,7 @@ bool Debugger::cmdSpawn(int argc, const char **argv) {
 
 	int xloc = (targetX << 8) + 128;
 	int yloc = (targetY << 8) + 128;
-	uint8 ang = _vm->_me.ang + 128; // face the player
+	uint8 ang = objAngFromPlayer((uint8)(_vm->_me.ang + 128)); // face the player
 
 	if (!_vm->createObject(type, xloc, yloc, ang)) {
 		debugPrintf("Failed to create object (no free slot?)\n");
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index de43762fe9a..212836f6c66 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -206,7 +206,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 					newPatch(kObjForkLift, from, _carryPatch[0], _mapData[CLIP<int>(from.xindex, 0, 30)][CLIP<int>(from.yindex, 0, 30)][4]);
 					_robotArray[obj.where.xindex][obj.where.yindex] = 0;
 					_objects[objNum - 1].alive = 0;
-					_me.look = _me.ang = obj.where.ang;
+					_me.look = _me.ang = objWorldAng(obj.where.ang);
 					_me.lookY = 0;
 					_fl = 1;
 				}
@@ -516,7 +516,7 @@ void ColonyEngine::destroyRobot(int num) {
 		obj.opcode = 4; // FSHOOT
 
 	// Face robot towards player
-	obj.where.look = obj.where.ang = (uint8)(_me.ang + 128);
+	obj.where.look = obj.where.ang = objAngFromPlayer((uint8)(_me.ang + 128));
 
 	obj.where.power[1] -= damage;
 	debugC(1, kColonyDebugAnimation, "DestroyRobot(%d): type=%d damage=%d remaining_hp=%d",
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 678e125e5bc..e1d9d637276 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -389,7 +389,7 @@ int ColonyEngine::occupiedObjectAt(int xnew, int ynew, int x, int y, const Locat
 				!playerIntersectsObjectFootprint(obj, xnew, ynew))
 			return 0;
 		if (obj.type <= kBaseObject)
-			obj.where.look = obj.where.ang = _me.ang + 128;
+			obj.where.look = obj.where.ang = objAngFromPlayer((uint8)(_me.ang + 128));
 	}
 	return rnum;
 }
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index dd7828673bf..59b1434a743 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -2195,10 +2195,16 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			_gfx->setDepthRange(0.0f, 1.0f);
 		}
 		break;
-	case kRobSnoop:
+	case kRobSnoop: {
+		// think.c raises and drops the snout tip as the snoop sniffs.
+		int headPts[4][3];
+		memcpy(headPts, kSnoopHeadPts, sizeof(headPts));
+		headPts[1][2] = _snoopSnoutZ;
+		const PrismPartDef headDef = {4, headPts, 3, kSnoopHeadSurf};
 		draw3DPrism(obj, kSnoopAbdomenDef, false, -1, true, false);
-		draw3DPrism(obj, kSnoopHeadDef, false, -1, true, false);
+		draw3DPrism(obj, headDef, false, -1, true, false);
 		break;
+	}
 	default:
 		return false;
 	}
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index 50e8dc59566..de1c3b93db4 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -40,6 +40,14 @@ enum {
 	kOpcodeSnoop = 20
 };
 
+uint8 objWorldAng(uint8 objectAng) {
+	return (uint8)(objectAng + 32);
+}
+
+uint8 objAngFromPlayer(uint8 playerAng) {
+	return (uint8)(playerAng - 32);
+}
+
 bool isBaseRobotType(int type) {
 	return type >= kRobEye && type <= kRobUPyramid;
 }
@@ -440,8 +448,9 @@ void ColonyEngine::moveThink(int num) {
 			_robotArray[obj.where.xindex][obj.where.yindex] = 0;
 
 		_suppressCollisionSound = true;
-		const int collide = checkwall(obj.where.xloc + (_cost[obj.where.ang] >> 2),
-			obj.where.yloc + (_sint[obj.where.ang] >> 2), &obj.where);
+		const uint8 wang = objWorldAng(obj.where.ang);
+		const int collide = checkwall(obj.where.xloc + (_cost[wang] >> 2),
+			obj.where.yloc + (_sint[wang] >> 2), &obj.where);
 		_suppressCollisionSound = false;
 
 		if (collide) {
@@ -462,8 +471,9 @@ void ColonyEngine::moveThink(int num) {
 			_robotArray[obj.where.xindex][obj.where.yindex] = 0;
 
 		_suppressCollisionSound = true;
-		const int collide = checkwall(obj.where.xloc + obj.where.dx + (_me.dx >> 2) + (_cost[obj.where.ang] >> 2),
-			obj.where.yloc + obj.where.dy + (_me.dy >> 2) + (_sint[obj.where.ang] >> 2), &obj.where);
+		const uint8 wang = objWorldAng(obj.where.ang);
+		const int collide = checkwall(obj.where.xloc + obj.where.dx + (_me.dx >> 2) + (_cost[wang] >> 2),
+			obj.where.yloc + obj.where.dy + (_me.dy >> 2) + (_sint[wang] >> 2), &obj.where);
 		_suppressCollisionSound = false;
 
 		if (collide) {
@@ -500,6 +510,12 @@ void ColonyEngine::snoopThink(int num) {
 	if (!obj.alive)
 		return;
 
+	_snoopSnoutZ += _snoopSniff;
+	if (++_snoopSniffCount == 25) {
+		_snoopSniff = -_snoopSniff;
+		_snoopSniffCount = 0;
+	}
+
 	switch (obj.opcode) {
 	case kOpcodeLRotate:
 		obj.where.ang = (uint8)(obj.where.ang + 7);
@@ -534,8 +550,9 @@ void ColonyEngine::snoopThink(int num) {
 		if (oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32 && _robotArray[oldX][oldY] == num)
 			_robotArray[oldX][oldY] = 0;
 
-		const int fx = obj.where.xloc + (_cost[obj.where.ang] >> 2);
-		const int fy = obj.where.yloc + (_sint[obj.where.ang] >> 2);
+		const uint8 wang = objWorldAng(obj.where.ang);
+		const int fx = obj.where.xloc + (_cost[wang] >> 2);
+		const int fy = obj.where.yloc + (_sint[wang] >> 2);
 		_suppressCollisionSound = true;
 		const int collide = checkwall(fx, fy, &obj.where);
 		_suppressCollisionSound = false;
@@ -717,14 +734,15 @@ int ColonyEngine::scanForPlayer(int num) {
 	int fireX = obj.where.xloc;
 	int fireY = obj.where.yloc;
 	int collide = 0;
+	const uint8 wang = objWorldAng(fire.ang);
 
 	do {
 		fire.xloc = fireX;
 		fire.yloc = fireY;
 		fire.xindex = fireX >> 8;
 		fire.yindex = fireY >> 8;
-		fireX += _cost[fire.ang] * 2;
-		fireY += _sint[fire.ang] * 2;
+		fireX += _cost[wang] * 2;
+		fireY += _sint[wang] * 2;
 		_suppressCollisionSound = true;
 		collide = checkwall(fireX, fireY, &fire);
 		_suppressCollisionSound = false;


Commit: 34a88404587f8c331ae6abc0675b4964d4524ad6
    https://github.com/scummvm/scummvm/commit/34a88404587f8c331ae6abc0675b4964d4524ad6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-18T11:30:24+02:00

Commit Message:
COLONY: fixed egg rendering artifact

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index eaca25a9ead..df6437b0049 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -719,19 +719,36 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 	float dx = wx1 - wx0, dy = wy1 - wy0, dz = wz1 - wz0;
 	float radius = sqrtf(dx * dx + dy * dy + dz * dz);
 
-	// Billboard: create a polygon perpendicular to the camera direction.
+	// Billboard turned to face the camera in 3D. The original drew the ball as a
+	// screen-space oval and never tilted the view; keeping the disc upright in
+	// world Z instead flattens it to a sliver when looking down at a floor egg.
 	// Camera is at (_me.xloc, _me.yloc, 0).
-	float viewDx = cx - (float)_me.xloc;
-	float viewDy = cy - (float)_me.yloc;
-	float viewLen = sqrtf(viewDx * viewDx + viewDy * viewDy);
+	float viewX = cx - (float)_me.xloc;
+	float viewY = cy - (float)_me.yloc;
+	float viewZ = cz;
+	float viewLen = sqrtf(viewX * viewX + viewY * viewY + viewZ * viewZ);
 	if (viewLen < 0.001f)
 		return;
+	viewX /= viewLen;
+	viewY /= viewLen;
+	viewZ /= viewLen;
+
+	// right = worldUp x view, collapsing to +X when looking straight down.
+	float rightX = -viewY;
+	float rightY = viewX;
+	float rightLen = sqrtf(rightX * rightX + rightY * rightY);
+	if (rightLen < 0.001f) {
+		rightX = 1.0f;
+		rightY = 0.0f;
+		rightLen = 1.0f;
+	}
+	rightX /= rightLen;
+	rightY /= rightLen;
 
-	// "right" vector: perpendicular to view in XY plane
-	float rightX = -viewDy / viewLen;
-	float rightY = viewDx / viewLen;
-	// "up" vector: world Z axis
-	float upZ = 1.0f;
+	// up = view x right; right has no Z component, so two terms drop out.
+	const float upX = -viewZ * rightY;
+	const float upY = viewZ * rightX;
+	const float upZ = viewX * rightY - viewY * rightX;
 
 	// Create 12-sided polygon
 	const int N = 12;
@@ -740,8 +757,8 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 		float a = (float)i * 2.0f * (float)M_PI / (float)N;
 		float cosA = cosf(a);
 		float sinA = sinf(a);
-		px[i] = cx + radius * (cosA * rightX);
-		py[i] = cy + radius * (cosA * rightY);
+		px[i] = cx + radius * (cosA * rightX + sinA * upX);
+		py[i] = cy + radius * (cosA * rightY + sinA * upY);
 		pz[i] = cz + radius * (sinA * upZ);
 	}
 


Commit: e9896e53a9af49da32ae605750105b4810fda2f2
    https://github.com/scummvm/scummvm/commit/e9896e53a9af49da32ae605750105b4810fda2f2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-18T12:34:44+02:00

Commit Message:
COLONY: fixed some core related code conditions in different controls

Changed paths:
    engines/colony/animation.cpp
    engines/colony/battle.cpp
    engines/colony/console.cpp
    engines/colony/map.cpp
    engines/colony/movement.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 80e658234e7..02607ade341 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -2099,27 +2099,28 @@ void ColonyEngine::handleTeleportClick(int item) {
 void ColonyEngine::handleControlsClick(int item) {
 	switch (item) {
 	case 4: // Accelerator
+		// GANIMATE.C: if(corepower<2) DoStopSound(); else if(corestate!=0) DoStopSound();
 		if (_corePower[_coreIndex] < 2 || _coreState[_coreIndex] != 0) {
-			// GANIMATE.C: if(corepower<2) DoStopSound(); else if(corestate!=0) DoStopSound();
 			_sound->play(Sound::kStop);
 			debugC(1, kColonyDebugAnimation, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
 			setObjectState(4, 1);
-			for (int i = 6; i > 0; i--) {
-				setObjectState(4, i);
-				drawAnimation();
-				_gfx->copyToScreen();
-				responsiveAnimationDelay(_system, 20);
-			}
-			break;
-		}
-
-		_animationRunning = false;
-		if (_orbit) {
+		} else if (_orbit) {
+			_animationRunning = false;
 			gameOver(false);
+			return;
 		} else {
 			takeOff();
 			_orbit = 1;
 		}
+
+		// The lever sweeps whether or not it fired, and the console stays open
+		// so the next press can launch.
+		for (int i = 6; i > 0; i--) {
+			setObjectState(4, i);
+			drawAnimation();
+			_gfx->copyToScreen();
+			responsiveAnimationDelay(_system, 20);
+		}
 		break;
 	case 5: // Emergency power
 		// setObjectState(5, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 9574a8c9b6e..b019fe1b1cf 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -1006,7 +1006,6 @@ void ColonyEngine::enterColonyFromBattle(int mapNum, int xloc, int yloc) {
 	_me.xindex = _me.xloc >> 8;
 	_me.yindex = _me.yloc >> 8;
 	loadMap(mapNum);
-	_coreIndex = (mapNum == 1) ? 0 : 1;
 }
 
 void ColonyEngine::battleCommand(int xnew, int ynew) {
diff --git a/engines/colony/console.cpp b/engines/colony/console.cpp
index b5319d50f37..e64cb2f89bd 100644
--- a/engines/colony/console.cpp
+++ b/engines/colony/console.cpp
@@ -122,7 +122,6 @@ bool Debugger::cmdTeleport(int argc, const char **argv) {
 	// Load the target level
 	if (level != _vm->_level)
 		_vm->loadMap(level);
-	_vm->_coreIndex = (level == 1) ? 0 : 1;
 
 	// If no coordinates given, scan for an entry point (stairs/tunnel/elevator)
 	if (targetX < 0) {
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index ed7263eeffe..9b68d0bcbb9 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -166,6 +166,9 @@ void ColonyEngine::loadMap(int mnum) {
 	_robotNum = MAX<int>(_robotNum, (int)_objects.size() + 1);
 	_bumpedObject = 0; // object numbers are per-level
 	_level = mnum;
+	// gamefile.c load_mapnum(): coreindex follows the map, so every route into a
+	// level picks up the right reactor.
+	_coreIndex = (mnum == 1) ? 0 : 1;
 	_me.type = kMeNum;
 
 	getWall();  // restore saved wall state changes (airlocks)
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index e1d9d637276..297ab2ad35b 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -798,10 +798,8 @@ int ColonyEngine::goToDestination(const uint8 *map, Locate *pobject) {
 		pobject->yindex = targetY;
 	}
 
-	if (targetMap > 0 && targetMap != _level) {
+	if (targetMap > 0 && targetMap != _level)
 		loadMap(targetMap);
-		_coreIndex = (targetMap == 1) ? 0 : 1;
-	}
 
 	if (pobject->xindex >= 0 && pobject->xindex < 32 &&
 		pobject->yindex >= 0 && pobject->yindex < 32)
@@ -949,10 +947,8 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 				pobject->look = pobject->ang;
 			}
 
-			if (targetMap > 0 && targetMap != _level) {
+			if (targetMap > 0 && targetMap != _level)
 				loadMap(targetMap);
-				_coreIndex = (targetMap == 1) ? 0 : 1;
-			}
 
 			if (pobject->xindex >= 0 && pobject->xindex < 32 &&
 				pobject->yindex >= 0 && pobject->yindex < 32)




More information about the Scummvm-git-logs mailing list