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

sev- noreply at scummvm.org
Fri Apr 3 13:35:23 UTC 2026


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

Summary:
5d1b2fea92 COLONY: basic loading of maps and rendering
9164033a04 COLONY: basic collision detection
9be5b46813 COLONY: implement corridor side-cell traversal and screen-edge clipping
3ec672d0a6 COLONY: add wall feature rendering (doors, windows, shelves, stairs)
ae272e3051 COLONY: add configure.engine registration file
4fb016c952 COLONY: some static object rendering code
e089f5bc05 COLONY: start to implement UI
55f39fbc2f COLONY: implement minimap rendering and HUD layout
f50db5ff2f COLONY: add compass ellipse drawing and filter minimap objects
a4160d479c COLONY: add 3D prism object rendering system
db57cb7ad2 COLONY: initial position
2c673bd901 COLONY: add 3D geometry for chair object
536746c788 COLONY: add occupiedObjectAt() collision detection for movement blocking
5bd78c0e6f COLONY: fix integer overflow in perspective projection using int64
f154dd5cc4 COLONY: keep minimap inside box
5caa532151 COLONY: add crosshair rendering and object interaction handling
73467a5053 COLONY: add 3D geometry for corridor wall, plant pot and stem objects
d2558d681c COLONY: split colony.cpp into render.cpp and ui.cpp
7830cab359 COLONY: initial text
b60004bc46 COLONY: add dither fill for Mac desktop pattern and improve HUD layout
9c0cd2c3e2 COLONY: adding initial animations code
88d55733f2 COLONY: refactor animation loop to use frame-based sprite rendering
d709c24db6 COLONY: migration to true 3d
071d5cc88c COLONY: removed most of the old 2d code
8212ca241f COLONY: fixed z-position of objects
fca94c9d83 COLONY: removed old sin tables
0ad4a21d8e COLONY: fixed movement
09442d12d0 COLONY: wireframe solid mode
cfb605ecea COLONY: fix object vertex overflow by clamping coordinates to -127..127
213d17b0ab COLONY: wall features
140d2c24e3 COLONY: delete unused software renderer stub
2f005ccd5b COLONY: fix z-fighting with glPolygonOffset and improved depth testing
c33f5707f0 COLONY: modern controls
6dbbde9602 COLONY: wall features
8ead128432 COLONY: widescreen support
0f12c493db COLONY: implement animation system with loadAnimation/playAnimation
e667e8c1da COLONY: add mouse click handling and center animation viewport
4c14336caf COLONY: implement sprite rendering with per-pixel clipping for animations
48916b5d93 COLONY: allow to turn lights on
5738756cb7 COLONY: add surface color selection for lit/dark render modes
72e37d927c COLONY: assign per-object surface color indices across all props
efe107c49c COLONY: expand 3D object geometry with additional surface definitions
57097beff2 COLONY: improved doors
8f543d1cfe COLONY: add ceiling detail rendering
29fd55b2e1 COLONY: add 3D geometry definitions for additional room objects
56f6ae3d5b COLONY: backface culling
3e02a5a548 COLONY: fix object vertex overflow by clamping -128 to -127
dd2cfc0aee COLONY: implement in-game message display system
a627906fd9 COLONY: implement desk animation handler with keypad decode logic
309c531bc1 COLONY: trigger desk animation on object interaction
8f78e7ef1b COLONY: fix setWireframe to pass uint32 color instead of int cast
ffa4a3543e COLONY: add mouse delta tracking and keypad button reset in animation mode
34f60ad43a COLONY: fix animation sprite positioning to use screen-relative coordinates
78ae732363 COLONY: change crosshair color to black when core power is active
7514fe5b90 COLONY: implement sound effects playback
2497037be8 COLONY: add named color constants enum for object rendering
ddded293ca COLONY: allow movement between levels
b9e2b05eef COLONY: compute visible cells
cbe8e2aacc COLONY: implement door and airlock open/close animation handlers
9dbf803311 COLONY: implement vanity/mirror animation with suit and inventory display
5e1d8a4727 COLONY: add pixel-perfect sprite hit-testing for animation clicks
375a7fcb1c COLONY: implement drag-to-move object interaction with link groups
5f4ab536cf COLONY: add DOS movement speed/rotation controls
b31166ae48 COLONY: add 3D leaf rendering for plant objects
5dc287f345 COLONY: power suit
96fb82caaf COLONY: add cryo chamber and forklift 3D object geometry
191556528b COLONY: frame limit
b2a94f4482 COLONY: fix airlock door rendering with octagon grid and wireframe outline
7b4726fe2d COLONY: mac render mode
9a7830e8c4 COLONY: add stipple pattern for wall holes and BFS room visibility culling
42254e584b COLONY: handle kColorClear surfaces as wireframe-only outlines
dc7af136b3 COLONY: add bench 3D object geometry
e0abaf317f COLONY: add animated COLOR wall feature and Mac stipple hole rendering
2d43ff6567 COLONY: implement PATCH.C object relocation system
121094cfc7 COLONY: robot rendering
55f821b6a3 COLONY: adjusted code to follow the code conventions
b93bfeac8b COLONY: initial mac support
69a21736a3 COLONY: improved mac intro
743a8862da COLONY: auto-detect render mode from platform and add Mac color pipeline
6ce1a95141 GRAPHICS: MACGUI: allow to skip g_system->copyRectToScreen (for 3D game backends)
c86598222d COLONY: initial mac gui support
777dd6e82d COLONY: support for mac fonts
c478c5c541 IMAGE: correctly decode PICT images with offsets
582f93c08e COLONY: rewrite intro sequence to match original with sound sync
470fdf2285 COLONY: mac sounds correctly loaded
eaad9cc0e0 COLONY: fix OpenGL depth test and wireframe color type mismatch
7927bb2c9b COLONY: add Mac color dashboard with PICT-based power/compass panels
cb4fbc4242 COLONY: load color images
8230c950a0 COLONY: refine Mac dashboard layout positioning and dithered background
9e08e49ceb COLONY: add Mac color animation table and resolveAnimColor lookup
72ba36e237 COLONY: fix animation color index offsets in HUD drawing
300e51bed3 COLONY: fullscreen key for mac color
a045551347 COLONY: simplify main loop, add power drain counter, clean up HUD
f45f6dc6ae COLONY: add Mac menu bar checkbox sync and cursor initialization
b18fd2a1a5 COLONY: avoid z-fighting in objects
5487317be6 COLONY: implement DOS EGA color table from ROBOCOLR.C
a451973a60 COLONY: regression fixes for Mac (b&w)
156e5b5a7f COLONY: fix DOS polyfill to use B&W fill with colored outline
1f15206feb COLONY: fix crash decoding mac animations
856082f8dc COLONY: use original mac ui for b&w
d24de62a89 COLONY: expand one-line conditionals to blocks and add code comments
ea2ff8bfda COLONY: font color
a54c83f86a COLONY: normalize comment characters and whitespace
0d8367edc5 COLONY: register engine keymap bindings
f5312417f5 COLONY: extract animation, interaction, intro, map and movement modules
31e4f99a8c COLONY: refactored ColonyEngine::handleAnimationClick
126c50afc5 COLONY: debug channels
4a9a9ab5ed COLONY: console debugger
c91464edec COLONY: load correctly animations in dos/mac
1fd420eccf COLONY: pulsating surfaces
6899636170 COLONY: reactor animation
b0a605186b COLONY: suit setup
6e3ad6184a COLONY: initial code for battle
5160af2837 COLONY: add perspective battle horizon and fix debugger command registration
1cc5cfb3c8 COLONY: movement and targets in battle mode
15a8b373b4 COLONY: fix battle projectile shift-to-multiply and minimap bounds guard
1a807c097c COLONY: add reactor core and cryo chamber Mac color geometry
76b5db1843 COLONY: correctly play mac animations
7393a8b5a2 COLONY: fix Mac color palette mapping for animations
f8f970bba2 COLONY: proper tunnel effect
1a9e3c6f5f COLONY: airlock rendering fixes
ce693ddc05 COLONY: airlock logic fixes
76928886dc COLONY: add ship launch animation, weapon controls and skip-intro support
5f29c6786a COLONY: implement robot AI think logic and easter eggs
07b0a11d0e COLONY: savegames
2db4ea2c36 COLONY: add battle aim-point tracking and expand render color handling
69e4b56217 COLONY: fix ASAN crash
4df0462027 COLONY: improve robot spawn positioning
db68ee1e1e COLONY: refine debug channel categories and fix minor issues
21e8f03156 COLONY: unlock airlocks when using debug console
eb9558aa69 COLONY: improve proportional letter spacing in text rendering
90a41977ed COLONY: save/load fixes
42564fea38 COLONY: improved battle mode rendering
f7352da5cf COLONY: split render.cpp into render_features.cpp and render_objects.cpp
adb4d83fbe COLONY: apply ScummVM coding standards formatting
49765868b2 COLONY: inline gfx.h contents into colony.h and remove separate header
8d9e7f70e7 COLONY: drag and drop elements
e16e05ac1b COLONY: add robot eye and pupil 3D rendering
74cc722e64 COLONY: avoid enemies to multiply out of control
b9e130dd2f COLONY: drawMacTextPopup
b1d4c6bca7 COLONY: restart game popup fixes
b1a8eb66ea GRAPHICS: MACGUI: allow to query cursor data
0053cc5d0f COLONY: mouse pointer fixes
5eef2e7764 COLONY: implement iris wipe screen transition
94b0fad16b COLONY: fix PC speaker divide-by-zero and animation operator precedence
112332e117 COLONY: malloc -> new / free -> delete
c4741f9a60 COLONY: removed lambdas
6712d35cef COLONY: improved DOS UI
02c393e3ef COLONY: corepower fixes
85a8dab31a COLONY: mac palette inverted in animations
76af8ad65c COLONY: add periodic power drain timer and minimap bounds guards
a510a6d5c2 COLONY: make sure some effects do not run too fast
6ffe27e77d COLONY: powersuit ui in mac b&w
c8739fe266 COLONY: shooting effects
5d868059ea COLONY: improve crosshair aiming precision
449ee08621 COLONY: add missing in-game text strings
05c18cace2 COLONY: text rendering
3db09eb0b2 COLONY: intro fixes
7f37140dad COLONY: add missing sound effect triggers
25e0bde88c COLONY: fixed some missing triangles
8febde6075 COLONY: corner walls fix
0041e8e57e COLONY: better collision with corner walls
abf4977f6b COLONY: forklift fixes
451888ee5e COLONY: more forklift fixes
85b854024e COLONY: projector fixes
9dee3813e1 COLONY: pyramid/eyeball fixes
237fda5338 COLONY: slower energy drain
72e9611186 COLONY: add robot eye/pupil rendering and fix savegame field
8512bfd3fc COLONY: movement fixes
18fc8d827c COLONY: button fixes
25850145ac COLONY: battle fixes
cc66da725b COLONY: add debugger "spawn" command for placing robots
4378aa1a38 COLONY: fix regression in animation state initialization
ab92b64bc6 COLONY: queen eyes
2dd5ba5709 COLONY: automap
5fb8d0315a COLONY: disable long DOS intro sound
a334827550 COLONY: initialize power suit animation switches from current armor state
c2337c5ff1 COLONY: fixed setupMacPattern warning using inline
377c72dfd4 COLONY: added extended copyright years/author to all the files
d015904e93 COLONY: use SeekableReadStreamEndian instead of read wrappers
94119aee30 COLONY: avoid mixing spaces and tabs
2f391fa69b COLONY: fix include order in all files, common/ first, then engine main, then engine-specific
caa05c95d6 COLONY: noted anonymous namespace in savegame.cpp
8f8436672e COLONY: noted anonymous namespace in colony.cpp
c14e2016fa COLONY: ifndef COLONY_H
7364024ba0 COLONY: initialize missing class variables in colony
6e65a1e6e5 COLONY: initialize missing class variables in secondary classes
31b6fc2efd COLONY: removed most of the static keyword usage
372b3ddbc0 COLONY: reduced static keyword usage
67678bd574 COLONY: replace long/long long by portable types
3cb777b863 COLONY: use << for powers of two constants
5bfe039bca COLONY: fix or document redundant cases in switches before a default
e6d163a6b8 COLONY: use int64 for some variables that can be large when calculating projects
649e76722c COLONY: added missing cases in some event loops
6a038d52ea COLONY: allow to access main menu during animation loop
1bb20696ca COLONY: renamed constants from mc_camerlCase to kMcCamelCase
5c178c4f6c COLONY: renamed crypt -> cryptArray to avoid collisions
f63a44b0b6 COLONY: use debugChannelSet instead of gDebugLevel
f3f66ac3ea COLONY: use tag2str instead of typeNames
ef2f3a5ddb COLONY: use kDesk enum instead of magic constants
9d606a1a50 COLONY: converted comments to C++ style
c25219ba4d COLONY: do not build this engine by default
32fa446da7 COLONY: added CData into the search data and improved game detection
995e9da259 COLONY: Renamed debugger.cpp/h -> console.cpp/h
dfdf4b3a74 COLONY: added hasFeature with extended sabes and loading during startup
e726faa2cb COLONY: added POTFILES
6f52d307a2 COLONY: simplified loadMap loops conditions
2ea2ed1f91 COLONY: removed CData code from path loading
287cb7008b COLONY: minimize header deps in colony.h using forward declarations
742eb9f04d COLONY: fixed CI issues: struct/class mismatch and double-to-float truncation
0d4beef4a6 COLONY: added comment regarding framebuffer
ce6162b440 AUDIO: fix PC speaker divide-by-zero in frequency calculation


Commit: 5d1b2fea92cdc3042716c9e32ff7d1b01ccad7e5
    https://github.com/scummvm/scummvm/commit/5d1b2fea92cdc3042716c9e32ff7d1b01ccad7e5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:00+02:00

Commit Message:
COLONY: basic loading of maps and rendering

Changed paths:
  A engines/colony/colony.cpp
  A engines/colony/colony.h
  A engines/colony/detection.cpp
  A engines/colony/detection.h
  A engines/colony/gfx.cpp
  A engines/colony/gfx.h
  A engines/colony/metaengine.cpp
  A engines/colony/module.mk


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
new file mode 100644
index 00000000000..63709f9ea86
--- /dev/null
+++ b/engines/colony/colony.cpp
@@ -0,0 +1,472 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "colony/gfx.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/util.h"
+#include "common/debug.h"
+#include "common/events.h"
+#include "common/keyboard.h"
+#include "engines/util.h"
+#include "graphics/palette.h"
+#include "graphics/paletteman.h"
+
+namespace Colony {
+
+static const int16 g_sintTable[256] = {
+	90,  93,  95,  97,  99, 101, 103, 105,
+	106, 108, 110, 111, 113, 114, 116, 117,
+	118, 119, 121, 122, 122, 123, 124, 125,
+	126, 126, 127, 127, 127, 128, 128, 128,
+	128, 128, 128, 128, 127, 127, 127, 126,
+	126, 125, 124, 123, 122, 122, 121, 119,
+	118, 117, 116, 114, 113, 111, 110, 108,
+	106, 105, 103, 101,  99,  97,  95,  93,
+	90,  88,  86,  84,  81,  79,  76,  74,
+	71,  68,  66,  63,  60,  56,  54,  52,
+	49,  46,  43,  40,  37,  34,  31,  28,
+	25,  23,  19,  16,  13,   9,   6,   3,
+	0,  -3,  -6,  -9, -13, -16, -19, -23,
+	-25, -28, -31, -34, -37, -40, -43, -46,
+	-49, -52, -54, -56, -60, -63, -66, -68,
+	-71, -74, -76, -79, -81, -84, -86, -88,
+	-88, -90, -93, -95, -97, -99,-101,-103,
+	-105,-106,-108,-110,-111,-113,-114,-116,
+	-117,-118,-119,-121,-122,-122,-123,-124,
+	-125,-126,-126,-127,-127,-127,-128,-128,
+	-128,-128,-128,-128,-127,-127,-127,-126,
+	-126,-125,-124,-123,-122,-122,-121,-119,
+	-118,-117,-116,-114,-113,-111,-110,-108,
+	-106,-105,-103,-101, -99, -97, -95, -93,
+	-90, -88, -86, -84, -81, -79, -76, -74,
+	-71, -68, -66, -63, -60, -56, -54, -52,
+	-49, -46, -43, -40, -37, -34, -31, -28,
+	-25, -23, -19, -16, -13,  -9,  -6,  -3,
+	0,   3,   6,   9,  13,  16,  19,  23,
+	25,  28,  31,  34,  37,  40,  43,  46,
+	49,  52,  54,  56,  60,  63,  66,  68,
+	71,  74,  76,  79,  81,  84,  86,  88
+};
+
+static const int g_indexTable[4][10] = {
+	{0, 0,  0, 0,  0,  1,  1,  0,  1, 2},
+	{1, 0,  0, 0, -1,  0,  0,  1,  2, 1},
+	{0, 1,  1, 0,  0, -1, -1,  0,  1, 2},
+	{0, 0,  0, 1,  1,  0,  0, -1,  2, 1}
+};
+
+ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd) {
+	_level = 0;
+	_robotNum = 0;
+	_gfx = nullptr;
+	_width = 640;
+	_height = 480;
+	_centerX = _width / 2;
+	_centerY = _height / 2;
+	_flip = false;
+	_mouseSensitivity = 1;
+	_change = true;
+	
+	memset(_wall, 0, sizeof(_wall));
+	memset(_mapData, 0, sizeof(_mapData));
+	memset(_robotArray, 0, sizeof(_robotArray));
+	memset(_foodArray, 0, sizeof(_foodArray));
+	
+	_me.xindex = 10;
+	_me.yindex = 10;
+	_me.xloc = 10 * 256 + 128;
+	_me.yloc = 10 * 256 + 128;
+	_me.look = 0;
+	_me.ang = 0;
+
+	initTrig();
+}
+
+ColonyEngine::~ColonyEngine() {
+}
+
+void ColonyEngine::loadMap(int mnum) {
+	Common::String mapName = Common::String::format("MAP.%d", mnum);
+	Common::File file;
+	if (!file.open(Common::Path(mapName))) {
+		warning("Could not open map file %s", mapName.c_str());
+		return;
+	}
+
+	file.readUint32BE(); // "DAVE" header
+	int16 mapDefs[10];
+	for (int i = 0; i < 10; i++) {
+		mapDefs[i] = file.readSint16BE(); // Swapped in original code
+	}
+
+	uint16 bLength = file.readUint16BE(); // Swapped in original code
+	uint8 *buffer = (uint8 *)malloc(bLength);
+	if (!buffer) {
+		error("Out of memory loading map");
+	}
+	file.read(buffer, bLength);
+	file.close();
+
+	// expand logic
+	int c = 0;
+	_robotNum = MENUM + 1;
+	for (int i = 0; i < 32; i++) {
+		for (int j = 0; j < 32; j++) {
+			_wall[i][j] = buffer[c++];
+			if (i < 31 && j < 31) {
+				for (int k = 0; k < 5; k++) {
+					if (_wall[i][j] & (1 << (k + 2))) {
+						for (int l = 0; l < 5; l++) {
+							_mapData[i][j][k][l] = buffer[c++];
+						}
+						// Robot creation logic will be added here
+					} else {
+						_mapData[i][j][k][0] = 0;
+					}
+				}
+			}
+		}
+	}
+	free(buffer);
+	_level = mnum;
+	debug("Successfully loaded map %d", mnum);
+}
+
+void ColonyEngine::initTrig() {
+	for (int i = 0; i < 256; i++) {
+		_sint[i] = g_sintTable[i];
+		_cost[i] = g_sintTable[(i + 64) & 0xFF];
+	}
+
+	_rtable[0] = 32000;
+	for (int i = 1; i < 11585; i++) {
+		_rtable[i] = (160 * 128) / i;
+	}
+}
+
+void ColonyEngine::rot_init(int x, int y) {
+	_rox = ((long)x * _tsin - (long)y * _tcos) >> 8;
+	_roy = ((long)y * _tsin + (long)x * _tcos) >> 8;
+}
+
+void ColonyEngine::perspective(int pnt[2], int rox, int roy) {
+	long p;
+
+	if (roy <= 0)
+		roy = 1;
+	p = _centerX + ((long)rox * 256) / roy;
+
+	if (p < -32000)
+		p = -32000;
+	else if (p > 32000)
+		p = 32000;
+	pnt[0] = (int)p;
+
+	if (roy >= 11585)
+		roy = 11584;
+
+	if (_flip)
+		pnt[1] = _centerY + _rtable[roy];
+	else
+		pnt[1] = _centerY - _rtable[roy];
+}
+
+void ColonyEngine::quadrant() {
+	int remain;
+	int quad;
+
+	quad = _me.look >> 6;				/*divide by 64		*/
+	remain = _me.look - (quad << 6);			/*multiply by 64	*/
+	_tsin = _sint[remain];
+	_tcos = _cost[remain];
+
+	switch (quad) {
+	case 0:
+		rot_init((_me.xindex << 8) - _me.xloc, (_me.yindex << 8) - _me.yloc);
+		_direction = 0; // NORTH
+		break;
+	case 1:
+		rot_init((_me.yindex << 8) - _me.yloc, _me.xloc - ((_me.xindex + 1) << 8));
+		_direction = 2; // WEST
+		break;
+	case 2:
+		rot_init(_me.xloc - ((_me.xindex + 1) << 8), _me.yloc - ((_me.yindex + 1) << 8));
+		_direction = 3; // SOUTH
+		break;
+	case 3:
+		rot_init(_me.yloc - ((_me.yindex + 1) << 8), (_me.xindex << 8) - _me.xloc);
+		_direction = 1; // EAST
+		break;
+	}
+
+	_frntxWall = g_indexTable[quad][0];
+	_frntyWall = g_indexTable[quad][1];
+	_sidexWall = g_indexTable[quad][2];
+	_sideyWall = g_indexTable[quad][3];
+	_frntx = g_indexTable[quad][4];
+	_frnty = g_indexTable[quad][5];
+	_sidex = g_indexTable[quad][6];
+	_sidey = g_indexTable[quad][7];
+	_front = g_indexTable[quad][8];
+	_side = g_indexTable[quad][9];
+}
+
+void ColonyEngine::corridor() {
+	int length = 1;
+	int xFrontLeft, yFrontLeft;
+	int xFrontRight, yFrontRight;
+	int xfbehind, yfbehind;
+	int dr[2];
+	int cellx, celly;
+
+	quadrant();
+
+	xfbehind = _me.xindex + _frntxWall;
+	yfbehind = _me.yindex + _frntyWall;
+	xFrontLeft = xfbehind + _frntx;
+	yFrontLeft = yfbehind + _frnty;
+	xFrontRight = xFrontLeft + _sidex;
+	yFrontRight = yFrontLeft + _sidey;
+	// cellx = _me.xindex; // Removed unused
+	// celly = _me.yindex; // Removed unused
+
+	if (_change) {
+		perspective(dr, _rox, _roy);
+		if (xfbehind >= 0 && xfbehind < 34 && yfbehind >= 0 && yfbehind < 34) {
+			_drY[xfbehind][yfbehind] = dr[1];
+			_drX[xfbehind][yfbehind] = dr[0];
+		}
+
+		perspective(dr, _rox + _tsin, _roy + _tcos);
+		if (xfbehind + _sidex >= 0 && xfbehind + _sidex < 34 && yfbehind + _sidey >= 0 && yfbehind + _sidey < 34) {
+			_drY[xfbehind + _sidex][yfbehind + _sidey] = dr[1];
+			_drX[xfbehind + _sidex][yfbehind + _sidey] = dr[0];
+		}
+	}
+
+	_rox -= _tcos;
+	_roy += _tsin;
+
+	if (_change) {
+		perspective(dr, _rox, _roy);
+		if (xFrontLeft >= 0 && xFrontLeft < 34 && yFrontLeft >= 0 && yFrontLeft < 34) {
+			_drX[xFrontLeft][yFrontLeft] = dr[0];
+			_drY[xFrontLeft][yFrontLeft] = dr[1];
+		}
+		perspective(dr, _rox + _tsin, _roy + _tcos);
+		if (xFrontRight >= 0 && xFrontRight < 34 && yFrontRight >= 0 && yFrontRight < 34) {
+			_drX[xFrontRight][yFrontRight] = dr[0];
+			_drY[xFrontRight][yFrontRight] = dr[1];
+		}
+	}
+
+	uint32 white = _gfx->white();
+	// Draw starting lines (behind us)
+	_gfx->drawLine(_drX[xfbehind][yfbehind], _drY[xfbehind][yfbehind],
+	               _drX[xfbehind + _sidex][yfbehind + _sidey], _drY[xfbehind + _sidex][yfbehind + _sidey], white);
+	_gfx->drawLine(_drX[xfbehind][yfbehind], _height - _drY[xfbehind][yfbehind],
+	               _drX[xfbehind + _sidex][yfbehind + _sidey], _height - _drY[xfbehind + _sidex][yfbehind + _sidey], white);
+	_gfx->drawLine(_drX[xfbehind][yfbehind], _drY[xfbehind][yfbehind],
+	               _drX[xfbehind][yfbehind], _height - _drY[xfbehind][yfbehind], white);
+	_gfx->drawLine(_drX[xfbehind + _sidex][yfbehind + _sidey], _drY[xfbehind + _sidex][yfbehind + _sidey],
+	               _drX[xfbehind + _sidex][yfbehind + _sidey], _height - _drY[xfbehind + _sidex][yfbehind + _sidey], white);
+
+	int xprevL = xfbehind;
+	int yprevL = yfbehind;
+	int xprevR = xfbehind + _sidex;
+	int yprevR = yfbehind + _sidey;
+
+	while (!(_wall[xFrontLeft][yFrontLeft] & _front)) {
+		_rox -= _tcos;
+		_roy += _tsin;
+		xFrontLeft += _frntx;
+		yFrontLeft += _frnty;
+		xFrontRight += _frntx;
+		yFrontRight += _frnty;
+		if (_change) {
+			perspective(dr, _rox, _roy);
+			if (xFrontLeft >= 0 && xFrontLeft < 34 && yFrontLeft >= 0 && yFrontLeft < 34) {
+				_drX[xFrontLeft][yFrontLeft] = dr[0];
+				_drY[xFrontLeft][yFrontLeft] = dr[1];
+			}
+			perspective(dr, _rox + _tsin, _roy + _tcos);
+			if (xFrontRight >= 0 && xFrontRight < 34 && yFrontRight >= 0 && yFrontRight < 34) {
+				_drX[xFrontRight][yFrontRight] = dr[0];
+				_drY[xFrontRight][yFrontRight] = dr[1];
+			}
+		}
+
+		// Horizontal transversal lines
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
+
+		// Longitudinal lines (floor and ceiling edges)
+		_gfx->drawLine(_drX[xprevL][yprevL], _drY[xprevL][yprevL],
+		               _drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft], white);
+		_gfx->drawLine(_drX[xprevR][yprevR], _drY[xprevR][yprevR],
+		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+		_gfx->drawLine(_drX[xprevL][yprevL], _height - _drY[xprevL][yprevL],
+		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
+		_gfx->drawLine(_drX[xprevR][yprevR], _height - _drY[xprevR][yprevR],
+		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
+
+		// Vertical corner lines
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
+		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight],
+		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
+
+		xprevL = xFrontLeft;
+		yprevL = yFrontLeft;
+		xprevR = xFrontRight;
+		yprevR = yFrontRight;
+
+		length++;
+		if (length > 30)
+			break; // Safety break
+	}
+	drawend(xfbehind, yfbehind, xFrontLeft, yFrontLeft);
+	// _change = false; // Keep true for now to debug turning
+}
+
+void ColonyEngine::drawend(int xstart, int ystart, int xFrontLeft, int yFrontLeft) {
+	int xFrontRight, yFrontRight;
+
+	xFrontRight = xFrontLeft + _sidex;
+	yFrontRight = yFrontLeft + _sidey;
+
+	uint32 white = _gfx->white();
+
+	if ((xstart != xFrontLeft) || (ystart != yFrontLeft)) {
+		if (_drY[xstart + _frntx][ystart + _frnty] > 0) {
+			_gfx->drawLine(_drX[xstart][ystart], _drY[xstart][ystart],
+			               _drX[xstart + _frntx][ystart + _frnty], _drY[xstart + _frntx][ystart + _frnty], white);
+		}
+		_gfx->drawLine(_drX[xstart + _frntx][ystart + _frnty], _drY[xstart + _frntx][ystart + _frnty],
+		               _drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft], white);
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
+		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight],
+		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight],
+		               _drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey], white);
+		if (_drY[xstart + _sidex][ystart + _sidey] > 0) {
+			_gfx->drawLine(_drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey],
+			               _drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey], white); // This one is still weird in original?
+		}
+	} else {
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
+		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight],
+		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+	}
+}
+
+Common::Error ColonyEngine::run() {
+	Graphics::PixelFormat format8bpp = Graphics::PixelFormat::createFormatCLUT8();
+	initGraphics(_width, _height, &format8bpp);
+
+	_width = _system->getWidth();
+	_height = _system->getHeight();
+	const Graphics::PixelFormat format = _system->getScreenFormat();
+	debug("Screen format: %d bytesPerPixel. Actual size: %dx%d", format.bytesPerPixel, _width, _height);
+
+	// Setup a grayscale palette
+	byte pal[256 * 3];
+	for (int i = 0; i < 256; i++) {
+		pal[i * 3 + 0] = i;
+		pal[i * 3 + 1] = i;
+		pal[i * 3 + 2] = i;
+	}
+	_system->getPaletteManager()->setPalette(pal, 0, 256);
+
+	_gfx = new Gfx(_system, _width, _height);
+	
+	loadMap(1); // Try to load the first map
+	
+	_system->lockMouse(true);
+	_system->warpMouse(_width / 2, _height / 2);
+
+	// Temporary infinite loop to prevent ScummVM from closing immediately
+	while (!shouldQuit()) {
+		Common::Event event;
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYDOWN) {
+				debug("Key down: %d", event.kbd.keycode);
+				switch (event.kbd.keycode) {
+				case Common::KEYCODE_UP:
+					_me.xloc += _cost[_me.look] >> 2;
+					_me.yloc += _sint[_me.look] >> 2;
+					_me.xindex = _me.xloc >> 8;
+					_me.yindex = _me.yloc >> 8;
+					_change = true;
+					break;
+				case Common::KEYCODE_DOWN:
+					_me.xloc -= _cost[_me.look] >> 2;
+					_me.yloc -= _sint[_me.look] >> 2;
+					_me.xindex = _me.xloc >> 8;
+					_me.yindex = _me.yloc >> 8;
+					_change = true;
+					break;
+				case Common::KEYCODE_LEFT:
+					_me.look = (uint8)((int)_me.look + 8);
+					_change = true;
+					break;
+				case Common::KEYCODE_RIGHT:
+					_me.look = (uint8)((int)_me.look - 8);
+					_change = true;
+					break;
+				default:
+					break;
+				}
+				debug("Me: x=%d y=%d look=%d", _me.xloc, _me.yloc, _me.look);
+			} else if (event.type == Common::EVENT_MOUSEMOVE) {
+				if (event.relMouse.x != 0) {
+					// Subtract because increasing look turns left, but mouse right is positive rel X
+					// Reduced sensitivity by half
+					_me.look = (uint8)((int)_me.look - (event.relMouse.x / 2));
+					_change = true;
+				}
+			}
+		}
+		_system->warpMouse(_width / 2, _height / 2);
+
+		_gfx->clear(_gfx->black());
+		
+		corridor();
+		
+		_gfx->copyToScreen();
+		_system->delayMillis(10);
+	}
+
+	delete _gfx;
+	return Common::kNoError;
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
new file mode 100644
index 00000000000..e12d8a299dc
--- /dev/null
+++ b/engines/colony/colony.h
@@ -0,0 +1,126 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef COLONY_COLONY_H
+#define COLONY_COLONY_H
+
+#include "engines/engine.h"
+#include "engines/advancedDetector.h"
+#include "common/array.h"
+#include "common/rect.h"
+#include "colony/gfx.h"
+
+namespace Colony {
+
+#define BASEOBJECT 20
+#define MENUM 101
+
+struct Locate {
+	uint8 ang;
+	uint8 look;
+	int lookx;
+	int delta;
+	int xloc;
+	int yloc;
+	int xindex;
+	int yindex;
+	int xmx, xmn;
+	int zmx, zmn;
+	int32 power[3];
+	int type;
+	int dx, dy;
+	int dist;
+};
+
+struct Thing {
+	int type;
+	int visible;
+	int alive;
+	Common::Rect clip;
+	int count;
+	Locate where;
+	int opcode;
+	int counter;
+	int time;
+	int grow;
+	// void (*make)(); // To be implemented as virtual functions or member function pointers
+	// void (*think)();
+};
+
+class ColonyEngine : public Engine {
+public:
+	ColonyEngine(OSystem *syst, const ADGameDescription *gd);
+	virtual ~ColonyEngine();
+
+	Common::Error run() override;
+
+	void initTrig();
+	void loadMap(int mnum);
+	void corridor();
+	void quadrant();
+	void perspective(int pnt[2], int rox, int roy);
+	void rot_init(int x, int y);
+
+private:
+	const ADGameDescription *_gameDescription;
+
+	uint8 _wall[32][32];
+	uint8 _mapData[31][31][5][5];
+	uint8 _robotArray[32][32];
+	uint8 _foodArray[32][32];
+	
+	Locate _me;
+	Common::Array<Thing> _objects;
+	int _level;
+	int _robotNum;
+
+	Gfx *_gfx;
+	
+	int _rox, _roy;
+	int _tsin, _tcos;
+	int _sint[256];
+	int _cost[256];
+	int _rtable[11585];
+	int _centerX, _centerY;
+	int _width, _height;
+	bool _flip;
+	int _mouseSensitivity;
+	bool _change;
+
+	int _frntxWall, _frntyWall;
+	int _sidexWall, _sideyWall;
+	int _frntx, _frnty;
+	int _sidex, _sidey;
+	int _front, _side;
+	int _direction;
+
+	int _drX[34][34];
+	int _drY[34][34];
+
+	Common::Rect _clip;
+	Common::Rect _screenR;
+
+	void drawend(int xstart, int ystart, int xFrontLeft, int yFrontLeft);
+};
+
+} // End of namespace Colony
+
+#endif // COLONY_COLONY_H
diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
new file mode 100644
index 00000000000..90df5bd0a64
--- /dev/null
+++ b/engines/colony/detection.cpp
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/detection.h"
+
+namespace Colony {
+
+static const PlainGameDescriptor colonyGames[] = {
+	{"colony", "The Colony"},
+	{0, 0}
+};
+
+const ADGameDescription gameDescriptions[] = {
+	{
+		"colony",
+		"",
+		AD_ENTRY2s("logo1.pic", "70d44e40ac19ea0413f1253b781399de", 6689,
+		           "MAP.1", "ab40dc3d9658e8cdc0bee63c2ca9c79b", 3350),
+		Common::EN_ANY,
+		Common::kPlatformDOS,
+		ADGF_NO_FLAGS,
+		GUIO0()
+	},
+	AD_TABLE_END_MARKER
+};
+
+} // End of namespace Colony
+
+class ColonyMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
+public:
+	ColonyMetaEngineDetection() : AdvancedMetaEngineDetection(Colony::gameDescriptions, Colony::colonyGames) {
+	}
+
+	const char *getName() const override {
+		return "colony";
+	}
+
+	const char *getEngineName() const override {
+		return "The Colony";
+	}
+
+	const char *getOriginalCopyright() const override {
+		return "Copyright (C) 1988 David A. Smith";
+	}
+};
+
+REGISTER_PLUGIN_STATIC(COLONY_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, ColonyMetaEngineDetection);
diff --git a/engines/colony/detection.h b/engines/colony/detection.h
new file mode 100644
index 00000000000..e8c913ab202
--- /dev/null
+++ b/engines/colony/detection.h
@@ -0,0 +1,37 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef COLONY_DETECTION_H
+#define COLONY_DETECTION_H
+
+#include "engines/advancedDetector.h"
+
+namespace Colony {
+
+enum ColonyDebugChannels {
+	kColonyDebugGeneral = 1 << 0
+};
+
+extern const ADGameDescription gameDescriptions[];
+
+} // End of namespace Colony
+
+#endif // COLONY_DETECTION_H
diff --git a/engines/colony/gfx.cpp b/engines/colony/gfx.cpp
new file mode 100644
index 00000000000..b4cc9a29b2b
--- /dev/null
+++ b/engines/colony/gfx.cpp
@@ -0,0 +1,56 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/system.h"
+#include "colony/gfx.h"
+
+namespace Colony {
+
+Gfx::Gfx(OSystem *system, int width, int height) : _system(system), _width(width), _height(height) {
+	_surface.create(width, height, _system->getScreenFormat());
+}
+
+Gfx::~Gfx() {
+	_surface.free();
+}
+
+void Gfx::clear(uint32 color) {
+	_surface.clear(color);
+}
+
+void Gfx::drawLine(int x1, int y1, int x2, int y2, uint32 color) {
+	_surface.drawLine(x1, y1, x2, y2, color);
+}
+
+void Gfx::drawRect(const Common::Rect &rect, uint32 color) {
+	_surface.frameRect(rect, color);
+}
+
+void Gfx::fillRect(const Common::Rect &rect, uint32 color) {
+	_surface.fillRect(rect, color);
+}
+
+void Gfx::copyToScreen() {
+	_system->copyRectToScreen(_surface.getPixels(), _surface.pitch, 0, 0, _surface.w, _surface.h);
+	_system->updateScreen();
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/gfx.h b/engines/colony/gfx.h
new file mode 100644
index 00000000000..f7bfa3e66d3
--- /dev/null
+++ b/engines/colony/gfx.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef COLONY_GFX_H
+#define COLONY_GFX_H
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+#include "graphics/managed_surface.h"
+
+namespace Colony {
+
+class Gfx {
+public:
+	Gfx(OSystem *system, int width, int height);
+	~Gfx();
+
+	void clear(uint32 color);
+	void drawLine(int x1, int y1, int x2, int y2, uint32 color);
+	void drawRect(const Common::Rect &rect, uint32 color);
+	void fillRect(const Common::Rect &rect, uint32 color);
+	
+	void copyToScreen();
+
+	uint32 white() { return 255; }
+	uint32 black() { return 0; }
+
+private:
+	OSystem *_system;
+	Graphics::ManagedSurface _surface;
+	int _width;
+	int _height;
+};
+
+} // End of namespace Colony
+
+#endif
diff --git a/engines/colony/metaengine.cpp b/engines/colony/metaengine.cpp
new file mode 100644
index 00000000000..64849fc5445
--- /dev/null
+++ b/engines/colony/metaengine.cpp
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "colony/detection.h"
+#include "common/system.h"
+
+namespace Colony {
+
+class ColonyMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
+public:
+	const char *getName() const override {
+		return "colony";
+	}
+
+	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const override {
+		*engine = new ColonyEngine(syst, gd);
+		return Common::kNoError;
+	}
+};
+
+} // End of namespace Colony
+
+#if PLUGIN_ENABLED_DYNAMIC(COLONY)
+REGISTER_PLUGIN_DYNAMIC(COLONY, PLUGIN_TYPE_ENGINE, Colony::ColonyMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(COLONY, PLUGIN_TYPE_ENGINE, Colony::ColonyMetaEngine);
+#endif
diff --git a/engines/colony/module.mk b/engines/colony/module.mk
new file mode 100644
index 00000000000..94f5330614a
--- /dev/null
+++ b/engines/colony/module.mk
@@ -0,0 +1,20 @@
+MODULE := engines/colony
+
+MODULE_OBJS := \
+	colony.o \
+	gfx.o \
+	metaengine.o
+
+MODULE_DIRS += \
+	engines/colony
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_COLONY), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
+
+# Detection objects
+DETECT_OBJS += $(MODULE)/detection.o


Commit: 9164033a04811066a36616b84ecbb44346638248
    https://github.com/scummvm/scummvm/commit/9164033a04811066a36616b84ecbb44346638248
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:00+02:00

Commit Message:
COLONY: basic collision detection

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 63709f9ea86..e3d59027088 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -191,6 +191,114 @@ void ColonyEngine::perspective(int pnt[2], int rox, int roy) {
 		pnt[1] = _centerY - _rtable[roy];
 }
 
+int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
+	int xind2, yind2;
+	xind2 = xnew >> 8;
+	yind2 = ynew >> 8;
+	_change = true;
+
+	if (xind2 == pobject->xindex) {
+		if (yind2 == pobject->yindex) {
+			pobject->dx = xnew - pobject->xloc;
+			pobject->dy = ynew - pobject->yloc;
+			pobject->xloc = xnew;
+			pobject->yloc = ynew;
+			return 0;
+		} else {
+			if (yind2 > pobject->yindex) {
+				if (!(_wall[pobject->xindex][yind2] & 1)) {
+					pobject->yindex = yind2;
+					pobject->xindex = xind2;
+					pobject->dx = xnew - pobject->xloc;
+					pobject->dy = ynew - pobject->yloc;
+					pobject->xloc = xnew;
+					pobject->yloc = ynew;
+					return 0;
+				} else {
+					debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
+					return -1;
+				}
+			} else {
+				if (!(_wall[pobject->xindex][pobject->yindex] & 1)) {
+					pobject->yindex = yind2;
+					pobject->xindex = xind2;
+					pobject->dx = xnew - pobject->xloc;
+					pobject->dy = ynew - pobject->yloc;
+					pobject->xloc = xnew;
+					pobject->yloc = ynew;
+					return 0;
+				} else {
+					debug("Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
+					return -1;
+				}
+			}
+		}
+	} else if (yind2 == pobject->yindex) {
+		if (xind2 > pobject->xindex) {
+			if (!(_wall[xind2][pobject->yindex] & 2)) {
+				pobject->yindex = yind2;
+				pobject->xindex = xind2;
+				pobject->dx = xnew - pobject->xloc;
+				pobject->dy = ynew - pobject->yloc;
+				pobject->xloc = xnew;
+				pobject->yloc = ynew;
+				return 0;
+			} else {
+				debug("Collision East at x=%d y=%d", xind2, pobject->yindex);
+				return -1;
+			}
+		} else {
+			if (!(_wall[pobject->xindex][pobject->yindex] & 2)) {
+				pobject->yindex = yind2;
+				pobject->xindex = xind2;
+				pobject->dx = xnew - pobject->xloc;
+				pobject->dy = ynew - pobject->yloc;
+				pobject->xloc = xnew;
+				pobject->yloc = ynew;
+				return 0;
+			} else {
+				debug("Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
+				return -1;
+			}
+		}
+	} else {
+		// Diagonal
+		if (xind2 > pobject->xindex) {
+			if (yind2 > pobject->yindex) {
+				if ((_wall[pobject->xindex][yind2] & 1) || (_wall[xind2][pobject->yindex] & 2) || (_wall[xind2][yind2] & 3)) {
+					debug("Collision Diagonal SE");
+					return -1;
+				}
+			} else {
+				if ((_wall[pobject->xindex][pobject->yindex] & 1) || (_wall[xind2][yind2] & 2) || (_wall[xind2][pobject->yindex] & 3)) {
+					debug("Collision Diagonal NE");
+					return -1;
+				}
+			}
+		} else {
+			if (yind2 > pobject->yindex) {
+				if ((_wall[xind2][yind2] & 1) || (_wall[pobject->xindex][pobject->yindex] & 2) || (_wall[pobject->xindex][yind2] & 3)) {
+					debug("Collision Diagonal SW");
+					return -1;
+				}
+			} else {
+				if ((_wall[xind2][pobject->yindex] & 1) || (_wall[pobject->xindex][yind2] & 2) || (_wall[pobject->xindex][pobject->yindex] & 3)) {
+					debug("Collision Diagonal NW");
+					return -1;
+				}
+			}
+		}
+		pobject->yindex = yind2;
+		pobject->xindex = xind2;
+		pobject->dx = xnew - pobject->xloc;
+		pobject->dy = ynew - pobject->yloc;
+		pobject->xloc = xnew;
+		pobject->yloc = ynew;
+		return 0;
+	}
+	return -1;
+}
+
 void ColonyEngine::quadrant() {
 	int remain;
 	int quad;
@@ -247,33 +355,34 @@ void ColonyEngine::corridor() {
 	yFrontLeft = yfbehind + _frnty;
 	xFrontRight = xFrontLeft + _sidex;
 	yFrontRight = yFrontLeft + _sidey;
-	// cellx = _me.xindex; // Removed unused
-	// celly = _me.yindex; // Removed unused
+
+	int rox = _rox;
+	int roy = _roy;
 
 	if (_change) {
-		perspective(dr, _rox, _roy);
+		perspective(dr, rox, roy);
 		if (xfbehind >= 0 && xfbehind < 34 && yfbehind >= 0 && yfbehind < 34) {
-			_drY[xfbehind][yfbehind] = dr[1];
 			_drX[xfbehind][yfbehind] = dr[0];
+			_drY[xfbehind][yfbehind] = dr[1];
 		}
 
-		perspective(dr, _rox + _tsin, _roy + _tcos);
+		perspective(dr, rox + _tsin, roy + _tcos);
 		if (xfbehind + _sidex >= 0 && xfbehind + _sidex < 34 && yfbehind + _sidey >= 0 && yfbehind + _sidey < 34) {
-			_drY[xfbehind + _sidex][yfbehind + _sidey] = dr[1];
 			_drX[xfbehind + _sidex][yfbehind + _sidey] = dr[0];
+			_drY[xfbehind + _sidex][yfbehind + _sidey] = dr[1];
 		}
 	}
 
-	_rox -= _tcos;
-	_roy += _tsin;
+	rox -= _tcos;
+	roy += _tsin;
 
 	if (_change) {
-		perspective(dr, _rox, _roy);
+		perspective(dr, rox, roy);
 		if (xFrontLeft >= 0 && xFrontLeft < 34 && yFrontLeft >= 0 && yFrontLeft < 34) {
 			_drX[xFrontLeft][yFrontLeft] = dr[0];
 			_drY[xFrontLeft][yFrontLeft] = dr[1];
 		}
-		perspective(dr, _rox + _tsin, _roy + _tcos);
+		perspective(dr, rox + _tsin, roy + _tcos);
 		if (xFrontRight >= 0 && xFrontRight < 34 && yFrontRight >= 0 && yFrontRight < 34) {
 			_drX[xFrontRight][yFrontRight] = dr[0];
 			_drY[xFrontRight][yFrontRight] = dr[1];
@@ -291,25 +400,25 @@ void ColonyEngine::corridor() {
 	_gfx->drawLine(_drX[xfbehind + _sidex][yfbehind + _sidey], _drY[xfbehind + _sidex][yfbehind + _sidey],
 	               _drX[xfbehind + _sidex][yfbehind + _sidey], _height - _drY[xfbehind + _sidex][yfbehind + _sidey], white);
 
-	int xprevL = xfbehind;
-	int yprevL = yfbehind;
-	int xprevR = xfbehind + _sidex;
-	int yprevR = yfbehind + _sidey;
+	int xprevL = xFrontLeft;
+	int yprevL = yFrontLeft;
+	int xprevR = xFrontRight;
+	int yprevR = yFrontRight;
 
 	while (!(_wall[xFrontLeft][yFrontLeft] & _front)) {
-		_rox -= _tcos;
-		_roy += _tsin;
+		rox -= _tcos;
+		roy += _tsin;
 		xFrontLeft += _frntx;
 		yFrontLeft += _frnty;
 		xFrontRight += _frntx;
 		yFrontRight += _frnty;
 		if (_change) {
-			perspective(dr, _rox, _roy);
+			perspective(dr, rox, roy);
 			if (xFrontLeft >= 0 && xFrontLeft < 34 && yFrontLeft >= 0 && yFrontLeft < 34) {
 				_drX[xFrontLeft][yFrontLeft] = dr[0];
 				_drY[xFrontLeft][yFrontLeft] = dr[1];
 			}
-			perspective(dr, _rox + _tsin, _roy + _tcos);
+			perspective(dr, rox + _tsin, roy + _tcos);
 			if (xFrontRight >= 0 && xFrontRight < 34 && yFrontRight >= 0 && yFrontRight < 34) {
 				_drX[xFrontRight][yFrontRight] = dr[0];
 				_drY[xFrontRight][yFrontRight] = dr[1];
@@ -421,19 +530,19 @@ Common::Error ColonyEngine::run() {
 				debug("Key down: %d", event.kbd.keycode);
 				switch (event.kbd.keycode) {
 				case Common::KEYCODE_UP:
-					_me.xloc += _cost[_me.look] >> 2;
-					_me.yloc += _sint[_me.look] >> 2;
-					_me.xindex = _me.xloc >> 8;
-					_me.yindex = _me.yloc >> 8;
-					_change = true;
+				{
+					int xnew = _me.xloc + (_cost[_me.look] >> 2);
+					int ynew = _me.yloc + (_sint[_me.look] >> 2);
+					checkwall(xnew, ynew, &_me);
 					break;
+				}
 				case Common::KEYCODE_DOWN:
-					_me.xloc -= _cost[_me.look] >> 2;
-					_me.yloc -= _sint[_me.look] >> 2;
-					_me.xindex = _me.xloc >> 8;
-					_me.yindex = _me.yloc >> 8;
-					_change = true;
+				{
+					int xnew = _me.xloc - (_cost[_me.look] >> 2);
+					int ynew = _me.yloc - (_sint[_me.look] >> 2);
+					checkwall(xnew, ynew, &_me);
 					break;
+				}
 				case Common::KEYCODE_LEFT:
 					_me.look = (uint8)((int)_me.look + 8);
 					_change = true;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index e12d8a299dc..effbf6c906b 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -78,6 +78,7 @@ public:
 	void quadrant();
 	void perspective(int pnt[2], int rox, int roy);
 	void rot_init(int x, int y);
+	int checkwall(int xnew, int ynew, Locate *pobject);
 
 private:
 	const ADGameDescription *_gameDescription;


Commit: 9be5b46813c22590f3e28d3a4221068f2ed93fdc
    https://github.com/scummvm/scummvm/commit/9be5b46813c22590f3e28d3a4221068f2ed93fdc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:01+02:00

Commit Message:
COLONY: implement corridor side-cell traversal and screen-edge clipping

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index e3d59027088..41149867868 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -182,9 +182,6 @@ void ColonyEngine::perspective(int pnt[2], int rox, int roy) {
 		p = 32000;
 	pnt[0] = (int)p;
 
-	if (roy >= 11585)
-		roy = 11584;
-
 	if (_flip)
 		pnt[1] = _centerY + _rtable[roy];
 	else
@@ -343,18 +340,34 @@ void ColonyEngine::corridor() {
 	int length = 1;
 	int xFrontLeft, yFrontLeft;
 	int xFrontRight, yFrontRight;
+	int xsstart, ysstart;
 	int xfbehind, yfbehind;
-	int dr[2];
+	int roxsave, roysave;
+	int left, right;
+	int left2, right2;
 	int cellx, celly;
+	int cellxsave, cellysave;
+	int dr[2];
+	const int screenLeft = (int)_screenR.left;
+	const int screenRight = (int)_screenR.right;
 
 	quadrant();
 
+	right = screenRight;
+	left = screenLeft;
+	right2 = right;
+	left2 = left;
+
 	xfbehind = _me.xindex + _frntxWall;
 	yfbehind = _me.yindex + _frntyWall;
 	xFrontLeft = xfbehind + _frntx;
 	yFrontLeft = yfbehind + _frnty;
 	xFrontRight = xFrontLeft + _sidex;
 	yFrontRight = yFrontLeft + _sidey;
+	xsstart = _me.xindex + _sidexWall;
+	ysstart = _me.yindex + _sideyWall;
+	cellxsave = cellx = _me.xindex;
+	cellysave = celly = _me.yindex;
 
 	int rox = _rox;
 	int roy = _roy;
@@ -362,17 +375,27 @@ void ColonyEngine::corridor() {
 	if (_change) {
 		perspective(dr, rox, roy);
 		if (xfbehind >= 0 && xfbehind < 34 && yfbehind >= 0 && yfbehind < 34) {
-			_drX[xfbehind][yfbehind] = dr[0];
 			_drY[xfbehind][yfbehind] = dr[1];
+			if (dr[0] > _screenR.left)
+				_drX[xfbehind][yfbehind] = -32000;
+			else
+				_drX[xfbehind][yfbehind] = dr[0];
 		}
 
 		perspective(dr, rox + _tsin, roy + _tcos);
 		if (xfbehind + _sidex >= 0 && xfbehind + _sidex < 34 && yfbehind + _sidey >= 0 && yfbehind + _sidey < 34) {
-			_drX[xfbehind + _sidex][yfbehind + _sidey] = dr[0];
 			_drY[xfbehind + _sidex][yfbehind + _sidey] = dr[1];
+			if (dr[0] < _screenR.right)
+				_drX[xfbehind + _sidex][yfbehind + _sidey] = 32000;
+			else
+				_drX[xfbehind + _sidex][yfbehind + _sidey] = dr[0];
 		}
 	}
 
+	roxsave = rox;
+	roysave = roy;
+
+	// Move to the first wall in front of the observer.
 	rox -= _tcos;
 	roy += _tsin;
 
@@ -389,23 +412,22 @@ void ColonyEngine::corridor() {
 		}
 	}
 
+	if (wallAt(cellx + _sidexWall, celly + _sideyWall) & _side)
+		left2 = MAX(_drX[xFrontLeft][yFrontLeft], screenLeft);
+	else
+		left2 = MAX(left, left2);
+	left2 = MAX(left2, screenLeft);
+
+	if (wallAt(cellx + _sidexWall + _sidex, celly + _sideyWall + _sidey) & _side)
+		right2 = _drX[xFrontRight][yFrontRight];
+	else
+		right2 = MIN(right, right2);
+
 	uint32 white = _gfx->white();
-	// Draw starting lines (behind us)
-	_gfx->drawLine(_drX[xfbehind][yfbehind], _drY[xfbehind][yfbehind],
-	               _drX[xfbehind + _sidex][yfbehind + _sidey], _drY[xfbehind + _sidex][yfbehind + _sidey], white);
-	_gfx->drawLine(_drX[xfbehind][yfbehind], _height - _drY[xfbehind][yfbehind],
-	               _drX[xfbehind + _sidex][yfbehind + _sidey], _height - _drY[xfbehind + _sidex][yfbehind + _sidey], white);
-	_gfx->drawLine(_drX[xfbehind][yfbehind], _drY[xfbehind][yfbehind],
-	               _drX[xfbehind][yfbehind], _height - _drY[xfbehind][yfbehind], white);
-	_gfx->drawLine(_drX[xfbehind + _sidex][yfbehind + _sidey], _drY[xfbehind + _sidex][yfbehind + _sidey],
-	               _drX[xfbehind + _sidex][yfbehind + _sidey], _height - _drY[xfbehind + _sidex][yfbehind + _sidey], white);
-
-	int xprevL = xFrontLeft;
-	int yprevL = yFrontLeft;
-	int xprevR = xFrontRight;
-	int yprevR = yFrontRight;
-
-	while (!(_wall[xFrontLeft][yFrontLeft] & _front)) {
+	_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
+	               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+
+	while (!(wallAt(xFrontLeft, yFrontLeft) & _front)) {
 		rox -= _tcos;
 		roy += _tsin;
 		xFrontLeft += _frntx;
@@ -425,39 +447,44 @@ void ColonyEngine::corridor() {
 			}
 		}
 
-		// Horizontal transversal lines
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
+		cellx += _frntx;
+		celly += _frnty;
+		if (wallAt(cellx + _sidexWall, celly + _sideyWall) & _side)
+			left2 = MAX(screenLeft, _drX[xFrontLeft][yFrontLeft]);
+		else
+			left2 = MAX(left, left2);
+		left2 = MAX(left2, screenLeft);
+		if (wallAt(cellx + _sidexWall + _sidex, celly + _sideyWall + _sidey) & _side)
+			right2 = _drX[xFrontRight][yFrontRight];
+		else
+			right2 = MIN(right, right2);
 
-		// Longitudinal lines (floor and ceiling edges)
-		_gfx->drawLine(_drX[xprevL][yprevL], _drY[xprevL][yprevL],
-		               _drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft], white);
-		_gfx->drawLine(_drX[xprevR][yprevR], _drY[xprevR][yprevR],
-		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
-		_gfx->drawLine(_drX[xprevL][yprevL], _height - _drY[xprevL][yprevL],
-		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
-		_gfx->drawLine(_drX[xprevR][yprevR], _height - _drY[xprevR][yprevR],
-		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
-
-		// Vertical corner lines
 		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
-		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight],
-		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
-
-		xprevL = xFrontLeft;
-		yprevL = yFrontLeft;
-		xprevR = xFrontRight;
-		yprevR = yFrontRight;
+		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
 
 		length++;
 		if (length > 30)
 			break; // Safety break
 	}
 	drawend(xfbehind, yfbehind, xFrontLeft, yFrontLeft);
-	// _change = false; // Keep true for now to debug turning
+
+	left = screenLeft;
+	right = MIN(right, _drX[xFrontLeft][yFrontLeft]);
+	if (left < right)
+		checkleft(xsstart, ysstart, xfbehind, yfbehind, left, right, roxsave, roysave, cellxsave, cellysave, length);
+
+	left = MAX(left, _drX[xFrontRight][yFrontRight]);
+	if (left < screenLeft)
+		left = screenLeft;
+	right = screenRight;
+	xsstart += _sidex;
+	ysstart += _sidey;
+	xfbehind += _sidex;
+	yfbehind += _sidey;
+	if (left < right)
+		checkright(xsstart, ysstart, xfbehind, yfbehind, left, right, roxsave + _tsin, roysave + _tcos, cellxsave, cellysave, length);
+
+	_change = false;
 }
 
 void ColonyEngine::drawend(int xstart, int ystart, int xFrontLeft, int yFrontLeft) {
@@ -485,7 +512,7 @@ void ColonyEngine::drawend(int xstart, int ystart, int xFrontLeft, int yFrontLef
 		               _drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey], white);
 		if (_drY[xstart + _sidex][ystart + _sidey] > 0) {
 			_gfx->drawLine(_drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey],
-			               _drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey], white); // This one is still weird in original?
+			               _drX[xstart + _frntx + _sidex][ystart + _frnty + _sidey], _drY[xstart + _frntx + _sidex][ystart + _frnty + _sidey], white);
 		}
 	} else {
 		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
@@ -497,12 +524,332 @@ void ColonyEngine::drawend(int xstart, int ystart, int xFrontLeft, int yFrontLef
 	}
 }
 
+uint8 ColonyEngine::wallAt(int x, int y) const {
+	if (x < 0 || x >= 32 || y < 0 || y >= 32)
+		return 3;
+	return _wall[x][y];
+}
+
+void ColonyEngine::checkleft(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len) {
+	int i = 0, j;
+	int xf2, yf2;
+	int rox, roy;
+	int xsstart, ysstart;
+	int xfstart, yfstart;
+	int xestart, yestart;
+	int cellxsave, cellysave;
+	int dr[2];
+	uint32 white = _gfx->white();
+
+	cellx -= _sidex;
+	celly -= _sidey;
+	rx = rx - _tsin;
+	ry = ry - _tcos;
+
+	while (i < len && left <= right) {
+		if (wallAt(xs, ys) & _side) {
+			if (_flip)
+				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+
+			while ((wallAt(xs, ys) & _side) && i < len && left <= right) {
+				_gfx->drawLine(_drX[xf][yf], _drY[xf][yf], _drX[xf][yf], _height - _drY[xf][yf], white);
+				_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf],
+				               _drX[xf + _frntx][yf + _frnty], _height - _drY[xf + _frntx][yf + _frnty], white);
+
+				left = MAX(_drX[xf][yf], left);
+				xf += _frntx;
+				yf += _frnty;
+				xs += _frntx;
+				ys += _frnty;
+				cellx += _frntx;
+				celly += _frnty;
+				rx -= _tcos;
+				ry += _tsin;
+				i++;
+			}
+
+			if (_flip)
+				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+			_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf], _drX[xf][yf], _drY[xf][yf], white);
+			left = MAX(_drX[xf][yf], left);
+		}
+
+		if (i < len && left <= right) {
+			j = 0;
+			xf2 = xf - _sidex;
+			yf2 = yf - _sidey;
+			xfstart = xf2;
+			yfstart = yf2;
+			xsstart = xs - _sidex;
+			ysstart = ys - _sidey;
+			cellxsave = cellx;
+			cellysave = celly;
+
+			rox = rx;
+			roy = ry;
+			if (_change) {
+				perspective(dr, rx, ry);
+				_drX[xf2][yf2] = dr[0];
+				_drY[xf2][yf2] = dr[1];
+			}
+
+			while (!(wallAt(xs, ys) & _side) && i < len) {
+				rx -= _tcos;
+				ry += _tsin;
+				if (_change) {
+					perspective(dr, rx, ry);
+					_drX[xf2 + _frntx][yf2 + _frnty] = dr[0];
+					_drY[xf2 + _frntx][yf2 + _frnty] = dr[1];
+				}
+
+				if (_drX[xf + _frntx][yf + _frnty] > left) {
+					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2],
+					               _drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty], white);
+					_gfx->drawLine(_drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty],
+					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
+				} else {
+					j = 0;
+					xfstart = xf2;
+					yfstart = yf2;
+					xsstart = xs - _sidex;
+					ysstart = ys - _sidey;
+					rox = rx + _tcos;
+					roy = ry - _tsin;
+					cellxsave = cellx;
+					cellysave = celly;
+				}
+
+				xf2 += _frntx;
+				yf2 += _frnty;
+				xf += _frntx;
+				yf += _frnty;
+				xs += _frntx;
+				ys += _frnty;
+				cellx += _frntx;
+				celly += _frnty;
+				i++;
+				j++;
+			}
+
+			if (wallAt(xf - _sidex, yf - _sidey) & _front) {
+				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2], _drX[xf][yf], _height - _drY[xf][yf], white);
+
+				if (MIN(_drX[xf2][yf2], right) >= left) {
+					checkleft(xsstart, ysstart, xfstart, yfstart, left, MIN(right, _drX[xf2][yf2]),
+					          rox, roy, cellxsave, cellysave, j);
+				}
+			} else {
+				if (_flip)
+					_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+				xestart = xf2;
+				yestart = yf2;
+
+				while (!(wallAt(xf2, yf2) & _front)) {
+					rx -= _tcos;
+					ry += _tsin;
+					cellx += _frntx;
+					celly += _frnty;
+					xf2 += _frntx;
+					yf2 += _frnty;
+					xf += _frntx;
+					yf += _frnty;
+					xs += _frntx;
+					ys += _frnty;
+					if (_change) {
+						perspective(dr, rx, ry);
+						_drX[xf2][yf2] = dr[0];
+						_drY[xf2][yf2] = dr[1];
+					}
+					if (_change) {
+						perspective(dr, rx + _tsin, ry + _tcos);
+						_drX[xf][yf] = dr[0];
+						_drY[xf][yf] = dr[1];
+					}
+					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
+					i++;
+					j++;
+				}
+
+				_gfx->drawLine(_drX[xestart][yestart], _drY[xestart][yestart], _drX[xf2][yf2], _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2],
+				               _drX[xf2 + _sidex][yf2 + _sidey], _height - _drY[xf2 + _sidex][yf2 + _sidey], white);
+
+				if (MIN(_drX[xf2][yf2], right) >= left) {
+					checkleft(xsstart, ysstart, xfstart, yfstart, left, MIN(_drX[xf2][yf2], right),
+					          rox, roy, cellxsave, cellysave, j);
+				}
+			}
+		}
+	}
+}
+
+void ColonyEngine::checkright(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len) {
+	int i = 0, j;
+	int xf2, yf2;
+	int rox, roy;
+	int xsstart, ysstart;
+	int xfstart, yfstart;
+	int xestart, yestart;
+	int cellxsave, cellysave;
+	int dr[2];
+	uint32 white = _gfx->white();
+
+	cellx += _sidex;
+	celly += _sidey;
+	rx = rx + _tsin;
+	ry = ry + _tcos;
+
+	while (i < len && left < right) {
+		if (wallAt(xs, ys) & _side) {
+			if (_flip)
+				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+
+			while ((wallAt(xs, ys) & _side) && i < len && left < right) {
+				_gfx->drawLine(_drX[xf][yf], _drY[xf][yf], _drX[xf][yf], _height - _drY[xf][yf], white);
+				_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf],
+				               _drX[xf + _frntx][yf + _frnty], _height - _drY[xf + _frntx][yf + _frnty], white);
+
+				right = MIN(_drX[xf][yf], right);
+				xf += _frntx;
+				yf += _frnty;
+				xs += _frntx;
+				ys += _frnty;
+				cellx += _frntx;
+				celly += _frnty;
+				rx -= _tcos;
+				ry += _tsin;
+				i++;
+			}
+
+			if (_flip)
+				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+			_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf], _drX[xf][yf], _drY[xf][yf], white);
+			right = MIN(_drX[xf][yf], right);
+		}
+
+		if (i < len && left < right) {
+			j = 0;
+			xf2 = xf + _sidex;
+			yf2 = yf + _sidey;
+			xfstart = xf2;
+			yfstart = yf2;
+			xsstart = xs + _sidex;
+			ysstart = ys + _sidey;
+			cellxsave = cellx;
+			cellysave = celly;
+
+			rox = rx;
+			roy = ry;
+			if (_change) {
+				perspective(dr, rx, ry);
+				_drX[xf2][yf2] = dr[0];
+				_drY[xf2][yf2] = dr[1];
+			}
+
+			while (!(wallAt(xs, ys) & _side) && i < len) {
+				rx -= _tcos;
+				ry += _tsin;
+				if (_change) {
+					perspective(dr, rx, ry);
+					_drX[xf2 + _frntx][yf2 + _frnty] = dr[0];
+					_drY[xf2 + _frntx][yf2 + _frnty] = dr[1];
+				}
+
+				if (_drX[xf + _frntx][yf + _frnty] < right) {
+					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2],
+					               _drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty], white);
+					_gfx->drawLine(_drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty],
+					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
+				} else {
+					j = 0;
+					xfstart = xf2;
+					yfstart = yf2;
+					xsstart = xs + _sidex;
+					ysstart = ys + _sidey;
+					rox = rx + _tcos;
+					roy = ry - _tsin;
+					cellxsave = cellx;
+					cellysave = celly;
+				}
+
+				xf2 += _frntx;
+				yf2 += _frnty;
+				xf += _frntx;
+				yf += _frnty;
+				xs += _frntx;
+				ys += _frnty;
+				cellx += _frntx;
+				celly += _frnty;
+				i++;
+				j++;
+			}
+
+			if (wallAt(xf, yf) & _front) {
+				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2], _drX[xf][yf], _height - _drY[xf][yf], white);
+
+				if (MAX(_drX[xf2][yf2], left) < right) {
+					checkright(xsstart, ysstart, xfstart, yfstart, MAX(left, _drX[xf2][yf2]), right,
+					           rox, roy, cellxsave, cellysave, j);
+				}
+			} else {
+				if (_flip)
+					_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+				xestart = xf2;
+				yestart = yf2;
+
+				while (!(wallAt(xf, yf) & _front)) {
+					rx -= _tcos;
+					ry += _tsin;
+					cellx += _frntx;
+					celly += _frnty;
+					xf2 += _frntx;
+					yf2 += _frnty;
+					xf += _frntx;
+					yf += _frnty;
+					xs += _frntx;
+					ys += _frnty;
+					if (_change) {
+						perspective(dr, rx, ry);
+						_drX[xf2][yf2] = dr[0];
+						_drY[xf2][yf2] = dr[1];
+					}
+					if (_change) {
+						perspective(dr, rx - _tsin, ry - _tcos);
+						_drX[xf][yf] = dr[0];
+						_drY[xf][yf] = dr[1];
+					}
+					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
+					i++;
+					j++;
+				}
+
+				_gfx->drawLine(_drX[xestart][yestart], _drY[xestart][yestart], _drX[xf2][yf2], _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2],
+				               _drX[xf2 - _sidex][yf2 - _sidey], _height - _drY[xf2 - _sidex][yf2 - _sidey], white);
+
+				if (MAX(_drX[xf2][yf2], left) < right) {
+					checkright(xsstart, ysstart, xfstart, yfstart, MAX(_drX[xf2][yf2], left), right,
+					           rox, roy, cellxsave, cellysave, j);
+				}
+			}
+		}
+	}
+}
+
 Common::Error ColonyEngine::run() {
 	Graphics::PixelFormat format8bpp = Graphics::PixelFormat::createFormatCLUT8();
 	initGraphics(_width, _height, &format8bpp);
 
 	_width = _system->getWidth();
 	_height = _system->getHeight();
+	_centerX = _width / 2;
+	_centerY = _height / 2;
+	_screenR = Common::Rect(0, 0, _width, _height);
+	_clip = _screenR;
 	const Graphics::PixelFormat format = _system->getScreenFormat();
 	debug("Screen format: %d bytesPerPixel. Actual size: %dx%d", format.bytesPerPixel, _width, _height);
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index effbf6c906b..94483ad9c7b 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -120,6 +120,9 @@ private:
 	Common::Rect _screenR;
 
 	void drawend(int xstart, int ystart, int xFrontLeft, int yFrontLeft);
+	void checkleft(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len);
+	void checkright(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len);
+	uint8 wallAt(int x, int y) const;
 };
 
 } // End of namespace Colony


Commit: 3ec672d0a6da5b434cf66971ed2b3c613c85666b
    https://github.com/scummvm/scummvm/commit/3ec672d0a6da5b434cf66971ed2b3c613c85666b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:01+02:00

Commit Message:
COLONY: add wall feature rendering (doors, windows, shelves, stairs)

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 41149867868..1620c9a44d3 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -75,6 +75,24 @@ static const int g_indexTable[4][10] = {
 	{0, 0,  0, 1,  1,  0,  0, -1,  2, 1}
 };
 
+enum WallFeatureType {
+	kWallFeatureNone = 0,
+	kWallFeatureDoor = 2,
+	kWallFeatureWindow = 3,
+	kWallFeatureShelves = 4,
+	kWallFeatureUpStairs = 5,
+	kWallFeatureDnStairs = 6,
+	kWallFeatureChar = 7,
+	kWallFeatureGlyph = 8,
+	kWallFeatureElevator = 9,
+	kWallFeatureTunnel = 10,
+	kWallFeatureAirlock = 11,
+	kWallFeatureColor = 12
+};
+
+static const int g_dirRight[4] = {1, 3, 0, 2};
+static const int g_dirLeft[4] = {2, 0, 3, 1};
+
 ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd) {
 	_level = 0;
 	_robotNum = 0;
@@ -426,6 +444,8 @@ void ColonyEngine::corridor() {
 	uint32 white = _gfx->white();
 	_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
 	               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+	if (wallAt(cellx, celly) & ~0x03)
+		frontfeature(cellx, celly, xFrontLeft, yFrontLeft, left2, right2, rox, roy);
 
 	while (!(wallAt(xFrontLeft, yFrontLeft) & _front)) {
 		rox -= _tcos;
@@ -458,6 +478,8 @@ void ColonyEngine::corridor() {
 			right2 = _drX[xFrontRight][yFrontRight];
 		else
 			right2 = MIN(right, right2);
+		if (wallAt(cellx, celly) & ~0x03)
+			features(cellx, celly, xFrontLeft, yFrontLeft, left2, right2, rox, roy);
 
 		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
 		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
@@ -607,6 +629,8 @@ void ColonyEngine::checkleft(int xs, int ys, int xf, int yf, int left, int right
 					               _drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty], white);
 					_gfx->drawLine(_drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty],
 					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
+					if (wallAt(cellx, celly) & ~0x03)
+						features(cellx, celly, xf2 + _frntx, yf2 + _frnty, left, right, rx, ry);
 				} else {
 					j = 0;
 					xfstart = xf2;
@@ -667,6 +691,8 @@ void ColonyEngine::checkleft(int xs, int ys, int xf, int yf, int left, int right
 						_drY[xf][yf] = dr[1];
 					}
 					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
+					if (wallAt(cellx - _frntx, celly - _frnty) & ~0x03)
+						features(cellx - _frntx, celly - _frnty, xf2, yf2, left, right, rx, ry);
 					i++;
 					j++;
 				}
@@ -762,6 +788,8 @@ void ColonyEngine::checkright(int xs, int ys, int xf, int yf, int left, int righ
 					               _drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty], white);
 					_gfx->drawLine(_drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty],
 					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
+					if (wallAt(cellx, celly) & ~0x03)
+						features(cellx, celly, xf + _frntx, yf + _frnty, left, right, rx - _tsin, ry - _tcos);
 				} else {
 					j = 0;
 					xfstart = xf2;
@@ -822,6 +850,8 @@ void ColonyEngine::checkright(int xs, int ys, int xf, int yf, int left, int righ
 						_drY[xf][yf] = dr[1];
 					}
 					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
+					if (wallAt(cellx - _frntx, celly - _frnty) & ~0x03)
+						features(cellx - _frntx, celly - _frnty, xf, yf, left, right, rx - _tsin, ry - _tcos);
 					i++;
 					j++;
 				}
@@ -840,6 +870,577 @@ void ColonyEngine::checkright(int xs, int ys, int xf, int yf, int left, int righ
 	}
 }
 
+const uint8 *ColonyEngine::mapFeatureAt(int x, int y, int direction) const {
+	if (x < 0 || x >= 31 || y < 0 || y >= 31 || direction < 0 || direction >= 5)
+		return nullptr;
+	return _mapData[x][y][direction];
+}
+
+void ColonyEngine::frontfeature(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry) {
+	int l[4], r[4];
+
+	l[0] = _drX[xFront][yFront];
+	l[2] = rx - _tcos;
+	l[3] = ry + _tsin;
+	r[0] = _drX[xFront + _sidex][yFront + _sidey];
+	r[2] = rx + _tsin - _tcos;
+	r[3] = ry + _tsin + _tcos;
+	if (_flip) {
+		l[1] = _height - _drY[xFront][yFront];
+		r[1] = _height - _drY[xFront + _sidex][yFront + _sidey];
+	} else {
+		l[1] = _drY[xFront][yFront];
+		r[1] = _drY[xFront + _sidex][yFront + _sidey];
+	}
+
+	if (MAX(left, l[0]) < MIN(right, r[0])) {
+		const uint8 *map = mapFeatureAt(cellx, celly, _direction);
+		if (map && map[0])
+			dowall(cellx, celly, _direction, l, r);
+	}
+}
+
+void ColonyEngine::features(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry) {
+	int l[4], r[4], ll[4], rr[4];
+
+	l[0] = _drX[xFront][yFront];
+	l[2] = rx - _tcos;
+	l[3] = ry + _tsin;
+	r[0] = _drX[xFront + _sidex][yFront + _sidey];
+	r[2] = rx + _tsin - _tcos;
+	r[3] = ry + _tsin + _tcos;
+	if (_flip) {
+		l[1] = _height - _drY[xFront][yFront];
+		r[1] = _height - _drY[xFront + _sidex][yFront + _sidey];
+	} else {
+		l[1] = _drY[xFront][yFront];
+		r[1] = _drY[xFront + _sidex][yFront + _sidey];
+	}
+
+	if (MAX(left, l[0]) + 1 < MIN(right, r[0]) - 1) {
+		const uint8 *map = mapFeatureAt(cellx, celly, _direction);
+		if (map && map[0])
+			dowall(cellx, celly, _direction, l, r);
+	}
+
+	ll[0] = r[0];
+	ll[1] = r[1];
+	ll[2] = rx + _tsin + _tsin;
+	ll[3] = ry + _tcos + _tcos;
+	rr[0] = _drX[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
+	if (_flip)
+		rr[1] = _height - _drY[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
+	else
+		rr[1] = _drY[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
+	rr[2] = rx + _tsin + _tsin + _tcos;
+	rr[3] = ry + _tcos + _tcos - _tsin;
+	if (MAX(left, ll[0]) + 1 < MIN(right, rr[0]) - 1) {
+		const uint8 *map = mapFeatureAt(cellx, celly, g_dirRight[_direction]);
+		if (map && map[0])
+			dowall(cellx, celly, g_dirRight[_direction], ll, rr);
+	}
+
+	ll[0] = _drX[xFront - _frntx][yFront - _frnty];
+	if (_flip)
+		ll[1] = _height - _drY[xFront - _frntx][yFront - _frnty];
+	else
+		ll[1] = _drY[xFront - _frntx][yFront - _frnty];
+	ll[2] = rx + _tcos - _tsin;
+	ll[3] = (ry - _tcos) - _tsin;
+	rr[0] = l[0];
+	rr[1] = l[1];
+	rr[2] = rx - _tsin;
+	rr[3] = ry - _tcos;
+	if (MAX(left, ll[0]) + 1 < MIN(right, rr[0]) - 1) {
+		const uint8 *map = mapFeatureAt(cellx, celly, g_dirLeft[_direction]);
+		if (map && map[0])
+			dowall(cellx, celly, g_dirLeft[_direction], ll, rr);
+	}
+}
+
+void ColonyEngine::dowall(int cellx, int celly, int direction, int left[4], int right[4]) {
+	const uint8 *map = mapFeatureAt(cellx, celly, direction);
+	int left2[2], right2[2];
+	if (!map)
+		return;
+
+	switch (map[0]) {
+	case kWallFeatureDoor:
+		if (_level == 1 || _level == 5 || _level == 6) {
+			if (map[1] == 0)
+				drawOpenSSDoor(left, right);
+			else
+				drawClosedSSDoor(left, right);
+		} else {
+			if (map[1] == 0) {
+				perspective(left2, left[2], left[3]);
+				perspective(right2, right[2], right[3]);
+				if (_flip) {
+					left2[1] = _height - left2[1];
+					right2[1] = _height - right2[1];
+				}
+				drawOpenDoor(left, right, left2, right2);
+			} else {
+				drawClosedDoor(left, right);
+			}
+		}
+		break;
+	case kWallFeatureWindow:
+		drawWindow(left, right);
+		break;
+	case kWallFeatureShelves:
+		perspective(left2, left[2], left[3]);
+		perspective(right2, right[2], right[3]);
+		if (_flip) {
+			left2[1] = _height - left2[1];
+			right2[1] = _height - right2[1];
+		}
+		drawBooks(left, right, left2, right2);
+		break;
+	case kWallFeatureUpStairs:
+		perspective(left2, left[2], left[3]);
+		perspective(right2, right[2], right[3]);
+		if (_flip) {
+			left2[1] = _height - left2[1];
+			right2[1] = _height - right2[1];
+		}
+		drawUpStairs(left, right, left2, right2);
+		break;
+	case kWallFeatureDnStairs:
+		perspective(left2, left[2], left[3]);
+		perspective(right2, right[2], right[3]);
+		if (_flip) {
+			left2[1] = _height - left2[1];
+			right2[1] = _height - right2[1];
+		}
+		drawDnStairs(left, right, left2, right2);
+		break;
+	case kWallFeatureGlyph:
+		drawGlyphs(left, right);
+		break;
+	case kWallFeatureElevator:
+		drawElevator(left, right);
+		break;
+	case kWallFeatureTunnel:
+		perspective(left2, left[2], left[3]);
+		perspective(right2, right[2], right[3]);
+		if (_flip) {
+			left2[1] = _height - left2[1];
+			right2[1] = _height - right2[1];
+		}
+		drawTunnel(left, right, left2, right2);
+		break;
+	case kWallFeatureAirlock:
+		if (map[1] == 0)
+			drawALOpen(left, right);
+		else
+			drawALClosed(left, right);
+		break;
+	case kWallFeatureColor:
+		drawColor(map, left, right);
+		break;
+	default:
+		break;
+	}
+}
+
+void ColonyEngine::drawWindow(int left[4], int right[4]) {
+	const uint32 dark = 160;
+	int x1 = left[0];
+	int x2 = right[0];
+	int y1 = left[1];
+	int y2 = right[1];
+	int y3 = _height - right[1];
+	int y4 = _height - left[1];
+	int xc = (x1 + x2) >> 1;
+	int xx1 = (xc + x1) >> 1;
+	int xx2 = (xc + x2) >> 1;
+	if (xx2 < _screenR.left || xx1 > _screenR.right)
+		return;
+	int yl = (y1 + y4) >> 1;
+	int yr = (y2 + y3) >> 1;
+	int yy1 = (yl + y1) >> 1;
+	int yy2 = (yr + y2) >> 1;
+	int yy3 = (yl + y3) >> 1;
+	int yy4 = (yr + y4) >> 1;
+	int yy[4];
+	yy[0] = _height - ((((yy1 + yy2) >> 1) + yy1) >> 1);
+	yy[1] = _height - ((((yy1 + yy2) >> 1) + yy2) >> 1);
+	yy[2] = _height - ((((yy3 + yy4) >> 1) + yy3) >> 1);
+	yy[3] = _height - ((((yy3 + yy4) >> 1) + yy4) >> 1);
+	_gfx->drawLine(xx1, yy[0], xx2, yy[1], dark);
+	_gfx->drawLine(xx2, yy[1], xx2, yy[2], dark);
+	_gfx->drawLine(xx2, yy[2], xx1, yy[3], dark);
+	_gfx->drawLine(xx1, yy[3], xx1, yy[0], dark);
+	_gfx->drawLine(xc, (yy[0] + yy[1]) >> 1, xc, (yy[2] + yy[3]) >> 1, dark);
+	_gfx->drawLine(xx1, yl, xx2, yr, dark);
+}
+
+void ColonyEngine::drawClosedDoor(int left[4], int right[4]) {
+	const uint32 dark = 160;
+	int x1 = left[0];
+	int x2 = right[0];
+	int y1 = left[1];
+	int y2 = right[1];
+	int y3 = _height - right[1];
+	int y4 = _height - left[1];
+	int xc = (x1 + x2) >> 1;
+	int xx1 = (xc + x1) >> 1;
+	int xx2 = (xc + x2) >> 1;
+	if (xx2 < _screenR.left || xx1 > _screenR.right)
+		return;
+
+	int yc = (y1 + y2) >> 1;
+	int ytl = (yc + y1) >> 1;
+	int ytr = (yc + y2) >> 1;
+	yc = (y4 + y3) >> 1;
+	int ybl = (yc + y4) >> 1;
+	int ybr = (yc + y3) >> 1;
+	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
+	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
+
+	_gfx->drawLine(xx1, ybl, xx1, ytl, dark);
+	_gfx->drawLine(xx1, ytl, xx2, ytr, dark);
+	_gfx->drawLine(xx2, ytr, xx2, ybr, dark);
+	_gfx->drawLine(xx2, ybr, xx1, ybl, dark);
+
+	ybl = (ybl + ytl) >> 1;
+	ybr = (ybr + ytr) >> 1;
+	yc = (ybl + ybr) >> 1;
+	ybl = (((yc + ybl) >> 1) + ybl) >> 1;
+	ybr = (((yc + ybr) >> 1) + ybr) >> 1;
+	xx1 = (((xx1 + xc) >> 1) + xx1) >> 1;
+	xx2 = (((xx2 + xc) >> 1) + xx2) >> 1;
+	_gfx->drawLine(xx1, ybl, xx2, ybr, dark);
+}
+
+void ColonyEngine::drawOpenDoor(int left[4], int right[4], int left2[2], int right2[2]) {
+	const uint32 dark = 160;
+	const uint32 light = 210;
+	int x1 = left[0];
+	int x2 = right[0];
+	int y1 = left[1];
+	int y2 = right[1];
+	int y3 = _height - right[1];
+	int y4 = _height - left[1];
+	int xc = (x1 + x2) >> 1;
+	int xl = (xc + x1) >> 1;
+	int xr = (xc + x2) >> 1;
+	int yc = (y1 + y2) >> 1;
+	int ytl = (yc + y1) >> 1;
+	int ytr = (yc + y2) >> 1;
+	yc = (y4 + y3) >> 1;
+	int ybl = (yc + y4) >> 1;
+	int ybr = (yc + y3) >> 1;
+	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
+	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
+	if (xr < _screenR.left || xl > _screenR.right)
+		return;
+
+	_gfx->drawLine(xl, ybl, xl, ytl, dark);
+	_gfx->drawLine(xl, ytl, xr, ytr, dark);
+	_gfx->drawLine(xr, ytr, xr, ybr, dark);
+	_gfx->drawLine(xr, ybr, xl, ybl, dark);
+
+	x1 = left2[0];
+	x2 = right2[0];
+	y1 = _height - left2[1];
+	y2 = _height - right2[1];
+	xc = (x1 + x2) >> 1;
+	int xfl = (xc + x1) >> 1;
+	int xfr = (xc + x2) >> 1;
+	yc = (y1 + y2) >> 1;
+	int yfl = (yc + y1) >> 1;
+	int yfr = (yc + y2) >> 1;
+
+	_gfx->drawLine(xl, ybl, xfl, yfl, light);
+	_gfx->drawLine(xfl, yfl, xfr, yfr, light);
+	_gfx->drawLine(xfr, yfr, xr, ybr, light);
+	_gfx->drawLine(xr, ybr, xl, ybl, light);
+}
+
+void ColonyEngine::drawTunnel(int left[4], int right[4], int left2[2], int right2[2]) {
+	const uint32 dark = 120;
+	int baseX[7], baseY[7], tunnelY[7][7];
+	int xl = left[0];
+	int xr = right[0];
+	int ytl = left[1];
+	int ytr = right[1];
+	int ybr = _height - right[1];
+	int ybl = _height - left[1];
+	int hl = ybl - ytl;
+	int hr = ybr - ytr;
+	(void)left2;
+	(void)right2;
+	(void)MAX(hl, hr);
+	split7(baseX, xl, xr);
+	if (baseX[0] > _screenR.right || baseX[6] < _screenR.left)
+		return;
+	split7(baseY, ybl, ybr);
+	for (int i = 0; i < 7; i++)
+		split7(tunnelY[i], baseY[i], _height - baseY[i]);
+
+	int x[6] = {baseX[0], baseX[0], baseX[1], baseX[5], baseX[6], baseX[6]};
+	int y[6] = {baseY[0], tunnelY[0][5], tunnelY[1][6], tunnelY[5][6], tunnelY[6][5], baseY[6]};
+	for (int i = 0; i < 6; i++) {
+		int n = (i + 1) % 6;
+		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
+	}
+}
+
+void ColonyEngine::drawGlyphs(int left[4], int right[4]) {
+	const uint32 dark = 170;
+	int xl = left[0];
+	int xr = right[0];
+	int y1 = left[1];
+	int y2 = right[1];
+	int y3 = _height - right[1];
+	int y4 = _height - left[1];
+	int xc = (xl + xr) >> 1;
+	xl = (((xc + xl) >> 1) + xl) >> 1;
+	xr = (((xc + xr) >> 1) + xr) >> 1;
+	int ytc = (y1 + y2) >> 1;
+	int ybc = (y3 + y4) >> 1;
+	int ytl = (((y1 + ytc) >> 1) + y1) >> 1;
+	int ytr = (((y2 + ytc) >> 1) + y2) >> 1;
+	int ybl = (((y4 + ybc) >> 1) + y4) >> 1;
+	int ybr = (((y3 + ybc) >> 1) + y3) >> 1;
+	int yl1 = (ytl + ybl) >> 1;
+	int yr1 = (ytr + ybr) >> 1;
+	int yl2 = (yl1 + ytl) >> 1;
+	int yr2 = (yr1 + ytr) >> 1;
+	int yl3 = (yl2 + yl1) >> 1;
+	int yr3 = (yr2 + yr1) >> 1;
+	int yl4 = (yl1 + ybl) >> 1;
+	int yr4 = (yr1 + ybr) >> 1;
+	int yr5 = (yr4 + yr1) >> 1;
+	int yl5 = (yl4 + yl1) >> 1;
+
+	_gfx->drawLine(xl, yl1, xr, yr1, dark);
+	_gfx->drawLine(xl, yl2, xr, yr2, dark);
+	_gfx->drawLine(xl, yl3, xr, yr3, dark);
+	_gfx->drawLine(xl, yl4, xr, yr4, dark);
+	_gfx->drawLine(xl, yl5, xr, yr5, dark);
+	_gfx->drawLine(xl, (yl2 + yl3) >> 1, xr, (yr2 + yr3) >> 1, dark);
+	_gfx->drawLine(xl, (yl3 + yl1) >> 1, xr, (yr3 + yr1) >> 1, dark);
+	_gfx->drawLine(xl, (yl1 + yl5) >> 1, xr, (yr1 + yr5) >> 1, dark);
+	_gfx->drawLine(xl, (yl4 + yl5) >> 1, xr, (yr4 + yr5) >> 1, dark);
+}
+
+void ColonyEngine::drawBooks(int left[4], int right[4], int left2[2], int right2[2]) {
+	const uint32 dark = 170;
+	int l2[2] = {left2[0], left2[1]};
+	int r2[2] = {right2[0], right2[1]};
+	for (int i = 0; i < 2; i++) {
+		l2[0] = (l2[0] + left[0]) >> 1;
+		l2[1] = (l2[1] + left[1]) >> 1;
+		r2[0] = (r2[0] + right[0]) >> 1;
+		r2[1] = (r2[1] + right[1]) >> 1;
+	}
+	_gfx->drawLine(l2[0], l2[1], l2[0], _height - l2[1], dark);
+	_gfx->drawLine(l2[0], _height - l2[1], r2[0], _height - r2[1], dark);
+	_gfx->drawLine(r2[0], _height - r2[1], r2[0], r2[1], dark);
+	_gfx->drawLine(r2[0], r2[1], l2[0], l2[1], dark);
+	_gfx->drawLine(left[0], left[1], l2[0], l2[1], dark);
+	_gfx->drawLine(left[0], _height - left[1], l2[0], _height - l2[1], dark);
+	_gfx->drawLine(right[0], right[1], r2[0], r2[1], dark);
+	_gfx->drawLine(right[0], _height - right[1], r2[0], _height - r2[1], dark);
+
+	int lf[7], rf[7], lb[7], rb[7];
+	split7(lf, left[1], _height - left[1]);
+	split7(rf, right[1], _height - right[1]);
+	split7(lb, l2[1], _height - l2[1]);
+	split7(rb, r2[1], _height - r2[1]);
+	for (int i = 0; i < 7; i++) {
+		_gfx->drawLine(left[0], lf[i], right[0], rf[i], dark);
+		_gfx->drawLine(right[0], rf[i], r2[0], rb[i], dark);
+		_gfx->drawLine(r2[0], rb[i], l2[0], lb[i], dark);
+		_gfx->drawLine(l2[0], lb[i], left[0], lf[i], dark);
+	}
+}
+
+void ColonyEngine::drawUpStairs(int left[4], int right[4], int left2[2], int right2[2]) {
+	const uint32 dark = 170;
+	int xl[7], xr[7], yl[7], yr[7];
+	split7(xl, left[0], left2[0]);
+	split7(xr, right[0], right2[0]);
+	split7(yl, _height - left[1], left2[1]);
+	split7(yr, _height - right[1], right2[1]);
+	for (int i = 0; i < 6; i++) {
+		_gfx->drawLine(xl[i], yl[i], xl[i + 1], yl[i + 1], dark);
+		_gfx->drawLine(xr[i], yr[i], xr[i + 1], yr[i + 1], dark);
+		_gfx->drawLine(xl[i], yl[i], xr[i], yr[i], dark);
+	}
+}
+
+void ColonyEngine::drawDnStairs(int left[4], int right[4], int left2[2], int right2[2]) {
+	const uint32 dark = 170;
+	int xl[7], xr[7], yl[7], yr[7];
+	split7(xl, left[0], left2[0]);
+	split7(xr, right[0], right2[0]);
+	split7(yl, left[1], left2[1]);
+	split7(yr, right[1], right2[1]);
+	for (int i = 0; i < 6; i++) {
+		_gfx->drawLine(xl[i], yl[i], xl[i + 1], yl[i + 1], dark);
+		_gfx->drawLine(xr[i], yr[i], xr[i + 1], yr[i + 1], dark);
+		_gfx->drawLine(xl[i], _height - yl[i], xr[i], _height - yr[i], dark);
+	}
+}
+
+void ColonyEngine::drawALOpen(int left[4], int right[4]) {
+	const uint32 dark = 150;
+	int lr[7], ud[7][7];
+	split7x7(left, right, lr, ud);
+	int x[8] = {lr[0], lr[1], lr[3], lr[5], lr[6], lr[5], lr[3], lr[1]};
+	int y[8] = {ud[3][0], ud[5][1], ud[6][3], ud[5][5], ud[3][6], ud[1][5], ud[0][3], ud[1][1]};
+	for (int i = 0; i < 8; i++) {
+		int n = (i + 1) % 8;
+		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
+	}
+}
+
+void ColonyEngine::drawALClosed(int left[4], int right[4]) {
+	const uint32 dark = 170;
+	int lr[7], ud[7][7];
+	split7x7(left, right, lr, ud);
+	int x[8] = {lr[0], lr[1], lr[3], lr[5], lr[6], lr[5], lr[3], lr[1]};
+	int y[8] = {ud[3][0], ud[5][1], ud[6][3], ud[5][5], ud[3][6], ud[1][5], ud[0][3], ud[1][1]};
+	for (int i = 0; i < 8; i++) {
+		int n = (i + 1) % 8;
+		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
+	}
+	_gfx->drawLine(lr[0], ud[3][0], lr[3], ud[3][3], dark);
+	_gfx->drawLine(lr[3], ud[6][3], lr[3], ud[3][3], dark);
+	_gfx->drawLine(lr[6], ud[3][6], lr[3], ud[3][3], dark);
+	_gfx->drawLine(lr[3], ud[0][3], lr[3], ud[3][3], dark);
+}
+
+void ColonyEngine::drawOpenSSDoor(int left[4], int right[4]) {
+	const uint32 dark = 140;
+	int lr[7], ud[7][7];
+	split7x7(left, right, lr, ud);
+	int x[8] = {lr[2], lr[1], lr[1], lr[2], lr[4], lr[5], lr[5], lr[4]};
+	int y[8] = {ud[0][2], ud[1][1], ud[5][1], ud[6][2], ud[6][4], ud[5][5], ud[1][5], ud[0][4]};
+	for (int i = 0; i < 8; i++) {
+		int n = (i + 1) % 8;
+		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
+	}
+}
+
+void ColonyEngine::drawClosedSSDoor(int left[4], int right[4]) {
+	const uint32 dark = 170;
+	int lr[7], ud[7][7];
+	split7x7(left, right, lr, ud);
+	int x[8] = {lr[2], lr[1], lr[1], lr[2], lr[4], lr[5], lr[5], lr[4]};
+	int y[8] = {ud[0][2], ud[1][1], ud[5][1], ud[6][2], ud[6][4], ud[5][5], ud[1][5], ud[0][4]};
+	for (int i = 0; i < 8; i++) {
+		int n = (i + 1) % 8;
+		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
+	}
+	_gfx->drawLine(lr[2], ud[1][2], lr[2], ud[5][2], dark);
+	_gfx->drawLine(lr[2], ud[5][2], lr[4], ud[5][4], dark);
+	_gfx->drawLine(lr[4], ud[5][4], lr[4], ud[1][4], dark);
+	_gfx->drawLine(lr[4], ud[1][4], lr[2], ud[1][2], dark);
+}
+
+void ColonyEngine::drawElevator(int left[4], int right[4]) {
+	const uint32 dark = 170;
+	int x1 = left[0];
+	int x2 = right[0];
+	int y1 = left[1];
+	int y2 = right[1];
+	int y3 = _height - right[1];
+	int y4 = _height - left[1];
+	int xc = (x1 + x2) >> 1;
+	int xx1 = (xc + x1) >> 1;
+	xx1 = (x1 + xx1) >> 1;
+	int xx2 = (xc + x2) >> 1;
+	xx2 = (x2 + xx2) >> 1;
+	if (xx2 < _screenR.left || xx1 > _screenR.right)
+		return;
+	int ytc = (y1 + y2) >> 1;
+	int ytl = (ytc + y1) >> 1;
+	ytl = (ytl + y1) >> 1;
+	int ytr = (ytc + y2) >> 1;
+	ytr = (ytr + y2) >> 1;
+	int ybc = (y4 + y3) >> 1;
+	int ybl = (ybc + y4) >> 1;
+	ybl = (ybl + y4) >> 1;
+	int ybr = (ybc + y3) >> 1;
+	ybr = (ybr + y3) >> 1;
+	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
+	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
+	_gfx->drawLine(xx1, ybl, xx1, ytl, dark);
+	_gfx->drawLine(xx1, ytl, xx2, ytr, dark);
+	_gfx->drawLine(xx2, ytr, xx2, ybr, dark);
+	_gfx->drawLine(xx2, ybr, xx1, ybl, dark);
+	_gfx->drawLine(xc, ybc, xc, (ytl + ytr) >> 1, dark);
+}
+
+void ColonyEngine::drawColor(const uint8 *map, int left[4], int right[4]) {
+	int xl = left[0];
+	int xr = right[0];
+	int yl[5], yr[5];
+	yl[0] = left[1];
+	yr[0] = right[1];
+	yl[4] = _height - yl[0];
+	yr[4] = _height - yr[0];
+	yl[2] = (yl[0] + yl[4]) >> 1;
+	yr[2] = (yr[0] + yr[4]) >> 1;
+	yl[1] = (yl[0] + yl[2]) >> 1;
+	yl[3] = (yl[2] + yl[4]) >> 1;
+	yr[1] = (yr[0] + yr[2]) >> 1;
+	yr[3] = (yr[2] + yr[4]) >> 1;
+
+	if (map[1] || map[2] || map[3] || map[4]) {
+		for (int i = 1; i <= 3; i++) {
+			uint32 c = 120 + map[i] * 20;
+			_gfx->drawLine(xl, yl[i], xr, yr[i], c);
+		}
+	} else {
+		uint32 c = 100 + (_level * 15);
+		_gfx->drawLine(xl, yl[1], xr, yr[1], c);
+		_gfx->drawLine(xl, yl[2], xr, yr[2], c);
+		_gfx->drawLine(xl, yl[3], xr, yr[3], c);
+	}
+}
+
+void ColonyEngine::split7(int arr[7], int x1, int x2) const {
+	arr[3] = (x1 + x2) >> 1;
+	arr[1] = (x1 + arr[3]) >> 1;
+	arr[0] = (x1 + arr[1]) >> 1;
+	arr[2] = (arr[1] + arr[3]) >> 1;
+	arr[5] = (arr[3] + x2) >> 1;
+	arr[6] = (arr[5] + x2) >> 1;
+	arr[4] = (arr[3] + arr[5]) >> 1;
+}
+
+void ColonyEngine::split7x7(int left[4], int right[4], int lr[7], int ud[7][7]) const {
+	int leftX, rightX, leftY, rightY;
+	int lud[7], rud[7];
+	if (right[0] < left[0]) {
+		rightX = left[0];
+		leftX = right[0];
+		rightY = left[1];
+		leftY = right[1];
+	} else {
+		leftX = left[0];
+		rightX = right[0];
+		leftY = left[1];
+		rightY = right[1];
+	}
+	split7(lr, leftX, rightX);
+	if (_flip) {
+		split7(lud, leftY, _height - leftY);
+		split7(rud, rightY, _height - rightY);
+	} else {
+		split7(lud, _height - leftY, leftY);
+		split7(rud, _height - rightY, rightY);
+	}
+	for (int i = 0; i < 7; i++)
+		split7(ud[i], lud[i], rud[i]);
+}
+
 Common::Error ColonyEngine::run() {
 	Graphics::PixelFormat format8bpp = Graphics::PixelFormat::createFormatCLUT8();
 	initGraphics(_width, _height, &format8bpp);
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 94483ad9c7b..2d2d32739c5 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -123,6 +123,26 @@ private:
 	void checkleft(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len);
 	void checkright(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len);
 	uint8 wallAt(int x, int y) const;
+	const uint8 *mapFeatureAt(int x, int y, int direction) const;
+	void frontfeature(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry);
+	void features(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry);
+	void dowall(int cellx, int celly, int direction, int left[4], int right[4]);
+	void drawWindow(int left[4], int right[4]);
+	void drawClosedDoor(int left[4], int right[4]);
+	void drawOpenDoor(int left[4], int right[4], int left2[2], int right2[2]);
+	void drawTunnel(int left[4], int right[4], int left2[2], int right2[2]);
+	void drawGlyphs(int left[4], int right[4]);
+	void drawBooks(int left[4], int right[4], int left2[2], int right2[2]);
+	void drawUpStairs(int left[4], int right[4], int left2[2], int right2[2]);
+	void drawDnStairs(int left[4], int right[4], int left2[2], int right2[2]);
+	void drawALOpen(int left[4], int right[4]);
+	void drawALClosed(int left[4], int right[4]);
+	void drawOpenSSDoor(int left[4], int right[4]);
+	void drawClosedSSDoor(int left[4], int right[4]);
+	void drawElevator(int left[4], int right[4]);
+	void drawColor(const uint8 *map, int left[4], int right[4]);
+	void split7(int arr[7], int x1, int x2) const;
+	void split7x7(int left[4], int right[4], int lr[7], int ud[7][7]) const;
 };
 
 } // End of namespace Colony


Commit: ae272e3051daf0a7f37fed5df9821a46ada18a9b
    https://github.com/scummvm/scummvm/commit/ae272e3051daf0a7f37fed5df9821a46ada18a9b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:01+02:00

Commit Message:
COLONY: add configure.engine registration file

Changed paths:
  A engines/colony/configure.engine


diff --git a/engines/colony/configure.engine b/engines/colony/configure.engine
new file mode 100644
index 00000000000..ee31d680cfd
--- /dev/null
+++ b/engines/colony/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
+add_engine colony "The Colony" yes "" "" "highres 16bit 3d" ""


Commit: 4fb016c9528ce3af71664a6075cb7ee374adb6dd
    https://github.com/scummvm/scummvm/commit/4fb016c9528ce3af71664a6075cb7ee374adb6dd
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:02+02:00

Commit Message:
COLONY: some static object rendering code

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 1620c9a44d3..14b54756e23 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -24,6 +24,7 @@
 #include "common/file.h"
 #include "common/system.h"
 #include "common/util.h"
+#include "common/algorithm.h"
 #include "common/debug.h"
 #include "common/events.h"
 #include "common/keyboard.h"
@@ -145,6 +146,11 @@ void ColonyEngine::loadMap(int mnum) {
 	file.read(buffer, bLength);
 	file.close();
 
+	memset(_mapData, 0, sizeof(_mapData));
+	memset(_robotArray, 0, sizeof(_robotArray));
+	memset(_foodArray, 0, sizeof(_foodArray));
+	_objects.clear();
+
 	// expand logic
 	int c = 0;
 	_robotNum = MENUM + 1;
@@ -157,7 +163,24 @@ void ColonyEngine::loadMap(int mnum) {
 						for (int l = 0; l < 5; l++) {
 							_mapData[i][j][k][l] = buffer[c++];
 						}
-						// Robot creation logic will be added here
+						// PACKIT.C: center feature type 6 marks static map objects.
+						if (k == 4 && _mapData[i][j][4][0] == 6 && i < 31 && j < 31) {
+							Thing obj;
+							memset(&obj, 0, sizeof(obj));
+							obj.alive = 1;
+							obj.visible = 0;
+							obj.type = _mapData[i][j][4][1] + BASEOBJECT;
+							obj.where.xloc = (i << 8) + 128;
+							obj.where.yloc = (j << 8) + 128;
+							obj.where.xindex = i;
+							obj.where.yindex = j;
+							obj.where.ang = (uint8)(_mapData[i][j][4][2] + 32);
+							obj.where.look = obj.where.ang;
+							_objects.push_back(obj);
+							const int objNum = (int)_objects.size(); // 1-based, DOS-style robot slots
+							if (objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
+								_robotArray[i][j] = (uint8)objNum;
+						}
 					} else {
 						_mapData[i][j][k][0] = 0;
 					}
@@ -167,7 +190,7 @@ void ColonyEngine::loadMap(int mnum) {
 	}
 	free(buffer);
 	_level = mnum;
-	debug("Successfully loaded map %d", mnum);
+	debug("Successfully loaded map %d (static objects: %d)", mnum, (int)_objects.size());
 }
 
 void ColonyEngine::initTrig() {
@@ -478,6 +501,12 @@ void ColonyEngine::corridor() {
 			right2 = _drX[xFrontRight][yFrontRight];
 		else
 			right2 = MIN(right, right2);
+		if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
+			if (_robotArray[cellx][celly])
+				setRobot(left2, right2, _robotArray[cellx][celly]);
+			if (_foodArray[cellx][celly])
+				setRobot(left2, right2, _foodArray[cellx][celly]);
+		}
 		if (wallAt(cellx, celly) & ~0x03)
 			features(cellx, celly, xFrontLeft, yFrontLeft, left2, right2, rox, roy);
 
@@ -631,6 +660,12 @@ void ColonyEngine::checkleft(int xs, int ys, int xf, int yf, int left, int right
 					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
 					if (wallAt(cellx, celly) & ~0x03)
 						features(cellx, celly, xf2 + _frntx, yf2 + _frnty, left, right, rx, ry);
+					if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
+						if (_robotArray[cellx][celly])
+							setRobot(left, right, _robotArray[cellx][celly]);
+						if (_foodArray[cellx][celly])
+							setRobot(left, right, _foodArray[cellx][celly]);
+					}
 				} else {
 					j = 0;
 					xfstart = xf2;
@@ -693,6 +728,14 @@ void ColonyEngine::checkleft(int xs, int ys, int xf, int yf, int left, int right
 					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
 					if (wallAt(cellx - _frntx, celly - _frnty) & ~0x03)
 						features(cellx - _frntx, celly - _frnty, xf2, yf2, left, right, rx, ry);
+					const int objxL = cellx - _frntx;
+					const int objyL = celly - _frnty;
+					if (objxL >= 0 && objxL < 32 && objyL >= 0 && objyL < 32) {
+						if (_robotArray[objxL][objyL])
+							setRobot(left, right, _robotArray[objxL][objyL]);
+						if (_foodArray[objxL][objyL])
+							setRobot(left, right, _foodArray[objxL][objyL]);
+					}
 					i++;
 					j++;
 				}
@@ -790,6 +833,12 @@ void ColonyEngine::checkright(int xs, int ys, int xf, int yf, int left, int righ
 					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
 					if (wallAt(cellx, celly) & ~0x03)
 						features(cellx, celly, xf + _frntx, yf + _frnty, left, right, rx - _tsin, ry - _tcos);
+					if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
+						if (_robotArray[cellx][celly])
+							setRobot(left, right, _robotArray[cellx][celly]);
+						if (_foodArray[cellx][celly])
+							setRobot(left, right, _foodArray[cellx][celly]);
+					}
 				} else {
 					j = 0;
 					xfstart = xf2;
@@ -852,6 +901,14 @@ void ColonyEngine::checkright(int xs, int ys, int xf, int yf, int left, int righ
 					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
 					if (wallAt(cellx - _frntx, celly - _frnty) & ~0x03)
 						features(cellx - _frntx, celly - _frnty, xf, yf, left, right, rx - _tsin, ry - _tcos);
+					const int objxR = cellx - _frntx;
+					const int objyR = celly - _frnty;
+					if (objxR >= 0 && objxR < 32 && objyR >= 0 && objyR < 32) {
+						if (_robotArray[objxR][objyR])
+							setRobot(left, right, _robotArray[objxR][objyR]);
+						if (_foodArray[objxR][objyR])
+							setRobot(left, right, _foodArray[objxR][objyR]);
+					}
 					i++;
 					j++;
 				}
@@ -1441,6 +1498,123 @@ void ColonyEngine::split7x7(int left[4], int right[4], int lr[7], int ud[7][7])
 		split7(ud[i], lud[i], rud[i]);
 }
 
+bool ColonyEngine::projectWorld(int worldX, int worldY, int &screenX, int &depth) const {
+	long x = worldX - _me.xloc;
+	long y = worldY - _me.yloc;
+	long tsin = _cost[_me.look];
+	long tcos = _sint[_me.look];
+	long xx = (x * tcos - y * tsin) >> 7;
+	long yy = (x * tsin + y * tcos) >> 7;
+
+	if (yy <= 16)
+		return false;
+	if (yy >= 11585)
+		yy = 11584;
+
+	screenX = _centerX + (int)((xx << 8) / yy);
+	depth = (int)yy;
+	return true;
+}
+
+uint32 ColonyEngine::objectColor(int type) const {
+	switch (type) {
+	case 21: // DESK
+		return 220;
+	case 22: // PLANT
+		return 100;
+	case 24: // BED
+	case 42: // BBED
+		return 180;
+	case 29: // SCREEN
+	case 30: // CONSOLE
+		return 240;
+	case 31: // POWERSUIT
+	case 46: // REACTOR
+		return 255;
+	case 36: // TELEPORT
+		return 140;
+	default:
+		return 160 + ((uint32)(type * 7) & 0x3F);
+	}
+}
+
+void ColonyEngine::drawStaticObjects() {
+	struct DrawCmd {
+		int depth;
+		int index;
+	};
+
+	Common::Array<DrawCmd> drawList;
+	drawList.reserve(_objects.size());
+
+	for (uint i = 0; i < _objects.size(); i++) {
+		const Thing &obj = _objects[i];
+		if (!obj.alive || !obj.visible)
+			continue;
+		int sx, depth;
+		if (!projectWorld(obj.where.xloc, obj.where.yloc, sx, depth))
+			continue;
+		if (depth > 11000)
+			continue;
+		DrawCmd cmd;
+		cmd.depth = depth;
+		cmd.index = (int)i;
+		drawList.push_back(cmd);
+	}
+
+	Common::sort(drawList.begin(), drawList.end(), [](const DrawCmd &a, const DrawCmd &b) {
+		return a.depth > b.depth; // far to near
+	});
+
+	for (uint i = 0; i < drawList.size(); i++) {
+		const DrawCmd &d = drawList[i];
+		const Thing &obj = _objects[d.index];
+		int sx, depth;
+		if (!projectWorld(obj.where.xloc, obj.where.yloc, sx, depth))
+			continue;
+		int scale = _rtable[d.depth];
+		int baseY = _height - (_centerY - scale);
+		int h = CLIP<int>(scale, 4, 96);
+		int w = CLIP<int>(h >> 1, 3, 64);
+		Common::Rect body(sx - w, baseY - h, sx + w, baseY);
+		const int bodyLeft = (int)body.left;
+		const int bodyTop = (int)body.top;
+		const int bodyRight = (int)body.right;
+		const int bodyBottom = (int)body.bottom;
+		const int clipLeft = MAX(bodyLeft, MAX((int)obj.clip.left, (int)_screenR.left));
+		const int clipTop = MAX(bodyTop, MAX((int)obj.clip.top, (int)_screenR.top));
+		const int clipRight = MIN(bodyRight, MIN((int)obj.clip.right, (int)_screenR.right));
+		const int clipBottom = MIN(bodyBottom, MIN((int)obj.clip.bottom, (int)_screenR.bottom));
+		if (clipLeft >= clipRight || clipTop >= clipBottom)
+			continue;
+		Common::Rect clipped(clipLeft, clipTop, clipRight, clipBottom);
+
+		uint32 color = objectColor(obj.type);
+		_gfx->drawRect(clipped, color);
+		_gfx->drawLine(clipped.left, clipped.bottom - 1, clipped.right - 1, clipped.bottom - 1, color);
+	}
+}
+
+void ColonyEngine::setRobot(int l, int r, int num) {
+	if (num <= 0 || num > (int)_objects.size())
+		return;
+	if (l < _screenR.left)
+		l = _screenR.left;
+	if (r > _screenR.right)
+		r = _screenR.right;
+	if (l >= r)
+		return;
+
+	Thing &obj = _objects[num - 1];
+	if (!obj.alive)
+		return;
+	obj.visible = 1;
+	obj.clip.left = l + 1;
+	obj.clip.right = r - 2;
+	obj.clip.top = _clip.top;
+	obj.clip.bottom = _clip.bottom;
+}
+
 Common::Error ColonyEngine::run() {
 	Graphics::PixelFormat format8bpp = Graphics::PixelFormat::createFormatCLUT8();
 	initGraphics(_width, _height, &format8bpp);
@@ -1515,8 +1689,11 @@ Common::Error ColonyEngine::run() {
 		_system->warpMouse(_width / 2, _height / 2);
 
 		_gfx->clear(_gfx->black());
+		for (uint i = 0; i < _objects.size(); i++)
+			_objects[i].visible = 0;
 		
 		corridor();
+		drawStaticObjects();
 		
 		_gfx->copyToScreen();
 		_system->delayMillis(10);
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 2d2d32739c5..fe4171cfcf4 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -143,6 +143,10 @@ private:
 	void drawColor(const uint8 *map, int left[4], int right[4]);
 	void split7(int arr[7], int x1, int x2) const;
 	void split7x7(int left[4], int right[4], int lr[7], int ud[7][7]) const;
+	void drawStaticObjects();
+	bool projectWorld(int worldX, int worldY, int &screenX, int &depth) const;
+	uint32 objectColor(int type) const;
+	void setRobot(int l, int r, int num);
 };
 
 } // End of namespace Colony


Commit: e089f5bc0577c1b48da38efb120c5ae376630244
    https://github.com/scummvm/scummvm/commit/e089f5bc0577c1b48da38efb120c5ae376630244
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:02+02:00

Commit Message:
COLONY: start to implement UI

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 14b54756e23..56d20fd746e 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -105,11 +105,19 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_flip = false;
 	_mouseSensitivity = 1;
 	_change = true;
+	_showDashBoard = true;
 	
 	memset(_wall, 0, sizeof(_wall));
 	memset(_mapData, 0, sizeof(_mapData));
 	memset(_robotArray, 0, sizeof(_robotArray));
 	memset(_foodArray, 0, sizeof(_foodArray));
+
+	_screenR = Common::Rect(0, 0, _width, _height);
+	_clip = _screenR;
+	_dashBoardRect = Common::Rect(0, 0, 0, 0);
+	_compassRect = Common::Rect(0, 0, 0, 0);
+	_headsUpRect = Common::Rect(0, 0, 0, 0);
+	_powerRect = Common::Rect(0, 0, 0, 0);
 	
 	_me.xindex = 10;
 	_me.yindex = 10;
@@ -1615,16 +1623,102 @@ void ColonyEngine::setRobot(int l, int r, int num) {
 	obj.clip.bottom = _clip.bottom;
 }
 
+void ColonyEngine::updateViewportLayout() {
+	auto makeSafeRect = [](int left, int top, int right, int bottom) {
+		if (right < left)
+			right = left;
+		if (bottom < top)
+			bottom = top;
+		return Common::Rect(left, top, right, bottom);
+	};
+
+	int dashWidth = 0;
+	if (_showDashBoard) {
+		dashWidth = CLIP<int>(_width / 6, 72, 140);
+		if (_width - dashWidth < 160)
+			dashWidth = 0;
+	}
+
+	_screenR = makeSafeRect(dashWidth, 0, _width, _height);
+	_clip = _screenR;
+	_centerX = (_screenR.left + _screenR.right) >> 1;
+	_centerY = (_screenR.top + _screenR.bottom) >> 1;
+
+	_dashBoardRect = makeSafeRect(0, 0, dashWidth, _height);
+	if (dashWidth == 0) {
+		_compassRect = Common::Rect(0, 0, 0, 0);
+		_headsUpRect = Common::Rect(0, 0, 0, 0);
+		_powerRect = Common::Rect(0, 0, 0, 0);
+		return;
+	}
+
+	const int pad = 2;
+	const int unit = MAX(8, (dashWidth - (pad * 2)) / 4);
+	const int blockLeft = pad;
+	const int blockRight = MIN(dashWidth - pad, blockLeft + unit * 4);
+
+	const int compassBottom = _height - MAX(2, unit / 4);
+	const int compassTop = MAX(pad, compassBottom - unit * 4);
+	_compassRect = makeSafeRect(blockLeft, compassTop, blockRight, compassBottom);
+
+	const int headsUpBottom = _compassRect.top - MAX(2, unit / 4) - 4;
+	const int headsUpTop = headsUpBottom - unit * 4;
+	_headsUpRect = makeSafeRect(blockLeft, MAX(pad, headsUpTop), blockRight, MAX(pad, headsUpBottom));
+
+	_powerRect = makeSafeRect(blockLeft, pad, blockRight, _headsUpRect.top - 4);
+}
+
+void ColonyEngine::drawDashboardStep1() {
+	if (_dashBoardRect.width() <= 0 || _dashBoardRect.height() <= 0)
+		return;
+
+	const uint32 panelBg = 24;
+	const uint32 frame = 190;
+	const uint32 accent = 220;
+	const uint32 mark = 255;
+
+	_gfx->fillRect(_dashBoardRect, panelBg);
+	_gfx->drawRect(_dashBoardRect, frame);
+	if (_screenR.left > 0)
+		_gfx->drawLine(_screenR.left - 1, _screenR.top, _screenR.left - 1, _screenR.bottom - 1, mark);
+
+	if (_compassRect.width() > 4 && _compassRect.height() > 4) {
+		_gfx->drawRect(_compassRect, frame);
+		const int cx = (_compassRect.left + _compassRect.right) >> 1;
+		const int cy = (_compassRect.top + _compassRect.bottom) >> 1;
+		const int rx = MAX(2, (_compassRect.width() - 6) >> 1);
+		const int ry = MAX(2, (_compassRect.height() - 6) >> 1);
+		const int ex = cx + ((_cost[_me.look] * rx) >> 8);
+		const int ey = cy - ((_sint[_me.look] * ry) >> 8);
+		_gfx->drawLine(cx, cy, ex, ey, mark);
+		_gfx->drawLine(cx - 2, cy, cx + 2, cy, accent);
+		_gfx->drawLine(cx, cy - 2, cx, cy + 2, accent);
+	}
+
+	if (_headsUpRect.width() > 4 && _headsUpRect.height() > 4) {
+		_gfx->drawRect(_headsUpRect, frame);
+		const int cx = (_headsUpRect.left + _headsUpRect.right) >> 1;
+		const int cy = (_headsUpRect.top + _headsUpRect.bottom) >> 1;
+		const int nx = cx + ((_cost[_me.look] * 6) >> 8);
+		const int ny = cy - ((_sint[_me.look] * 6) >> 8);
+		_gfx->drawLine(cx, cy, nx, ny, mark);
+		_gfx->drawRect(Common::Rect(cx - 2, cy - 2, cx + 2, cy + 2), accent);
+	}
+
+	if (_powerRect.width() > 4 && _powerRect.height() > 4) {
+		_gfx->drawRect(_powerRect, frame);
+		const int barY = _powerRect.bottom - MAX(3, _powerRect.width() / 8);
+		_gfx->drawLine(_powerRect.left + 1, barY, _powerRect.right - 2, barY, accent);
+	}
+}
+
 Common::Error ColonyEngine::run() {
 	Graphics::PixelFormat format8bpp = Graphics::PixelFormat::createFormatCLUT8();
 	initGraphics(_width, _height, &format8bpp);
 
 	_width = _system->getWidth();
 	_height = _system->getHeight();
-	_centerX = _width / 2;
-	_centerY = _height / 2;
-	_screenR = Common::Rect(0, 0, _width, _height);
-	_clip = _screenR;
+	updateViewportLayout();
 	const Graphics::PixelFormat format = _system->getScreenFormat();
 	debug("Screen format: %d bytesPerPixel. Actual size: %dx%d", format.bytesPerPixel, _width, _height);
 
@@ -1688,14 +1782,15 @@ Common::Error ColonyEngine::run() {
 		}
 		_system->warpMouse(_width / 2, _height / 2);
 
-		_gfx->clear(_gfx->black());
-		for (uint i = 0; i < _objects.size(); i++)
-			_objects[i].visible = 0;
-		
-		corridor();
-		drawStaticObjects();
-		
-		_gfx->copyToScreen();
+			_gfx->clear(_gfx->black());
+			for (uint i = 0; i < _objects.size(); i++)
+				_objects[i].visible = 0;
+			
+			corridor();
+			drawStaticObjects();
+			drawDashboardStep1();
+			
+			_gfx->copyToScreen();
 		_system->delayMillis(10);
 	}
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index fe4171cfcf4..4fff809b8e6 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -105,6 +105,7 @@ private:
 	bool _flip;
 	int _mouseSensitivity;
 	bool _change;
+	bool _showDashBoard;
 
 	int _frntxWall, _frntyWall;
 	int _sidexWall, _sideyWall;
@@ -118,6 +119,10 @@ private:
 
 	Common::Rect _clip;
 	Common::Rect _screenR;
+	Common::Rect _dashBoardRect;
+	Common::Rect _compassRect;
+	Common::Rect _headsUpRect;
+	Common::Rect _powerRect;
 
 	void drawend(int xstart, int ystart, int xFrontLeft, int yFrontLeft);
 	void checkleft(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len);
@@ -147,6 +152,8 @@ private:
 	bool projectWorld(int worldX, int worldY, int &screenX, int &depth) const;
 	uint32 objectColor(int type) const;
 	void setRobot(int l, int r, int num);
+	void updateViewportLayout();
+	void drawDashboardStep1();
 };
 
 } // End of namespace Colony


Commit: 55f39fbc2f943389089b99911d7237e8a56898f4
    https://github.com/scummvm/scummvm/commit/55f39fbc2f943389089b99911d7237e8a56898f4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:02+02:00

Commit Message:
COLONY: implement minimap rendering and HUD layout

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 56d20fd746e..0a09b4792c8 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -1676,6 +1676,8 @@ void ColonyEngine::drawDashboardStep1() {
 	const uint32 frame = 190;
 	const uint32 accent = 220;
 	const uint32 mark = 255;
+	const uint32 miniMapObj = 235;
+	const uint32 miniMapActor = 255;
 
 	_gfx->fillRect(_dashBoardRect, panelBg);
 	_gfx->drawRect(_dashBoardRect, frame);
@@ -1693,16 +1695,97 @@ void ColonyEngine::drawDashboardStep1() {
 		_gfx->drawLine(cx, cy, ex, ey, mark);
 		_gfx->drawLine(cx - 2, cy, cx + 2, cy, accent);
 		_gfx->drawLine(cx, cy - 2, cx, cy + 2, accent);
+		_gfx->drawRect(Common::Rect(_compassRect.left + 2, _compassRect.top + 2, _compassRect.right - 2, _compassRect.bottom - 2), accent);
 	}
 
 	if (_headsUpRect.width() > 4 && _headsUpRect.height() > 4) {
 		_gfx->drawRect(_headsUpRect, frame);
-		const int cx = (_headsUpRect.left + _headsUpRect.right) >> 1;
-		const int cy = (_headsUpRect.top + _headsUpRect.bottom) >> 1;
-		const int nx = cx + ((_cost[_me.look] * 6) >> 8);
-		const int ny = cy - ((_sint[_me.look] * 6) >> 8);
-		_gfx->drawLine(cx, cy, nx, ny, mark);
-		_gfx->drawRect(Common::Rect(cx - 2, cy - 2, cx + 2, cy + 2), accent);
+
+		const int lExtBase = _dashBoardRect.width() >> 1;
+		int lExt = lExtBase + (lExtBase >> 1);
+		if (lExt & 1)
+			lExt--;
+		const int sExt = lExt >> 1;
+		const int xloc = (lExt * ((_me.xindex << 8) - _me.xloc)) >> 8;
+		const int yloc = (lExt * ((_me.yindex << 8) - _me.yloc)) >> 8;
+		const int ccenterx = (_headsUpRect.left + _headsUpRect.right) >> 1;
+		const int ccentery = (_headsUpRect.top + _headsUpRect.bottom) >> 1;
+		const int tsin = _sint[_me.look];
+		const int tcos = _cost[_me.look];
+
+		int xcorner[6];
+		int ycorner[6];
+		xcorner[0] = ccenterx + (((long)xloc * tsin - (long)yloc * tcos) >> 8);
+		ycorner[0] = ccentery - (((long)yloc * tsin + (long)xloc * tcos) >> 8);
+		xcorner[1] = ccenterx + (((long)(xloc + lExt) * tsin - (long)yloc * tcos) >> 8);
+		ycorner[1] = ccentery - (((long)yloc * tsin + (long)(xloc + lExt) * tcos) >> 8);
+		xcorner[2] = ccenterx + (((long)(xloc + lExt) * tsin - (long)(yloc + lExt) * tcos) >> 8);
+		ycorner[2] = ccentery - (((long)(yloc + lExt) * tsin + (long)(xloc + lExt) * tcos) >> 8);
+		xcorner[3] = ccenterx + (((long)xloc * tsin - (long)(yloc + lExt) * tcos) >> 8);
+		ycorner[3] = ccentery - (((long)(yloc + lExt) * tsin + (long)xloc * tcos) >> 8);
+		xcorner[4] = ccenterx + (((long)(xloc + sExt) * tsin - (long)(yloc + sExt) * tcos) >> 8);
+		ycorner[4] = ccentery - (((long)(yloc + sExt) * tsin + (long)(xloc + sExt) * tcos) >> 8);
+		xcorner[5] = ccenterx + (((long)(xloc + sExt) * tsin - (long)yloc * tcos) >> 8);
+		ycorner[5] = ccentery - (((long)yloc * tsin + (long)(xloc + sExt) * tcos) >> 8);
+
+		const int dx = xcorner[1] - xcorner[0];
+		const int dy = ycorner[0] - ycorner[1];
+		_gfx->drawLine(xcorner[0] - dx, ycorner[0] + dy, xcorner[1] + dx, ycorner[1] - dy, accent);
+		_gfx->drawLine(xcorner[1] + dy, ycorner[1] + dx, xcorner[2] - dy, ycorner[2] - dx, accent);
+		_gfx->drawLine(xcorner[2] + dx, ycorner[2] - dy, xcorner[3] - dx, ycorner[3] + dy, accent);
+		_gfx->drawLine(xcorner[3] - dy, ycorner[3] - dx, xcorner[0] + dy, ycorner[0] + dx, accent);
+
+		auto drawMarker = [&](int x, int y, int halfSize, uint32 color) {
+			const int l = MAX<int>(_headsUpRect.left + 1, x - halfSize);
+			const int t = MAX<int>(_headsUpRect.top + 1, y - halfSize);
+			const int r = MIN<int>(_headsUpRect.right - 1, x + halfSize + 1);
+			const int b = MIN<int>(_headsUpRect.bottom - 1, y + halfSize + 1);
+			if (l >= r || t >= b)
+				return;
+			_gfx->drawRect(Common::Rect(l, t, r, b), color);
+		};
+
+		auto hasRobotAt = [&](int x, int y) -> bool {
+			if (x < 0 || x >= 32 || y < 0 || y >= 32)
+				return false;
+			return _robotArray[x][y] != 0;
+		};
+		auto hasFoodAt = [&](int x, int y) -> bool {
+			if (x < 0 || x >= 32 || y < 0 || y >= 32)
+				return false;
+			return _foodArray[x][y] != 0;
+		};
+
+		if (hasFoodAt(_me.xindex, _me.yindex))
+			drawMarker(xcorner[4], ycorner[4], 1, miniMapObj);
+
+		if (_me.yindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x01)) {
+			if (hasFoodAt(_me.xindex, _me.yindex - 1))
+				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 1, miniMapObj);
+			if (hasRobotAt(_me.xindex, _me.yindex - 1))
+				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 2, miniMapActor);
+		}
+		if (_me.xindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x02)) {
+			if (hasFoodAt(_me.xindex - 1, _me.yindex))
+				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 1, miniMapObj);
+			if (hasRobotAt(_me.xindex - 1, _me.yindex))
+				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 2, miniMapActor);
+		}
+		if (_me.yindex < 30 && !(_wall[_me.xindex][_me.yindex + 1] & 0x01)) {
+			if (hasFoodAt(_me.xindex, _me.yindex + 1))
+				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 1, miniMapObj);
+			if (hasRobotAt(_me.xindex, _me.yindex + 1))
+				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 2, miniMapActor);
+		}
+		if (_me.xindex < 30 && !(_wall[_me.xindex + 1][_me.yindex] & 0x02)) {
+			if (hasFoodAt(_me.xindex + 1, _me.yindex))
+				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 1, miniMapObj);
+			if (hasRobotAt(_me.xindex + 1, _me.yindex))
+				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 2, miniMapActor);
+		}
+
+		drawMarker(ccenterx, ccentery, 2, mark);
+		drawMarker(ccenterx, ccentery, 1, accent);
 	}
 
 	if (_powerRect.width() > 4 && _powerRect.height() > 4) {
@@ -1735,8 +1818,8 @@ Common::Error ColonyEngine::run() {
 	
 	loadMap(1); // Try to load the first map
 	
-	_system->lockMouse(true);
-	_system->warpMouse(_width / 2, _height / 2);
+		_system->lockMouse(true);
+		_system->warpMouse(_centerX, _centerY);
 
 	// Temporary infinite loop to prevent ScummVM from closing immediately
 	while (!shouldQuit()) {
@@ -1763,13 +1846,19 @@ Common::Error ColonyEngine::run() {
 					_me.look = (uint8)((int)_me.look + 8);
 					_change = true;
 					break;
-				case Common::KEYCODE_RIGHT:
-					_me.look = (uint8)((int)_me.look - 8);
-					_change = true;
-					break;
-				default:
-					break;
-				}
+					case Common::KEYCODE_RIGHT:
+						_me.look = (uint8)((int)_me.look - 8);
+						_change = true;
+						break;
+					case Common::KEYCODE_F7:
+						_showDashBoard = !_showDashBoard;
+						updateViewportLayout();
+						_system->warpMouse(_centerX, _centerY);
+						_change = true;
+						break;
+					default:
+						break;
+					}
 				debug("Me: x=%d y=%d look=%d", _me.xloc, _me.yloc, _me.look);
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
 				if (event.relMouse.x != 0) {
@@ -1780,7 +1869,7 @@ Common::Error ColonyEngine::run() {
 				}
 			}
 		}
-		_system->warpMouse(_width / 2, _height / 2);
+			_system->warpMouse(_centerX, _centerY);
 
 			_gfx->clear(_gfx->black());
 			for (uint i = 0; i < _objects.size(); i++)


Commit: f50db5ff2fea9180a0f82dfc42888db2ebf020af
    https://github.com/scummvm/scummvm/commit/f50db5ff2fea9180a0f82dfc42888db2ebf020af
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:03+02:00

Commit Message:
COLONY: add compass ellipse drawing and filter minimap objects

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 0a09b4792c8..0603713a992 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -31,6 +31,7 @@
 #include "engines/util.h"
 #include "graphics/palette.h"
 #include "graphics/paletteman.h"
+#include <math.h>
 
 namespace Colony {
 
@@ -1672,6 +1673,7 @@ void ColonyEngine::drawDashboardStep1() {
 	if (_dashBoardRect.width() <= 0 || _dashBoardRect.height() <= 0)
 		return;
 
+	const int kFWALLType = 48;
 	const uint32 panelBg = 24;
 	const uint32 frame = 190;
 	const uint32 accent = 220;
@@ -1685,17 +1687,33 @@ void ColonyEngine::drawDashboardStep1() {
 		_gfx->drawLine(_screenR.left - 1, _screenR.top, _screenR.left - 1, _screenR.bottom - 1, mark);
 
 	if (_compassRect.width() > 4 && _compassRect.height() > 4) {
-		_gfx->drawRect(_compassRect, frame);
 		const int cx = (_compassRect.left + _compassRect.right) >> 1;
 		const int cy = (_compassRect.top + _compassRect.bottom) >> 1;
 		const int rx = MAX(2, (_compassRect.width() - 6) >> 1);
 		const int ry = MAX(2, (_compassRect.height() - 6) >> 1);
+		const int irx = MAX(1, rx - 2);
+		const int iry = MAX(1, ry - 2);
+		auto drawEllipse = [&](int erx, int ery, uint32 color) {
+			const int segments = 48;
+			int px = cx + erx;
+			int py = cy;
+			for (int i = 1; i <= segments; i++) {
+				const double t = (6.28318530717958647692 * (double)i) / (double)segments;
+				const int x = cx + (int)((double)erx * cos(t));
+				const int y = cy + (int)((double)ery * sin(t));
+				_gfx->drawLine(px, py, x, y, color);
+				px = x;
+				py = y;
+			}
+		};
+		drawEllipse(rx, ry, frame);
+		drawEllipse(irx, iry, accent);
+
 		const int ex = cx + ((_cost[_me.look] * rx) >> 8);
 		const int ey = cy - ((_sint[_me.look] * ry) >> 8);
 		_gfx->drawLine(cx, cy, ex, ey, mark);
 		_gfx->drawLine(cx - 2, cy, cx + 2, cy, accent);
 		_gfx->drawLine(cx, cy - 2, cx, cy + 2, accent);
-		_gfx->drawRect(Common::Rect(_compassRect.left + 2, _compassRect.top + 2, _compassRect.right - 2, _compassRect.bottom - 2), accent);
 	}
 
 	if (_headsUpRect.width() > 4 && _headsUpRect.height() > 4) {
@@ -1753,7 +1771,12 @@ void ColonyEngine::drawDashboardStep1() {
 		auto hasFoodAt = [&](int x, int y) -> bool {
 			if (x < 0 || x >= 32 || y < 0 || y >= 32)
 				return false;
-			return _foodArray[x][y] != 0;
+			const uint8 num = _foodArray[x][y];
+			if (num == 0)
+				return false;
+			if (num <= _objects.size())
+				return _objects[num - 1].type < kFWALLType;
+			return true;
 		};
 
 		if (hasFoodAt(_me.xindex, _me.yindex))


Commit: a4160d479c34bbb81797ac7ad0b991c02f1c0ae2
    https://github.com/scummvm/scummvm/commit/a4160d479c34bbb81797ac7ad0b991c02f1c0ae2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:03+02:00

Commit Message:
COLONY: add 3D prism object rendering system

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 0603713a992..fd1f88fa6bf 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -95,6 +95,21 @@ enum WallFeatureType {
 static const int g_dirRight[4] = {1, 3, 0, 2};
 static const int g_dirLeft[4] = {2, 0, 3, 1};
 
+enum ObjectType {
+	kObjDesk = 21,
+	kObjBed = 24,
+	kObjTable = 25,
+	kObjScreen = 29,
+	kObjBBed = 42
+};
+
+struct ColonyEngine::PrismPartDef {
+	int pointCount;
+	const int (*points)[3];
+	int surfaceCount;
+	const int (*surfaces)[8];
+};
+
 ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd) {
 	_level = 0;
 	_robotNum = 0;
@@ -1547,10 +1562,485 @@ uint32 ColonyEngine::objectColor(int type) const {
 	}
 }
 
+bool ColonyEngine::isSurfaceClockwise(const ProjectedPrismPart &part, const int surface[8]) const {
+	const int n = surface[1];
+	for (int i = 2; i < n; i++) {
+		const int ia = surface[i];
+		const int ib = surface[i + 1];
+		const int ic = surface[i + 2];
+		if (ia < 0 || ia >= part.pointCount || ib < 0 || ib >= part.pointCount || ic < 0 || ic >= part.pointCount)
+			continue;
+		const long dx = part.x[ia] - part.x[ib];
+		const long dy = part.y[ia] - part.y[ib];
+		const long dxp = part.x[ic] - part.x[ib];
+		const long dyp = part.y[ic] - part.y[ib];
+		if (dx < 0) {
+			if (dy == 0) {
+				if (dyp > 0)
+					return false;
+				if (dyp < 0)
+					return true;
+			} else {
+				const long b = dy * dxp - dx * dyp;
+				if (b > 0)
+					return false;
+				if (b < 0)
+					return true;
+			}
+		} else if (dx > 0) {
+			if (dy == 0) {
+				if (dyp < 0)
+					return false;
+				if (dyp > 0)
+					return true;
+			} else {
+				const long b = dx * dyp - dy * dxp;
+				if (b < 0)
+					return false;
+				if (b > 0)
+					return true;
+			}
+		} else {
+			if (dy < 0) {
+				if (dxp > 0)
+					return true;
+				if (dxp < 0)
+					return false;
+			}
+			if (dy > 0) {
+				if (dxp < 0)
+					return true;
+				if (dxp > 0)
+					return false;
+			}
+		}
+	}
+	return false;
+}
+
+bool ColonyEngine::projectPrismPart(const Thing &obj, const PrismPartDef &part, bool useLook, ProjectedPrismPart &out) const {
+	out.pointCount = CLIP<int>(part.pointCount, 0, ProjectedPrismPart::kMaxPoints);
+	for (int i = 0; i < ProjectedPrismPart::kMaxSurfaces; i++)
+		out.vsurface[i] = false;
+	out.visible = false;
+	if (out.pointCount <= 0 || !part.points || !part.surfaces)
+		return false;
+
+	const uint8 ang = useLook ? obj.where.look : obj.where.ang;
+	const long rotCos = _cost[ang];
+	const long rotSin = _sint[ang];
+	const long viewSin = _cost[_me.look];
+	const long viewCos = _sint[_me.look];
+
+	int minX = 32000;
+	int maxX = -32000;
+	int minY = 32000;
+	int maxY = -32000;
+	// DOS InitObj() applies: Robot[i][j].pnt[k][2] -= Floor, with Floor == 160.
+	// We keep source geometry unmodified and apply the same offset at projection time.
+	static const int kFloorShift = 160;
+	for (int i = 0; i < out.pointCount; i++) {
+		const int px = part.points[i][0];
+		const int py = part.points[i][1];
+		const int pz = part.points[i][2];
+		const long rx = ((long)px * rotCos - (long)py * rotSin) >> 7;
+		const long ry = ((long)px * rotSin + (long)py * rotCos) >> 7;
+		const long worldX = rx + obj.where.xloc;
+		const long worldY = ry + obj.where.yloc;
+
+		const long tx = worldX - _me.xloc;
+		const long ty = worldY - _me.yloc;
+		const long xx = (tx * viewCos - ty * viewSin) >> 7;
+		long yy = (tx * viewSin + ty * viewCos) >> 7;
+		if (yy <= 16)
+			yy = 16;
+
+		out.x[i] = _centerX + (int)((xx << 8) / yy);
+		out.depth[i] = (int)yy;
+		const long zrel = (long)pz - kFloorShift;
+		out.y[i] = _centerY - (int)((zrel << 8) / yy);
+		minX = MIN(minX, out.x[i]);
+		maxX = MAX(maxX, out.x[i]);
+		minY = MIN(minY, out.y[i]);
+		maxY = MAX(maxY, out.y[i]);
+	}
+
+	out.visible = !(maxX < _screenR.left || minX >= _screenR.right || maxY < _screenR.top || minY >= _screenR.bottom);
+	if (!out.visible)
+		return false;
+
+	const int surfCount = CLIP<int>(part.surfaceCount, 0, ProjectedPrismPart::kMaxSurfaces);
+	for (int i = 0; i < surfCount; i++)
+		out.vsurface[i] = isSurfaceClockwise(out, part.surfaces[i]);
+	return true;
+}
+
+bool ColonyEngine::clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const {
+	if (clip.left >= clip.right || clip.top >= clip.bottom)
+		return false;
+	const int l = clip.left;
+	const int r = clip.right - 1;
+	const int t = clip.top;
+	const int b = clip.bottom - 1;
+	auto outCode = [&](int x, int y) {
+		int code = 0;
+		if (x < l)
+			code |= 1;
+		else if (x > r)
+			code |= 2;
+		if (y < t)
+			code |= 4;
+		else if (y > b)
+			code |= 8;
+		return code;
+	};
+
+	int c1 = outCode(x1, y1);
+	int c2 = outCode(x2, y2);
+	while (true) {
+		if ((c1 | c2) == 0)
+			return true;
+		if (c1 & c2)
+			return false;
+
+		const int cOut = c1 ? c1 : c2;
+		int x = 0;
+		int y = 0;
+		if (cOut & 8) {
+			if (y2 == y1)
+				return false;
+			x = x1 + (x2 - x1) * (b - y1) / (y2 - y1);
+			y = b;
+		} else if (cOut & 4) {
+			if (y2 == y1)
+				return false;
+			x = x1 + (x2 - x1) * (t - y1) / (y2 - y1);
+			y = t;
+		} else if (cOut & 2) {
+			if (x2 == x1)
+				return false;
+			y = y1 + (y2 - y1) * (r - x1) / (x2 - x1);
+			x = r;
+		} else {
+			if (x2 == x1)
+				return false;
+			y = y1 + (y2 - y1) * (l - x1) / (x2 - x1);
+			x = l;
+		}
+
+		if (cOut == c1) {
+			x1 = x;
+			y1 = y;
+			c1 = outCode(x1, y1);
+		} else {
+			x2 = x;
+			y2 = y;
+			c2 = outCode(x2, y2);
+		}
+	}
+}
+
+void ColonyEngine::drawProjectedPrism(const ProjectedPrismPart &part, const PrismPartDef &def, int force, uint32 color, const Common::Rect &clip) {
+	const int surfCount = CLIP<int>(def.surfaceCount, 0, ProjectedPrismPart::kMaxSurfaces);
+	for (int i = 0; i < surfCount; i++) {
+		if (!(part.vsurface[i] || force))
+			continue;
+		const int n = def.surfaces[i][1];
+		if (n < 2)
+			continue;
+		int first = def.surfaces[i][2];
+		if (first < 0 || first >= part.pointCount)
+			continue;
+		int prev = first;
+		for (int j = 1; j < n; j++) {
+			const int cur = def.surfaces[i][j + 2];
+			if (cur < 0 || cur >= part.pointCount)
+				continue;
+			int x1 = part.x[prev];
+			int y1 = part.y[prev];
+			int x2 = part.x[cur];
+			int y2 = part.y[cur];
+			if (clipLineToRect(x1, y1, x2, y2, clip))
+				_gfx->drawLine(x1, y1, x2, y2, color);
+			prev = cur;
+		}
+		int x1 = part.x[prev];
+		int y1 = part.y[prev];
+		int x2 = part.x[first];
+		int y2 = part.y[first];
+		if (clipLineToRect(x1, y1, x2, y2, clip))
+			_gfx->drawLine(x1, y1, x2, y2, color);
+	}
+}
+
+bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
+	// DOS object geometry from SCREEN.H / TABLE.H / BED.H / DESK.H.
+	static const int kScreenPts[8][3] = {
+		{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
+		{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
+	};
+	static const int kScreenSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	};
+
+	static const int kTableTopPts[4][3] = {
+		{-128, 128, 100}, {128, 128, 100}, {128, -128, 100}, {-128, -128, 100}
+	};
+	static const int kTableTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kTableBasePts[8][3] = {
+		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+		{-5, 5, 100}, {5, 5, 100}, {5, -5, 100}, {-5, -5, 100}
+	};
+	static const int kTableBaseSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	};
+
+	static const int kBedPostPts[4][3] = {
+		{-82, 128, 100}, {82, 128, 100}, {82, 128, 0}, {-82, 128, 0}
+	};
+	static const int kBBedPostPts[4][3] = {
+		{-130, 128, 100}, {130, 128, 100}, {130, 128, 0}, {-130, 128, 0}
+	};
+	static const int kBedPostSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kBlanketSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
+	};
+	static const int kSheetSurf[3][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+		{0, 4, 7, 6, 5, 4, 0, 0}
+	};
+	static const int kBedBlanketPts[8][3] = {
+		{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
+		{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
+	};
+	static const int kBedSheetPts[8][3] = {
+		{-80, 128, 30}, {80, 128, 30}, {80, 70, 30}, {-80, 70, 30},
+		{-80, 128, 60}, {80, 128, 60}, {80, 70, 60}, {-80, 70, 60}
+	};
+	static const int kBBedBlanketPts[8][3] = {
+		{-128, 70, 0}, {128, 70, 0}, {128, -175, 0}, {-128, -175, 0},
+		{-128, 70, 60}, {128, 70, 60}, {128, -175, 60}, {-128, -175, 60}
+	};
+	static const int kBBedSheetPts[8][3] = {
+		{-128, 128, 30}, {128, 128, 30}, {128, 70, 30}, {-128, 70, 30},
+		{-128, 128, 60}, {128, 128, 60}, {128, 70, 60}, {-128, 70, 60}
+	};
+
+	static const int kDeskTopPts[4][3] = {
+		{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
+	};
+	static const int kDeskTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kDeskLeftPts[8][3] = {
+		{-135, 95, 0}, {-55, 95, 0}, {-55, -95, 0}, {-135, -95, 0},
+		{-135, 95, 100}, {-55, 95, 100}, {-55, -95, 100}, {-135, -95, 100}
+	};
+	static const int kDeskRightPts[8][3] = {
+		{55, 95, 0}, {135, 95, 0}, {135, -95, 0}, {55, -95, 0},
+		{55, 95, 100}, {135, 95, 100}, {135, -95, 100}, {55, -95, 100}
+	};
+	static const int kDeskCabSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	};
+	static const int kSeatPts[4][3] = {
+		{-40, 210, 60}, {40, 210, 60}, {40, 115, 60}, {-40, 115, 60}
+	};
+	static const int kSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kArmLeftPts[4][3] = {
+		{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
+	};
+	static const int kArmRightPts[4][3] = {
+		{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
+	};
+	static const int kArmSurf[2][8] = {
+		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	};
+	static const int kBackPts[4][3] = {
+		{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
+	};
+	static const int kBackSurf[2][8] = {
+		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	};
+	static const int kComputerPts[8][3] = {
+		{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
+		{70, 25, 120}, {120, 25, 120}, {120, -25, 120}, {70, -25, 120}
+	};
+	static const int kMonitorPts[8][3] = {
+		{75, 20, 120}, {115, 20, 120}, {115, -20, 120}, {75, -20, 120},
+		{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
+	};
+	static const int kComputerSurf[5][8] = {
+		{0, 4, 7, 6, 5, 4, 0, 0}, {0, 4, 0, 3, 7, 4, 0, 0},
+		{0, 4, 3, 2, 6, 7, 0, 0}, {0, 4, 1, 0, 4, 5, 0, 0},
+		{0, 4, 2, 1, 5, 6, 0, 0}
+	};
+	static const int kDeskScreenPts[4][3] = {
+		{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
+	};
+	static const int kDeskScreenSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+
+	static const PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
+	static const PrismPartDef kTableParts[2] = {
+		{4, kTableTopPts, 1, kTableTopSurf},
+		{8, kTableBasePts, 4, kTableBaseSurf}
+	};
+	static const PrismPartDef kBedParts[3] = {
+		{4, kBedPostPts, 1, kBedPostSurf},
+		{8, kBedBlanketPts, 4, kBlanketSurf},
+		{8, kBedSheetPts, 3, kSheetSurf}
+	};
+	static const PrismPartDef kBBedParts[3] = {
+		{4, kBBedPostPts, 1, kBedPostSurf},
+		{8, kBBedBlanketPts, 4, kBlanketSurf},
+		{8, kBBedSheetPts, 3, kSheetSurf}
+	};
+	static const PrismPartDef kDeskParts[10] = {
+		{4, kDeskTopPts, 1, kDeskTopSurf},
+		{8, kDeskLeftPts, 4, kDeskCabSurf},
+		{8, kDeskRightPts, 4, kDeskCabSurf},
+		{4, kSeatPts, 1, kSeatSurf},
+		{4, kArmLeftPts, 2, kArmSurf},
+		{4, kArmRightPts, 2, kArmSurf},
+		{4, kBackPts, 2, kBackSurf},
+		{8, kComputerPts, 5, kComputerSurf},
+		{8, kMonitorPts, 5, kComputerSurf},
+		{4, kDeskScreenPts, 1, kDeskScreenSurf}
+	};
+
+	Common::Rect drawClip(MAX<int>((int)obj.clip.left, (int)_screenR.left),
+	                     MAX<int>((int)obj.clip.top, (int)_screenR.top),
+	                     MIN<int>((int)obj.clip.right, (int)_screenR.right),
+	                     MIN<int>((int)obj.clip.bottom, (int)_screenR.bottom));
+	if (drawClip.left >= drawClip.right || drawClip.top >= drawClip.bottom)
+		return false;
+
+	auto tint = [](uint32 base, int delta) -> uint32 {
+		return (uint32)CLIP<int>((int)base + delta, 0, 255);
+	};
+
+	ProjectedPrismPart p[10];
+	switch (obj.type) {
+	case kObjScreen:
+		if (!projectPrismPart(obj, kScreenPart, false, p[0]) || !p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kScreenPart, 0, tint(baseColor, 0), drawClip);
+		return true;
+	case kObjTable:
+		projectPrismPart(obj, kTableParts[0], false, p[0]);
+		projectPrismPart(obj, kTableParts[1], false, p[1]);
+		if (!p[1].visible)
+			return false;
+		drawProjectedPrism(p[1], kTableParts[1], 0, tint(baseColor, -10), drawClip);
+		drawProjectedPrism(p[0], kTableParts[0], 0, tint(baseColor, 20), drawClip);
+		return true;
+	case kObjBed:
+	case kObjBBed: {
+		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
+		projectPrismPart(obj, parts[0], false, p[0]);
+		projectPrismPart(obj, parts[1], false, p[1]);
+		projectPrismPart(obj, parts[2], false, p[2]);
+		if (!p[1].visible)
+			return false;
+		if (p[0].vsurface[0]) {
+			drawProjectedPrism(p[0], parts[0], 1, tint(baseColor, 15), drawClip);
+			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, 5), drawClip);
+			drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -10), drawClip);
+		} else {
+			drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -10), drawClip);
+			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, 5), drawClip);
+			drawProjectedPrism(p[0], parts[0], 1, tint(baseColor, 15), drawClip);
+		}
+		return true;
+	}
+	case kObjDesk:
+		for (int i = 0; i < 10; i++)
+			projectPrismPart(obj, kDeskParts[i], false, p[i]);
+		if (!p[0].visible)
+			return false;
+		if (p[6].vsurface[1]) {
+			if (p[1].vsurface[3] || p[2].vsurface[3]) {
+				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
+				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
+			} else {
+				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
+				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
+			}
+			drawProjectedPrism(p[0], kDeskParts[0], 0, tint(baseColor, 15), drawClip);
+			drawProjectedPrism(p[7], kDeskParts[7], 0, tint(baseColor, 25), drawClip);
+			drawProjectedPrism(p[8], kDeskParts[8], 0, tint(baseColor, 25), drawClip);
+			if (p[9].vsurface[0])
+				drawProjectedPrism(p[9], kDeskParts[9], 0, tint(baseColor, 40), drawClip);
+			if (p[4].vsurface[0]) {
+				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
+			} else {
+				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
+			}
+		} else {
+			if (p[4].vsurface[0]) {
+				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
+			} else {
+				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
+			}
+			if (p[1].vsurface[3] || p[2].vsurface[3]) {
+				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
+				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
+			} else {
+				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
+				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
+			}
+			drawProjectedPrism(p[0], kDeskParts[0], 0, tint(baseColor, 15), drawClip);
+			drawProjectedPrism(p[7], kDeskParts[7], 0, tint(baseColor, 25), drawClip);
+			drawProjectedPrism(p[8], kDeskParts[8], 0, tint(baseColor, 25), drawClip);
+			if (p[9].vsurface[0])
+				drawProjectedPrism(p[9], kDeskParts[9], 0, tint(baseColor, 40), drawClip);
+		}
+		return true;
+	default:
+		return false;
+	}
+}
+
+void ColonyEngine::drawStaticObjectFallback(const Thing &obj, uint32 color, int depth, int sx) {
+	int scale = _rtable[depth];
+	int baseY = _height - (_centerY - scale);
+	int h = CLIP<int>(scale, 4, 96);
+	int w = CLIP<int>(h >> 1, 3, 64);
+	Common::Rect body(sx - w, baseY - h, sx + w, baseY);
+	const int bodyLeft = (int)body.left;
+	const int bodyTop = (int)body.top;
+	const int bodyRight = (int)body.right;
+	const int bodyBottom = (int)body.bottom;
+	const int clipLeft = MAX(bodyLeft, MAX((int)obj.clip.left, (int)_screenR.left));
+	const int clipTop = MAX(bodyTop, MAX((int)obj.clip.top, (int)_screenR.top));
+	const int clipRight = MIN(bodyRight, MIN((int)obj.clip.right, (int)_screenR.right));
+	const int clipBottom = MIN(bodyBottom, MIN((int)obj.clip.bottom, (int)_screenR.bottom));
+	if (clipLeft >= clipRight || clipTop >= clipBottom)
+		return;
+	Common::Rect clipped(clipLeft, clipTop, clipRight, clipBottom);
+	_gfx->drawRect(clipped, color);
+	_gfx->drawLine(clipped.left, clipped.bottom - 1, clipped.right - 1, clipped.bottom - 1, color);
+}
+
 void ColonyEngine::drawStaticObjects() {
 	struct DrawCmd {
 		int depth;
 		int index;
+		int screenX;
 	};
 
 	Common::Array<DrawCmd> drawList;
@@ -1568,6 +2058,7 @@ void ColonyEngine::drawStaticObjects() {
 		DrawCmd cmd;
 		cmd.depth = depth;
 		cmd.index = (int)i;
+		cmd.screenX = sx;
 		drawList.push_back(cmd);
 	}
 
@@ -1578,29 +2069,9 @@ void ColonyEngine::drawStaticObjects() {
 	for (uint i = 0; i < drawList.size(); i++) {
 		const DrawCmd &d = drawList[i];
 		const Thing &obj = _objects[d.index];
-		int sx, depth;
-		if (!projectWorld(obj.where.xloc, obj.where.yloc, sx, depth))
-			continue;
-		int scale = _rtable[d.depth];
-		int baseY = _height - (_centerY - scale);
-		int h = CLIP<int>(scale, 4, 96);
-		int w = CLIP<int>(h >> 1, 3, 64);
-		Common::Rect body(sx - w, baseY - h, sx + w, baseY);
-		const int bodyLeft = (int)body.left;
-		const int bodyTop = (int)body.top;
-		const int bodyRight = (int)body.right;
-		const int bodyBottom = (int)body.bottom;
-		const int clipLeft = MAX(bodyLeft, MAX((int)obj.clip.left, (int)_screenR.left));
-		const int clipTop = MAX(bodyTop, MAX((int)obj.clip.top, (int)_screenR.top));
-		const int clipRight = MIN(bodyRight, MIN((int)obj.clip.right, (int)_screenR.right));
-		const int clipBottom = MIN(bodyBottom, MIN((int)obj.clip.bottom, (int)_screenR.bottom));
-		if (clipLeft >= clipRight || clipTop >= clipBottom)
-			continue;
-		Common::Rect clipped(clipLeft, clipTop, clipRight, clipBottom);
-
-		uint32 color = objectColor(obj.type);
-		_gfx->drawRect(clipped, color);
-		_gfx->drawLine(clipped.left, clipped.bottom - 1, clipped.right - 1, clipped.bottom - 1, color);
+		const uint32 color = objectColor(obj.type);
+		if (!drawStaticObjectPrisms(obj, color))
+			drawStaticObjectFallback(obj, color, d.depth, d.screenX);
 	}
 }
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 4fff809b8e6..5c0b2350cb8 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -152,6 +152,23 @@ private:
 	bool projectWorld(int worldX, int worldY, int &screenX, int &depth) const;
 	uint32 objectColor(int type) const;
 	void setRobot(int l, int r, int num);
+	struct ProjectedPrismPart {
+		static const int kMaxPoints = 8;
+		static const int kMaxSurfaces = 8;
+		int pointCount;
+		int x[kMaxPoints];
+		int y[kMaxPoints];
+		int depth[kMaxPoints];
+		bool vsurface[kMaxSurfaces];
+		bool visible;
+	};
+	struct PrismPartDef;
+	bool projectPrismPart(const Thing &obj, const PrismPartDef &part, bool useLook, ProjectedPrismPart &out) const;
+	bool isSurfaceClockwise(const ProjectedPrismPart &part, const int surface[8]) const;
+	bool clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const;
+	void drawProjectedPrism(const ProjectedPrismPart &part, const PrismPartDef &def, int force, uint32 color, const Common::Rect &clip);
+	bool drawStaticObjectPrisms(const Thing &obj, uint32 baseColor);
+	void drawStaticObjectFallback(const Thing &obj, uint32 color, int depth, int sx);
 	void updateViewportLayout();
 	void drawDashboardStep1();
 };


Commit: db57cb7ad26d3be5719f45b59dc53706005884cb
    https://github.com/scummvm/scummvm/commit/db57cb7ad26d3be5719f45b59dc53706005884cb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:03+02:00

Commit Message:
COLONY: initial position

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index fd1f88fa6bf..a7cde9e26e5 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -135,12 +135,13 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_headsUpRect = Common::Rect(0, 0, 0, 0);
 	_powerRect = Common::Rect(0, 0, 0, 0);
 	
-	_me.xindex = 10;
-	_me.yindex = 10;
-	_me.xloc = 10 * 256 + 128;
-	_me.yloc = 10 * 256 + 128;
-	_me.look = 0;
-	_me.ang = 0;
+	// DOS gameInit(): Me.ang=Me.look=32; Me.xloc=4400; Me.yloc=4400.
+	_me.xloc = 4400;
+	_me.yloc = 4400;
+	_me.xindex = _me.xloc >> 8;
+	_me.yindex = _me.yloc >> 8;
+	_me.look = 32;
+	_me.ang = 32;
 
 	initTrig();
 }


Commit: 2c673bd901b9b1b6577c277ae503bb48ebcd9c23
    https://github.com/scummvm/scummvm/commit/2c673bd901b9b1b6577c277ae503bb48ebcd9c23
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:03+02:00

Commit Message:
COLONY: add 3D geometry for chair object

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index a7cde9e26e5..c0e1e946987 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -97,9 +97,15 @@ static const int g_dirLeft[4] = {2, 0, 3, 1};
 
 enum ObjectType {
 	kObjDesk = 21,
+	kObjCChair = 23,
 	kObjBed = 24,
 	kObjTable = 25,
+	kObjCouch = 26,
+	kObjChair = 27,
+	kObjTV = 28,
 	kObjScreen = 29,
+	kObjConsole = 30,
+	kObjDrawer = 37,
 	kObjBBed = 42
 };
 
@@ -1883,6 +1889,110 @@ bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
 	};
 	static const int kDeskScreenSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
 
+	static const int kCSeatPts[4][3] = {
+		{-40, 40, 60}, {40, 40, 60}, {40, -40, 60}, {-40, -40, 60}
+	};
+	static const int kCSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kCArmLeftPts[4][3] = {
+		{-50, 40, 90}, {-40, 40, 60}, {-40, -40, 60}, {-50, -40, 90}
+	};
+	static const int kCArmLeftSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kCArmRightPts[4][3] = {
+		{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
+	};
+	static const int kCArmRightSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kCBackPts[4][3] = {
+		{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
+	};
+	static const int kCBackSurf[2][8] = {
+		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	};
+	static const int kCBasePts[8][3] = {
+		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+		{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
+	};
+	static const int kCBaseSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	};
+
+	static const int kConsolePts[8][3] = {
+		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+		{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
+	};
+	static const int kConsoleSurf[5][8] = {
+		{0, 4, 4, 0, 3, 7, 0, 0}, {0, 4, 7, 3, 2, 6, 0, 0},
+		{0, 4, 5, 1, 0, 4, 0, 0}, {0, 4, 6, 2, 1, 5, 0, 0},
+		{0, 4, 7, 6, 5, 4, 0, 0}
+	};
+
+	static const int kCouchSurf[5][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+		{0, 4, 7, 6, 5, 4, 0, 0}
+	};
+	static const int kACouchPts[8][3] = {
+		{-50, 150, 0}, {50, 150, 0}, {50, -150, 0}, {-50, -150, 0},
+		{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50}
+	};
+	static const int kBCouchPts[8][3] = {
+		{-80, 150, 0}, {-45, 150, 0}, {-45, -150, 0}, {-80, -150, 0},
+		{-80, 150, 120}, {-55, 150, 120}, {-55, -150, 120}, {-80, -150, 120}
+	};
+	static const int kCCouchPts[8][3] = {
+		{-70, 170, 0}, {50, 170, 0}, {50, 150, 0}, {-70, 150, 0},
+		{-70, 170, 80}, {50, 170, 80}, {50, 150, 80}, {-70, 150, 80}
+	};
+	static const int kDCouchPts[8][3] = {
+		{-70, -150, 0}, {50, -150, 0}, {50, -170, 0}, {-70, -170, 0},
+		{-70, -150, 80}, {50, -150, 80}, {50, -170, 80}, {-70, -170, 80}
+	};
+
+	static const int kAChairPts[8][3] = {
+		{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
+		{-50, 50, 50}, {50, 50, 50}, {50, -50, 50}, {-50, -50, 50}
+	};
+	static const int kBChairPts[8][3] = {
+		{-80, 50, 0}, {-45, 50, 0}, {-45, -50, 0}, {-80, -50, 0},
+		{-80, 50, 120}, {-55, 50, 120}, {-55, -50, 120}, {-80, -50, 120}
+	};
+	static const int kCChairPts2[8][3] = {
+		{-70, 70, 0}, {50, 70, 0}, {50, 50, 0}, {-70, 50, 0},
+		{-70, 70, 80}, {50, 70, 80}, {50, 50, 80}, {-70, 50, 80}
+	};
+	static const int kDChairPts[8][3] = {
+		{-70, -50, 0}, {50, -50, 0}, {50, -70, 0}, {-70, -70, 0},
+		{-70, -50, 80}, {50, -50, 80}, {50, -70, 80}, {-70, -70, 80}
+	};
+
+	static const int kTVBodyPts[8][3] = {
+		{-30, 60, 0}, {30, 60, 0}, {30, -60, 0}, {-30, -60, 0},
+		{-30, 60, 120}, {30, 60, 120}, {30, -60, 120}, {-30, -60, 120}
+	};
+	static const int kTVBodySurf[5][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+		{0, 4, 7, 6, 5, 4, 0, 0}
+	};
+	static const int kTVScreenPts[4][3] = {
+		{30, 50, 10}, {30, -50, 10}, {30, 50, 110}, {30, -50, 110}
+	};
+	static const int kTVScreenSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
+
+	static const int kDrawerPts[8][3] = {
+		{-80, 70, 0}, {0, 70, 0}, {0, -70, 0}, {-80, -70, 0},
+		{-80, 70, 100}, {0, 70, 100}, {0, -70, 100}, {-80, -70, 100}
+	};
+	static const int kDrawerSurf[5][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+		{0, 4, 7, 6, 5, 4, 0, 0}
+	};
+	static const int kMirrorPts[4][3] = {
+		{-80, 65, 100}, {-80, -65, 100}, {-80, 65, 210}, {-80, -65, 210}
+	};
+	static const int kMirrorSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
+
 	static const PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
 	static const PrismPartDef kTableParts[2] = {
 		{4, kTableTopPts, 1, kTableTopSurf},
@@ -1910,6 +2020,34 @@ bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
 		{8, kMonitorPts, 5, kComputerSurf},
 		{4, kDeskScreenPts, 1, kDeskScreenSurf}
 	};
+	static const PrismPartDef kCChairParts[5] = {
+		{4, kCSeatPts, 1, kCSeatSurf},
+		{4, kCArmLeftPts, 1, kCArmLeftSurf},
+		{4, kCArmRightPts, 1, kCArmRightSurf},
+		{4, kCBackPts, 2, kCBackSurf},
+		{8, kCBasePts, 4, kCBaseSurf}
+	};
+	static const PrismPartDef kConsolePart = {8, kConsolePts, 5, kConsoleSurf};
+	static const PrismPartDef kCouchParts[4] = {
+		{8, kACouchPts, 5, kCouchSurf},
+		{8, kBCouchPts, 5, kCouchSurf},
+		{8, kCCouchPts, 5, kCouchSurf},
+		{8, kDCouchPts, 5, kCouchSurf}
+	};
+	static const PrismPartDef kChairParts[4] = {
+		{8, kAChairPts, 5, kCouchSurf},
+		{8, kBChairPts, 5, kCouchSurf},
+		{8, kCChairPts2, 5, kCouchSurf},
+		{8, kDChairPts, 5, kCouchSurf}
+	};
+	static const PrismPartDef kTVParts[2] = {
+		{8, kTVBodyPts, 5, kTVBodySurf},
+		{4, kTVScreenPts, 1, kTVScreenSurf}
+	};
+	static const PrismPartDef kDrawerParts[2] = {
+		{8, kDrawerPts, 5, kDrawerSurf},
+		{4, kMirrorPts, 1, kMirrorSurf}
+	};
 
 	Common::Rect drawClip(MAX<int>((int)obj.clip.left, (int)_screenR.left),
 	                     MAX<int>((int)obj.clip.top, (int)_screenR.top),
@@ -1924,6 +2062,96 @@ bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
 
 	ProjectedPrismPart p[10];
 	switch (obj.type) {
+	case kObjConsole:
+		if (!projectPrismPart(obj, kConsolePart, false, p[0]) || !p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kConsolePart, 0, tint(baseColor, 0), drawClip);
+		return true;
+	case kObjCChair:
+		for (int i = 0; i < 5; i++)
+			projectPrismPart(obj, kCChairParts[i], false, p[i]);
+		if (p[4].visible)
+			drawProjectedPrism(p[4], kCChairParts[4], 0, tint(baseColor, -10), drawClip);
+		if (!p[0].visible)
+			return p[4].visible;
+		drawProjectedPrism(p[0], kCChairParts[0], 0, tint(baseColor, 0), drawClip);
+		if (p[3].vsurface[0]) {
+			drawProjectedPrism(p[3], kCChairParts[3], 1, tint(baseColor, 0), drawClip);
+			if (p[1].vsurface[0]) {
+				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
+				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
+			} else {
+				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
+				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
+			}
+		} else {
+			if (p[1].vsurface[0]) {
+				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
+				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
+			} else {
+				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
+				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
+			}
+			drawProjectedPrism(p[3], kCChairParts[3], 1, tint(baseColor, 0), drawClip);
+		}
+		return true;
+	case kObjCouch:
+	case kObjChair: {
+		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
+		for (int i = 0; i < 4; i++)
+			projectPrismPart(obj, parts[i], false, p[i]);
+		if (!p[0].visible)
+			return false;
+		if (p[2].vsurface[1] && p[3].vsurface[2]) {
+			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
+			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
+			if (p[0].vsurface[3]) {
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+			} else {
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+			}
+		} else if (p[3].vsurface[1]) {
+			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
+			if (p[0].vsurface[3]) {
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+			} else {
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+			}
+			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
+		} else {
+			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
+			if (p[0].vsurface[3]) {
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+			} else {
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+			}
+			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
+		}
+		return true;
+	}
+	case kObjTV:
+		projectPrismPart(obj, kTVParts[0], false, p[0]);
+		projectPrismPart(obj, kTVParts[1], false, p[1]);
+		if (!p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kTVParts[0], 0, tint(baseColor, 0), drawClip);
+		if (p[1].vsurface[0])
+			drawProjectedPrism(p[1], kTVParts[1], 0, tint(baseColor, 35), drawClip);
+		return true;
+	case kObjDrawer:
+		projectPrismPart(obj, kDrawerParts[0], false, p[0]);
+		projectPrismPart(obj, kDrawerParts[1], false, p[1]);
+		if (!p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kDrawerParts[0], 0, tint(baseColor, 0), drawClip);
+		drawProjectedPrism(p[1], kDrawerParts[1], 1, tint(baseColor, 30), drawClip);
+		return true;
 	case kObjScreen:
 		if (!projectPrismPart(obj, kScreenPart, false, p[0]) || !p[0].visible)
 			return false;


Commit: 536746c7883e2e6b17da58fa955bfc164508517c
    https://github.com/scummvm/scummvm/commit/536746c7883e2e6b17da58fa955bfc164508517c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:04+02:00

Commit Message:
COLONY: add occupiedObjectAt() collision detection for movement blocking

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c0e1e946987..5f07a1ab3ef 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -97,6 +97,7 @@ static const int g_dirLeft[4] = {2, 0, 3, 1};
 
 enum ObjectType {
 	kObjDesk = 21,
+	kObjPlant = 22,
 	kObjCChair = 23,
 	kObjBed = 24,
 	kObjTable = 25,
@@ -105,7 +106,19 @@ enum ObjectType {
 	kObjTV = 28,
 	kObjScreen = 29,
 	kObjConsole = 30,
+	kObjPowerSuit = 31,
+	kObjForkLift = 32,
+	kObjCryo = 33,
+	kObjBox1 = 34,
+	kObjBox2 = 35,
+	kObjTeleport = 36,
 	kObjDrawer = 37,
+	kObjTub = 38,
+	kObjSink = 39,
+	kObjToilet = 40,
+	kObjPToilet = 43,
+	kObjProjector = 45,
+	kObjReactor = 46,
 	kObjBBed = 42
 };
 
@@ -142,12 +155,14 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_powerRect = Common::Rect(0, 0, 0, 0);
 	
 	// DOS gameInit(): Me.ang=Me.look=32; Me.xloc=4400; Me.yloc=4400.
+	memset(&_me, 0, sizeof(_me));
 	_me.xloc = 4400;
 	_me.yloc = 4400;
 	_me.xindex = _me.xloc >> 8;
 	_me.yindex = _me.yloc >> 8;
 	_me.look = 32;
 	_me.ang = 32;
+	_me.type = MENUM;
 
 	initTrig();
 }
@@ -221,6 +236,9 @@ void ColonyEngine::loadMap(int mnum) {
 	}
 	free(buffer);
 	_level = mnum;
+	_me.type = MENUM;
+	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
+		_robotArray[_me.xindex][_me.yindex] = MENUM;
 	debug("Successfully loaded map %d (static objects: %d)", mnum, (int)_objects.size());
 }
 
@@ -260,11 +278,28 @@ void ColonyEngine::perspective(int pnt[2], int rox, int roy) {
 		pnt[1] = _centerY - _rtable[roy];
 }
 
+int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
+	if (x < 0 || x >= 32 || y < 0 || y >= 32)
+		return -1;
+	const int rnum = _robotArray[x][y];
+	if (rnum <= 0)
+		return 0;
+	if (pobject == &_me && rnum <= (int)_objects.size()) {
+		Thing &obj = _objects[rnum - 1];
+		if (obj.type <= BASEOBJECT)
+			obj.where.look = obj.where.ang = _me.ang + 128;
+	}
+	return rnum;
+}
+
 int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 	int xind2, yind2;
 	xind2 = xnew >> 8;
 	yind2 = ynew >> 8;
 	_change = true;
+	auto occupied = [&]() -> int {
+		return occupiedObjectAt(xind2, yind2, pobject);
+	};
 
 	if (xind2 == pobject->xindex) {
 		if (yind2 == pobject->yindex) {
@@ -274,10 +309,13 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 			pobject->yloc = ynew;
 			return 0;
 		} else {
-			if (yind2 > pobject->yindex) {
-				if (!(_wall[pobject->xindex][yind2] & 1)) {
-					pobject->yindex = yind2;
-					pobject->xindex = xind2;
+				if (yind2 > pobject->yindex) {
+					if (!(_wall[pobject->xindex][yind2] & 1)) {
+						int rnum = occupied();
+						if (rnum)
+							return rnum;
+						pobject->yindex = yind2;
+						pobject->xindex = xind2;
 					pobject->dx = xnew - pobject->xloc;
 					pobject->dy = ynew - pobject->yloc;
 					pobject->xloc = xnew;
@@ -287,10 +325,13 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 					debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
 					return -1;
 				}
-			} else {
-				if (!(_wall[pobject->xindex][pobject->yindex] & 1)) {
-					pobject->yindex = yind2;
-					pobject->xindex = xind2;
+				} else {
+					if (!(_wall[pobject->xindex][pobject->yindex] & 1)) {
+						int rnum = occupied();
+						if (rnum)
+							return rnum;
+						pobject->yindex = yind2;
+						pobject->xindex = xind2;
 					pobject->dx = xnew - pobject->xloc;
 					pobject->dy = ynew - pobject->yloc;
 					pobject->xloc = xnew;
@@ -305,6 +346,9 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 	} else if (yind2 == pobject->yindex) {
 		if (xind2 > pobject->xindex) {
 			if (!(_wall[xind2][pobject->yindex] & 2)) {
+				int rnum = occupied();
+				if (rnum)
+					return rnum;
 				pobject->yindex = yind2;
 				pobject->xindex = xind2;
 				pobject->dx = xnew - pobject->xloc;
@@ -318,6 +362,9 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 			}
 		} else {
 			if (!(_wall[pobject->xindex][pobject->yindex] & 2)) {
+				int rnum = occupied();
+				if (rnum)
+					return rnum;
 				pobject->yindex = yind2;
 				pobject->xindex = xind2;
 				pobject->dx = xnew - pobject->xloc;
@@ -357,6 +404,9 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 				}
 			}
 		}
+		int rnum = occupied();
+		if (rnum)
+			return rnum;
 		pobject->yindex = yind2;
 		pobject->xindex = xind2;
 		pobject->dx = xnew - pobject->xloc;
@@ -368,6 +418,113 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 	return -1;
 }
 
+void ColonyEngine::interactWithObject(int objNum) {
+	if (objNum <= 0 || objNum > (int)_objects.size())
+		return;
+
+	const Thing &obj = _objects[objNum - 1];
+	if (!obj.alive)
+		return;
+
+	const int x = CLIP<int>(obj.where.xindex, 0, 30);
+	const int y = CLIP<int>(obj.where.yindex, 0, 30);
+	const int action0 = _mapData[x][y][4][3];
+	const int action1 = _mapData[x][y][4][4];
+
+	switch (obj.type) {
+	case kObjDesk:
+		debug("CCommand: DESK action (%d, %d)", action0, action1);
+		break;
+	case kObjConsole:
+		switch (action0) {
+		case 1:
+			debug("CCommand: CONSOLE reactor");
+			break;
+		case 2:
+			debug("CCommand: CONSOLE ship controls");
+			break;
+		case 3:
+			debug("CCommand: CONSOLE security");
+			break;
+		default:
+			debug("CCommand: CONSOLE action=%d", action0);
+			break;
+		}
+		break;
+	case kObjProjector:
+		switch (action0) {
+		case 1:
+			debug("CCommand: PROJECTOR creatures");
+			break;
+		case 2:
+			debug("CCommand: PROJECTOR teleporters");
+			break;
+		default:
+			debug("CCommand: PROJECTOR action=%d", action0);
+			break;
+		}
+		break;
+	case kObjPowerSuit:
+		debug("CCommand: POWERSUIT");
+		break;
+	case kObjTeleport:
+		debug("CCommand: TELEPORT");
+		break;
+	case kObjDrawer:
+		debug("CCommand: DRAWER vanity=%d", action0);
+		break;
+	case kObjScreen:
+		debug("CCommand: SCREEN");
+		break;
+	case kObjToilet:
+	case kObjPToilet:
+		debug("CCommand: TOILET");
+		break;
+	case kObjTub:
+		debug("CCommand: TUB");
+		break;
+	case kObjSink:
+		debug("CCommand: SINK");
+		break;
+	case kObjCryo:
+		debug("CCommand: CRYO text=%d", action0);
+		break;
+	case kObjTV:
+		debug("CCommand: TV level=%d", _level);
+		break;
+	case kObjForkLift:
+	case kObjReactor:
+	case kObjBox1:
+	case kObjBox2:
+		debug("CCommand: object type %d requires forklift/reactor flow", obj.type);
+		break;
+	case kObjPlant:
+	case kObjCChair:
+	case kObjBed:
+	case kObjTable:
+	case kObjCouch:
+	case kObjChair:
+	case kObjBBed:
+		// Matches DOS CCommand where these objects are non-interactive blockers.
+		break;
+	default:
+		debug("CCommand: object type %d", obj.type);
+		break;
+	}
+}
+
+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 robot = checkwall(xnew, ynew, &_me);
+	if (robot > 0 && allowInteraction)
+		interactWithObject(robot);
+
+	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
+		_robotArray[_me.xindex][_me.yindex] = MENUM;
+}
+
 void ColonyEngine::quadrant() {
 	int remain;
 	int quad;
@@ -2548,23 +2705,24 @@ Common::Error ColonyEngine::run() {
 	while (!shouldQuit()) {
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN) {
-				debug("Key down: %d", event.kbd.keycode);
-				switch (event.kbd.keycode) {
-				case Common::KEYCODE_UP:
-				{
-					int xnew = _me.xloc + (_cost[_me.look] >> 2);
-					int ynew = _me.yloc + (_sint[_me.look] >> 2);
-					checkwall(xnew, ynew, &_me);
-					break;
-				}
-				case Common::KEYCODE_DOWN:
-				{
-					int xnew = _me.xloc - (_cost[_me.look] >> 2);
-					int ynew = _me.yloc - (_sint[_me.look] >> 2);
-					checkwall(xnew, ynew, &_me);
-					break;
-				}
+				if (event.type == Common::EVENT_KEYDOWN) {
+					debug("Key down: %d", event.kbd.keycode);
+					const bool allowInteraction = (event.kbd.flags & Common::KBD_CTRL) == 0;
+					switch (event.kbd.keycode) {
+					case Common::KEYCODE_UP:
+					{
+						int xnew = _me.xloc + (_cost[_me.look] >> 2);
+						int ynew = _me.yloc + (_sint[_me.look] >> 2);
+						cCommand(xnew, ynew, allowInteraction);
+						break;
+					}
+					case Common::KEYCODE_DOWN:
+					{
+						int xnew = _me.xloc - (_cost[_me.look] >> 2);
+						int ynew = _me.yloc - (_sint[_me.look] >> 2);
+						cCommand(xnew, ynew, allowInteraction);
+						break;
+					}
 				case Common::KEYCODE_LEFT:
 					_me.look = (uint8)((int)_me.look + 8);
 					_change = true;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 5c0b2350cb8..09f3274f62a 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -79,6 +79,7 @@ public:
 	void perspective(int pnt[2], int rox, int roy);
 	void rot_init(int x, int y);
 	int checkwall(int xnew, int ynew, Locate *pobject);
+	void cCommand(int xnew, int ynew, bool allowInteraction);
 
 private:
 	const ADGameDescription *_gameDescription;
@@ -169,6 +170,8 @@ private:
 	void drawProjectedPrism(const ProjectedPrismPart &part, const PrismPartDef &def, int force, uint32 color, const Common::Rect &clip);
 	bool drawStaticObjectPrisms(const Thing &obj, uint32 baseColor);
 	void drawStaticObjectFallback(const Thing &obj, uint32 color, int depth, int sx);
+	int occupiedObjectAt(int x, int y, const Locate *pobject);
+	void interactWithObject(int objNum);
 	void updateViewportLayout();
 	void drawDashboardStep1();
 };


Commit: 5bd78c0e6fa13751fcc3a429fe3115acc58a5c62
    https://github.com/scummvm/scummvm/commit/5bd78c0e6fa13751fcc3a429fe3115acc58a5c62
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:04+02:00

Commit Message:
COLONY: fix integer overflow in perspective projection using int64

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 5f07a1ab3ef..b8961ab326c 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -1699,7 +1699,7 @@ bool ColonyEngine::projectWorld(int worldX, int worldY, int &screenX, int &depth
 	if (yy >= 11585)
 		yy = 11584;
 
-	screenX = _centerX + (int)((xx << 8) / yy);
+	screenX = _centerX + (int)(((int64)xx * 256) / yy);
 	depth = (int)yy;
 	return true;
 }
@@ -1819,10 +1819,10 @@ bool ColonyEngine::projectPrismPart(const Thing &obj, const PrismPartDef &part,
 		if (yy <= 16)
 			yy = 16;
 
-		out.x[i] = _centerX + (int)((xx << 8) / yy);
+		out.x[i] = _centerX + (int)(((int64)xx * 256) / yy);
 		out.depth[i] = (int)yy;
 		const long zrel = (long)pz - kFloorShift;
-		out.y[i] = _centerY - (int)((zrel << 8) / yy);
+		out.y[i] = _centerY - (int)(((int64)zrel * 256) / yy);
 		minX = MIN(minX, out.x[i]);
 		maxX = MAX(maxX, out.x[i]);
 		minY = MIN(minY, out.y[i]);
@@ -2206,12 +2206,13 @@ bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
 		{4, kMirrorPts, 1, kMirrorSurf}
 	};
 
-	Common::Rect drawClip(MAX<int>((int)obj.clip.left, (int)_screenR.left),
-	                     MAX<int>((int)obj.clip.top, (int)_screenR.top),
-	                     MIN<int>((int)obj.clip.right, (int)_screenR.right),
-	                     MIN<int>((int)obj.clip.bottom, (int)_screenR.bottom));
-	if (drawClip.left >= drawClip.right || drawClip.top >= drawClip.bottom)
+	const int clipLeft = MAX<int>((int)obj.clip.left, (int)_screenR.left);
+	const int clipTop = MAX<int>((int)obj.clip.top, (int)_screenR.top);
+	const int clipRight = MIN<int>((int)obj.clip.right, (int)_screenR.right);
+	const int clipBottom = MIN<int>((int)obj.clip.bottom, (int)_screenR.bottom);
+	if (clipLeft >= clipRight || clipTop >= clipBottom)
 		return false;
+	Common::Rect drawClip(clipLeft, clipTop, clipRight, clipBottom);
 
 	auto tint = [](uint32 base, int delta) -> uint32 {
 		return (uint32)CLIP<int>((int)base + delta, 0, 255);
@@ -2470,13 +2471,17 @@ void ColonyEngine::setRobot(int l, int r, int num) {
 		r = _screenR.right;
 	if (l >= r)
 		return;
+	const int clipLeft = l + 1;
+	const int clipRight = r - 2;
+	if (clipLeft >= clipRight)
+		return;
 
 	Thing &obj = _objects[num - 1];
 	if (!obj.alive)
 		return;
 	obj.visible = 1;
-	obj.clip.left = l + 1;
-	obj.clip.right = r - 2;
+	obj.clip.left = clipLeft;
+	obj.clip.right = clipRight;
 	obj.clip.top = _clip.top;
 	obj.clip.bottom = _clip.bottom;
 }


Commit: f154dd5cc47005873328ad8628fd6d5b4090e43a
    https://github.com/scummvm/scummvm/commit/f154dd5cc47005873328ad8628fd6d5b4090e43a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:04+02:00

Commit Message:
COLONY: keep minimap inside box

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index b8961ab326c..85b45287b8b 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -2578,8 +2578,13 @@ void ColonyEngine::drawDashboardStep1() {
 		_gfx->drawLine(cx, cy - 2, cx, cy + 2, accent);
 	}
 
-	if (_headsUpRect.width() > 4 && _headsUpRect.height() > 4) {
-		_gfx->drawRect(_headsUpRect, frame);
+		if (_headsUpRect.width() > 4 && _headsUpRect.height() > 4) {
+			_gfx->drawRect(_headsUpRect, frame);
+			const Common::Rect miniMapClip(_headsUpRect.left + 1, _headsUpRect.top + 1, _headsUpRect.right - 1, _headsUpRect.bottom - 1);
+			auto drawMiniMapLine = [&](int x1, int y1, int x2, int y2, uint32 color) {
+				if (clipLineToRect(x1, y1, x2, y2, miniMapClip))
+					_gfx->drawLine(x1, y1, x2, y2, color);
+			};
 
 		const int lExtBase = _dashBoardRect.width() >> 1;
 		int lExt = lExtBase + (lExtBase >> 1);
@@ -2608,12 +2613,12 @@ void ColonyEngine::drawDashboardStep1() {
 		xcorner[5] = ccenterx + (((long)(xloc + sExt) * tsin - (long)yloc * tcos) >> 8);
 		ycorner[5] = ccentery - (((long)yloc * tsin + (long)(xloc + sExt) * tcos) >> 8);
 
-		const int dx = xcorner[1] - xcorner[0];
-		const int dy = ycorner[0] - ycorner[1];
-		_gfx->drawLine(xcorner[0] - dx, ycorner[0] + dy, xcorner[1] + dx, ycorner[1] - dy, accent);
-		_gfx->drawLine(xcorner[1] + dy, ycorner[1] + dx, xcorner[2] - dy, ycorner[2] - dx, accent);
-		_gfx->drawLine(xcorner[2] + dx, ycorner[2] - dy, xcorner[3] - dx, ycorner[3] + dy, accent);
-		_gfx->drawLine(xcorner[3] - dy, ycorner[3] - dx, xcorner[0] + dy, ycorner[0] + dx, accent);
+			const int dx = xcorner[1] - xcorner[0];
+			const int dy = ycorner[0] - ycorner[1];
+			drawMiniMapLine(xcorner[0] - dx, ycorner[0] + dy, xcorner[1] + dx, ycorner[1] - dy, accent);
+			drawMiniMapLine(xcorner[1] + dy, ycorner[1] + dx, xcorner[2] - dy, ycorner[2] - dx, accent);
+			drawMiniMapLine(xcorner[2] + dx, ycorner[2] - dy, xcorner[3] - dx, ycorner[3] + dy, accent);
+			drawMiniMapLine(xcorner[3] - dy, ycorner[3] - dx, xcorner[0] + dy, ycorner[0] + dx, accent);
 
 		auto drawMarker = [&](int x, int y, int halfSize, uint32 color) {
 			const int l = MAX<int>(_headsUpRect.left + 1, x - halfSize);


Commit: 5caa532151e7bac87bd2a38438b81980114bb132
    https://github.com/scummvm/scummvm/commit/5caa532151e7bac87bd2a38438b81980114bb132
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:05+02:00

Commit Message:
COLONY: add crosshair rendering and object interaction handling

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 85b45287b8b..d84a1822f6c 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -92,6 +92,13 @@ enum WallFeatureType {
 	kWallFeatureColor = 12
 };
 
+enum MapDirection {
+	kDirNorth = 0,
+	kDirEast = 1,
+	kDirWest = 2,
+	kDirSouth = 3
+};
+
 static const int g_dirRight[4] = {1, 3, 0, 2};
 static const int g_dirLeft[4] = {2, 0, 3, 1};
 
@@ -141,6 +148,11 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_mouseSensitivity = 1;
 	_change = true;
 	_showDashBoard = true;
+	_crosshair = true;
+	_insight = false;
+	_hasKeycard = false;
+	_unlocked = false;
+	_weapons = 0;
 	
 	memset(_wall, 0, sizeof(_wall));
 	memset(_mapData, 0, sizeof(_mapData));
@@ -293,13 +305,25 @@ int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
 }
 
 int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
-	int xind2, yind2;
-	xind2 = xnew >> 8;
-	yind2 = ynew >> 8;
+	const int xind2 = xnew >> 8;
+	const int yind2 = ynew >> 8;
 	_change = true;
+
 	auto occupied = [&]() -> int {
 		return occupiedObjectAt(xind2, yind2, pobject);
 	};
+	auto moveTo = [&]() -> int {
+		const int rnum = occupied();
+		if (rnum)
+			return rnum;
+		pobject->yindex = yind2;
+		pobject->xindex = xind2;
+		pobject->dx = xnew - pobject->xloc;
+		pobject->dy = ynew - pobject->yloc;
+		pobject->xloc = xnew;
+		pobject->yloc = ynew;
+		return 0;
+	};
 
 	if (xind2 == pobject->xindex) {
 		if (yind2 == pobject->yindex) {
@@ -308,114 +332,156 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 			pobject->xloc = xnew;
 			pobject->yloc = ynew;
 			return 0;
-		} else {
-				if (yind2 > pobject->yindex) {
-					if (!(_wall[pobject->xindex][yind2] & 1)) {
-						int rnum = occupied();
-						if (rnum)
-							return rnum;
-						pobject->yindex = yind2;
-						pobject->xindex = xind2;
-					pobject->dx = xnew - pobject->xloc;
-					pobject->dy = ynew - pobject->yloc;
-					pobject->xloc = xnew;
-					pobject->yloc = ynew;
-					return 0;
-				} else {
-					debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
-					return -1;
-				}
-				} else {
-					if (!(_wall[pobject->xindex][pobject->yindex] & 1)) {
-						int rnum = occupied();
-						if (rnum)
-							return rnum;
-						pobject->yindex = yind2;
-						pobject->xindex = xind2;
-					pobject->dx = xnew - pobject->xloc;
-					pobject->dy = ynew - pobject->yloc;
-					pobject->xloc = xnew;
-					pobject->yloc = ynew;
-					return 0;
-				} else {
-					debug("Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
-					return -1;
-				}
-			}
 		}
-	} else if (yind2 == pobject->yindex) {
+
+		if (yind2 > pobject->yindex) {
+			if (!(_wall[pobject->xindex][yind2] & 1))
+				return moveTo();
+			if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirNorth, pobject))
+				return moveTo();
+			debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
+			return -1;
+		}
+
+		if (!(_wall[pobject->xindex][pobject->yindex] & 1))
+			return moveTo();
+		if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirSouth, pobject))
+			return moveTo();
+		debug("Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
+		return -1;
+	}
+
+	if (yind2 == pobject->yindex) {
 		if (xind2 > pobject->xindex) {
-			if (!(_wall[xind2][pobject->yindex] & 2)) {
-				int rnum = occupied();
-				if (rnum)
-					return rnum;
-				pobject->yindex = yind2;
-				pobject->xindex = xind2;
-				pobject->dx = xnew - pobject->xloc;
-				pobject->dy = ynew - pobject->yloc;
-				pobject->xloc = xnew;
-				pobject->yloc = ynew;
-				return 0;
-			} else {
-				debug("Collision East at x=%d y=%d", xind2, pobject->yindex);
+			if (!(_wall[xind2][pobject->yindex] & 2))
+				return moveTo();
+			if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirEast, pobject))
+				return moveTo();
+			debug("Collision East at x=%d y=%d", xind2, pobject->yindex);
+			return -1;
+		}
+
+		if (!(_wall[pobject->xindex][pobject->yindex] & 2))
+			return moveTo();
+		if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirWest, pobject))
+			return moveTo();
+		debug("Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
+		return -1;
+	}
+
+	// Diagonal
+	if (xind2 > pobject->xindex) {
+		if (yind2 > pobject->yindex) {
+			if ((_wall[pobject->xindex][yind2] & 1) || (_wall[xind2][pobject->yindex] & 2) || (_wall[xind2][yind2] & 3)) {
+				debug("Collision Diagonal SE");
 				return -1;
 			}
 		} else {
-			if (!(_wall[pobject->xindex][pobject->yindex] & 2)) {
-				int rnum = occupied();
-				if (rnum)
-					return rnum;
-				pobject->yindex = yind2;
-				pobject->xindex = xind2;
-				pobject->dx = xnew - pobject->xloc;
-				pobject->dy = ynew - pobject->yloc;
-				pobject->xloc = xnew;
-				pobject->yloc = ynew;
-				return 0;
-			} else {
-				debug("Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
+			if ((_wall[pobject->xindex][pobject->yindex] & 1) || (_wall[xind2][yind2] & 2) || (_wall[xind2][pobject->yindex] & 3)) {
+				debug("Collision Diagonal NE");
 				return -1;
 			}
 		}
 	} else {
-		// Diagonal
-		if (xind2 > pobject->xindex) {
-			if (yind2 > pobject->yindex) {
-				if ((_wall[pobject->xindex][yind2] & 1) || (_wall[xind2][pobject->yindex] & 2) || (_wall[xind2][yind2] & 3)) {
-					debug("Collision Diagonal SE");
-					return -1;
-				}
-			} else {
-				if ((_wall[pobject->xindex][pobject->yindex] & 1) || (_wall[xind2][yind2] & 2) || (_wall[xind2][pobject->yindex] & 3)) {
-					debug("Collision Diagonal NE");
-					return -1;
-				}
+		if (yind2 > pobject->yindex) {
+			if ((_wall[xind2][yind2] & 1) || (_wall[pobject->xindex][pobject->yindex] & 2) || (_wall[pobject->xindex][yind2] & 3)) {
+				debug("Collision Diagonal SW");
+				return -1;
 			}
 		} else {
-			if (yind2 > pobject->yindex) {
-				if ((_wall[xind2][yind2] & 1) || (_wall[pobject->xindex][pobject->yindex] & 2) || (_wall[pobject->xindex][yind2] & 3)) {
-					debug("Collision Diagonal SW");
-					return -1;
-				}
-			} else {
-				if ((_wall[xind2][pobject->yindex] & 1) || (_wall[pobject->xindex][yind2] & 2) || (_wall[pobject->xindex][pobject->yindex] & 3)) {
-					debug("Collision Diagonal NW");
-					return -1;
-				}
+			if ((_wall[xind2][pobject->yindex] & 1) || (_wall[pobject->xindex][yind2] & 2) || (_wall[pobject->xindex][pobject->yindex] & 3)) {
+				debug("Collision Diagonal NW");
+				return -1;
 			}
 		}
-		int rnum = occupied();
-		if (rnum)
-			return rnum;
-		pobject->yindex = yind2;
-		pobject->xindex = xind2;
-		pobject->dx = xnew - pobject->xloc;
-		pobject->dy = ynew - pobject->yloc;
-		pobject->xloc = xnew;
-		pobject->yloc = ynew;
-		return 0;
 	}
-	return -1;
+
+	return moveTo();
+}
+
+bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
+	if (x < 0 || x >= 31 || y < 0 || y >= 31 || direction < 0 || direction > 3)
+		return false;
+
+	const uint8 wallType = _mapData[x][y][direction][0];
+	if (wallType != kWallFeatureDoor && wallType != kWallFeatureAirlock)
+		return false;
+
+	_mapData[x][y][direction][1] = (uint8)state;
+
+	int nx = x;
+	int ny = y;
+	int opposite = -1;
+	switch (direction) {
+	case kDirNorth:
+		ny += 1;
+		opposite = kDirSouth;
+		break;
+	case kDirEast:
+		nx += 1;
+		opposite = kDirWest;
+		break;
+	case kDirWest:
+		nx -= 1;
+		opposite = kDirEast;
+		break;
+	case kDirSouth:
+		ny -= 1;
+		opposite = kDirNorth;
+		break;
+	default:
+		return true;
+	}
+
+	if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31 && opposite >= 0) {
+		if (_mapData[nx][ny][opposite][0] == wallType)
+			_mapData[nx][ny][opposite][1] = (uint8)state;
+	}
+
+	return true;
+}
+
+int ColonyEngine::openAdjacentDoors(int x, int y) {
+	int opened = 0;
+	for (int dir = 0; dir < 4; dir++) {
+		const uint8 *map = mapFeatureAt(x, y, dir);
+		if (!map)
+			continue;
+		if (map[0] == kWallFeatureDoor && map[1] != 0) {
+			if (setDoorState(x, y, dir, 0))
+				opened++;
+		}
+	}
+	return opened;
+}
+
+bool ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject) {
+	const uint8 *map = mapFeatureAt(fromX, fromY, direction);
+	if (!map || map[0] == kWallFeatureNone)
+		return false;
+
+	switch (map[0]) {
+	case kWallFeatureDoor:
+		if (map[1] == 0)
+			return true;
+		if (pobject != &_me)
+			return false;
+		if (_hasKeycard || _unlocked) {
+			setDoorState(fromX, fromY, direction, 0);
+			return true;
+		}
+		return false;
+	case kWallFeatureAirlock:
+		if (map[1] == 0)
+			return true;
+		if (pobject == &_me && _unlocked) {
+			setDoorState(fromX, fromY, direction, 0);
+			return true;
+		}
+		return false;
+	default:
+		return false;
+	}
 }
 
 void ColonyEngine::interactWithObject(int objNum) {
@@ -433,18 +499,27 @@ void ColonyEngine::interactWithObject(int objNum) {
 
 	switch (obj.type) {
 	case kObjDesk:
-		debug("CCommand: DESK action (%d, %d)", action0, action1);
+		if (!_hasKeycard) {
+			_hasKeycard = true;
+			debug("CCommand: DESK granted keycard");
+		} else {
+			debug("CCommand: DESK action (%d, %d)", action0, action1);
+		}
 		break;
 	case kObjConsole:
 		switch (action0) {
 		case 1:
-			debug("CCommand: CONSOLE reactor");
+			debug("CCommand: CONSOLE reactor (%d)", action1);
 			break;
 		case 2:
-			debug("CCommand: CONSOLE ship controls");
+		{
+			const int opened = openAdjacentDoors(_me.xindex, _me.yindex);
+			debug("CCommand: CONSOLE controls opened %d nearby doors", opened);
 			break;
+		}
 		case 3:
-			debug("CCommand: CONSOLE security");
+			_unlocked = true;
+			debug("CCommand: CONSOLE security unlocked");
 			break;
 		default:
 			debug("CCommand: CONSOLE action=%d", action0);
@@ -465,13 +540,44 @@ void ColonyEngine::interactWithObject(int objNum) {
 		}
 		break;
 	case kObjPowerSuit:
+		_weapons = MAX(_weapons, 1);
+		_crosshair = true;
 		debug("CCommand: POWERSUIT");
 		break;
 	case kObjTeleport:
-		debug("CCommand: TELEPORT");
+	{
+		const int targetLevelRaw = _mapData[x][y][4][2];
+		const int targetLevel = (targetLevelRaw == 0) ? _level : targetLevelRaw;
+		const int targetX = _mapData[x][y][4][3];
+		const int targetY = _mapData[x][y][4][4];
+		if (targetLevel >= 100 || targetX <= 0 || targetX >= 31 || targetY <= 0 || targetY >= 31) {
+			debug("CCommand: TELEPORT ignored invalid target L%d (%d,%d)", targetLevelRaw, targetX, targetY);
+			break;
+		}
+		if (targetLevel != _level)
+			loadMap(targetLevel);
+		const int oldX = _me.xindex;
+		const int oldY = _me.yindex;
+		_me.xindex = targetX;
+		_me.yindex = targetY;
+		_me.xloc = (targetX << 8) + 128;
+		_me.yloc = (targetY << 8) + 128;
+		_me.ang = _me.look;
+		if (oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32)
+			_robotArray[oldX][oldY] = 0;
+		if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
+			_robotArray[_me.xindex][_me.yindex] = MENUM;
+		_change = true;
+		debug("CCommand: TELEPORT to L%d (%d,%d)", targetLevel, targetX, targetY);
 		break;
+	}
 	case kObjDrawer:
-		debug("CCommand: DRAWER vanity=%d", action0);
+		if (!_hasKeycard) {
+			_hasKeycard = true;
+			debug("CCommand: DRAWER vanity=%d (picked keycard)", action0);
+		} else {
+			debug("CCommand: DRAWER vanity=%d", action0);
+		}
 		break;
 	case kObjScreen:
 		debug("CCommand: SCREEN");
@@ -2685,6 +2791,39 @@ void ColonyEngine::drawDashboardStep1() {
 	}
 }
 
+void ColonyEngine::drawCrosshair() {
+	if (!_crosshair || _screenR.width() <= 0 || _screenR.height() <= 0)
+		return;
+
+	const uint32 color = (_weapons > 0) ? 255 : 220;
+	const int cx = _centerX;
+	const int cy = _centerY;
+	const int qx = MAX(2, _screenR.width() / 32);
+	const int qy = MAX(2, _screenR.height() / 32);
+	const int fx = (qx * 3) >> 1;
+	const int fy = (qy * 3) >> 1;
+	auto drawCrossLine = [&](int x1, int y1, int x2, int y2) {
+		if (clipLineToRect(x1, y1, x2, y2, _screenR))
+			_gfx->drawLine(x1, y1, x2, y2, color);
+	};
+
+	if (_weapons > 0) {
+		const int yTop = _insight ? (cy - qy) : (cy - fy);
+		const int yBottom = _insight ? (cy + qy) : (cy + fy);
+
+		drawCrossLine(cx - qx, yTop, cx - fx, yTop);
+		drawCrossLine(cx - fx, yTop, cx - fx, yBottom);
+		drawCrossLine(cx - fx, yBottom, cx - qx, yBottom);
+		drawCrossLine(cx + qx, yTop, cx + fx, yTop);
+		drawCrossLine(cx + fx, yTop, cx + fx, yBottom);
+		drawCrossLine(cx + fx, yBottom, cx + qx, yBottom);
+		_insight = false;
+	} else {
+		drawCrossLine(cx - qx, cy, cx + qx, cy);
+		drawCrossLine(cx, cy - qy, cx, cy + qy);
+	}
+}
+
 Common::Error ColonyEngine::run() {
 	Graphics::PixelFormat format8bpp = Graphics::PixelFormat::createFormatCLUT8();
 	initGraphics(_width, _height, &format8bpp);
@@ -2766,11 +2905,12 @@ Common::Error ColonyEngine::run() {
 			for (uint i = 0; i < _objects.size(); i++)
 				_objects[i].visible = 0;
 			
-			corridor();
-			drawStaticObjects();
-			drawDashboardStep1();
-			
-			_gfx->copyToScreen();
+				corridor();
+				drawStaticObjects();
+				drawDashboardStep1();
+				drawCrosshair();
+				
+				_gfx->copyToScreen();
 		_system->delayMillis(10);
 	}
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 09f3274f62a..a0635f55218 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -107,6 +107,11 @@ private:
 	int _mouseSensitivity;
 	bool _change;
 	bool _showDashBoard;
+	bool _crosshair;
+	bool _insight;
+	bool _hasKeycard;
+	bool _unlocked;
+	int _weapons;
 
 	int _frntxWall, _frntyWall;
 	int _sidexWall, _sideyWall;
@@ -172,8 +177,12 @@ private:
 	void drawStaticObjectFallback(const Thing &obj, uint32 color, int depth, int sx);
 	int occupiedObjectAt(int x, int y, const Locate *pobject);
 	void interactWithObject(int objNum);
+	bool setDoorState(int x, int y, int direction, int state);
+	int openAdjacentDoors(int x, int y);
+	bool tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject);
 	void updateViewportLayout();
 	void drawDashboardStep1();
+	void drawCrosshair();
 };
 
 } // End of namespace Colony


Commit: 73467a50538ba2e4b252deebea0b31af1737d832
    https://github.com/scummvm/scummvm/commit/73467a50538ba2e4b252deebea0b31af1737d832
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:05+02:00

Commit Message:
COLONY: add 3D geometry for corridor wall, plant pot and stem objects

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index d84a1822f6c..e96b3567a23 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -126,6 +126,8 @@ enum ObjectType {
 	kObjPToilet = 43,
 	kObjProjector = 45,
 	kObjReactor = 46,
+	kObjFWall = 48,
+	kObjCWall = 49,
 	kObjBBed = 42
 };
 
@@ -611,6 +613,8 @@ void ColonyEngine::interactWithObject(int objNum) {
 	case kObjCouch:
 	case kObjChair:
 	case kObjBBed:
+	case kObjFWall:
+	case kObjCWall:
 		// Matches DOS CCommand where these objects are non-interactive blockers.
 		break;
 	default:
@@ -2312,6 +2316,50 @@ bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
 		{4, kMirrorPts, 1, kMirrorSurf}
 	};
 
+	/* CWALL (object 49) - ported from DOS INITOBJ.C: simple corner/quarter-wall prism */
+	static const int kCWallPts[8][3] = {
+		{-128, 128, -160}, {0, 112, -160}, {112, 0, -160}, {128, -128, -160},
+		{-128, 128, 160},  {0, 112, 160},  {112, 0, 160},  {128, -128, 160}
+	};
+	static const int kCWallSurf[3][8] = {
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0}
+	};
+	static const PrismPartDef kCWallParts[1] = {{8, kCWallPts, 3, kCWallSurf}};
+
+	/* PLANT (new) - simple pot + stem + two leaf plates (approximates DOS plant) */
+	static const int kPlantPotPts[8][3] = {
+		{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0},
+		{-20, 20, 40}, {20, 20, 40}, {20, -20, 40}, {-20, -20, 40}
+	};
+	static const int kPlantPotSurf[5][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
+	};
+
+	static const int kPlantStemPts[8][3] = {
+		{-5, 5, 40}, {5, 5, 40}, {5, -5, 40}, {-5, -5, 40},
+		{-5, 5, 120}, {5, 5, 120}, {5, -5, 120}, {-5, -5, 120}
+	};
+	static const int kPlantStemSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	};
+
+	static const int kPlantLeaf1Pts[4][3] = {
+		{-40, 0, 120}, {40, 0, 120}, {40, 0, 140}, {-40, 0, 140}
+	};
+	static const int kPlantLeaf2Pts[4][3] = {
+		{0, 40, 120}, {0, -40, 120}, {0, 40, 140}, {0, -40, 140}
+	};
+	static const int kPlantLeafSurf[1][8] = {{0, 4, 0, 1, 2, 3, 0, 0}};
+
+	static const PrismPartDef kPlantParts[4] = {
+		{8, kPlantPotPts, 5, kPlantPotSurf},
+		{8, kPlantStemPts, 4, kPlantStemSurf},
+		{4, kPlantLeaf1Pts, 1, kPlantLeafSurf},
+		{4, kPlantLeaf2Pts, 1, kPlantLeafSurf}
+	};
+
 	const int clipLeft = MAX<int>((int)obj.clip.left, (int)_screenR.left);
 	const int clipTop = MAX<int>((int)obj.clip.top, (int)_screenR.top);
 	const int clipRight = MIN<int>((int)obj.clip.right, (int)_screenR.right);
@@ -2359,6 +2407,22 @@ bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
 			drawProjectedPrism(p[3], kCChairParts[3], 1, tint(baseColor, 0), drawClip);
 		}
 		return true;
+	case kObjPlant: {
+		for (int i = 0; i < 4; i++)
+			projectPrismPart(obj, kPlantParts[i], false, p[i]);
+		// Pot must be visible for object to appear
+		if (!p[0].visible)
+			return false;
+		// Draw pot, stem and leaves (pot darker, foliage lighter)
+		drawProjectedPrism(p[0], kPlantParts[0], 0, tint(baseColor, -30), drawClip);
+		if (p[1].visible)
+			drawProjectedPrism(p[1], kPlantParts[1], 0, tint(baseColor, 10), drawClip);
+		if (p[2].vsurface[0])
+			drawProjectedPrism(p[2], kPlantParts[2], 0, tint(baseColor, 30), drawClip);
+		if (p[3].vsurface[0])
+			drawProjectedPrism(p[3], kPlantParts[3], 0, tint(baseColor, 30), drawClip);
+		return true;
+	}
 	case kObjCouch:
 	case kObjChair: {
 		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
@@ -2416,6 +2480,18 @@ bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
 		drawProjectedPrism(p[0], kDrawerParts[0], 0, tint(baseColor, 0), drawClip);
 		drawProjectedPrism(p[1], kDrawerParts[1], 1, tint(baseColor, 30), drawClip);
 		return true;
+case kObjFWall:
+		// Simple flat wall part (fallback handled by prism below)
+		if (!projectPrismPart(obj, kCWallParts[0], false, p[0]) || !p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kCWallParts[0], 0, tint(baseColor, -10), drawClip);
+		return true;
+case kObjCWall:
+		// Corner wall (CWALL) — mirror of DOS CWallparts
+		if (!projectPrismPart(obj, kCWallParts[0], false, p[0]) || !p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kCWallParts[0], 0, tint(baseColor, -5), drawClip);
+		return true;
 	case kObjScreen:
 		if (!projectPrismPart(obj, kScreenPart, false, p[0]) || !p[0].visible)
 			return false;
@@ -2846,11 +2922,9 @@ Common::Error ColonyEngine::run() {
 	_gfx = new Gfx(_system, _width, _height);
 	
 	loadMap(1); // Try to load the first map
-	
-		_system->lockMouse(true);
-		_system->warpMouse(_centerX, _centerY);
+	_system->lockMouse(true);
+	_system->warpMouse(_centerX, _centerY);
 
-	// Temporary infinite loop to prevent ScummVM from closing immediately
 	while (!shouldQuit()) {
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
@@ -2899,18 +2973,18 @@ Common::Error ColonyEngine::run() {
 				}
 			}
 		}
-			_system->warpMouse(_centerX, _centerY);
-
-			_gfx->clear(_gfx->black());
-			for (uint i = 0; i < _objects.size(); i++)
-				_objects[i].visible = 0;
-			
-				corridor();
-				drawStaticObjects();
-				drawDashboardStep1();
-				drawCrosshair();
-				
-				_gfx->copyToScreen();
+		_system->warpMouse(_centerX, _centerY);
+
+		_gfx->clear(_gfx->black());
+		for (uint i = 0; i < _objects.size(); i++)
+			_objects[i].visible = 0;
+		
+		corridor();
+		drawStaticObjects();
+		drawDashboardStep1();
+		drawCrosshair();
+		
+		_gfx->copyToScreen();
 		_system->delayMillis(10);
 	}
 


Commit: d2558d681cb02f1d1b9e89efc5f4a1c357da1d0d
    https://github.com/scummvm/scummvm/commit/d2558d681cb02f1d1b9e89efc5f4a1c357da1d0d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:05+02:00

Commit Message:
COLONY: split colony.cpp into render.cpp and ui.cpp

Changed paths:
  A engines/colony/render.cpp
  A engines/colony/ui.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/module.mk


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index e96b3567a23..c533b2a292b 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -70,74 +70,6 @@ static const int16 g_sintTable[256] = {
 	71,  74,  76,  79,  81,  84,  86,  88
 };
 
-static const int g_indexTable[4][10] = {
-	{0, 0,  0, 0,  0,  1,  1,  0,  1, 2},
-	{1, 0,  0, 0, -1,  0,  0,  1,  2, 1},
-	{0, 1,  1, 0,  0, -1, -1,  0,  1, 2},
-	{0, 0,  0, 1,  1,  0,  0, -1,  2, 1}
-};
-
-enum WallFeatureType {
-	kWallFeatureNone = 0,
-	kWallFeatureDoor = 2,
-	kWallFeatureWindow = 3,
-	kWallFeatureShelves = 4,
-	kWallFeatureUpStairs = 5,
-	kWallFeatureDnStairs = 6,
-	kWallFeatureChar = 7,
-	kWallFeatureGlyph = 8,
-	kWallFeatureElevator = 9,
-	kWallFeatureTunnel = 10,
-	kWallFeatureAirlock = 11,
-	kWallFeatureColor = 12
-};
-
-enum MapDirection {
-	kDirNorth = 0,
-	kDirEast = 1,
-	kDirWest = 2,
-	kDirSouth = 3
-};
-
-static const int g_dirRight[4] = {1, 3, 0, 2};
-static const int g_dirLeft[4] = {2, 0, 3, 1};
-
-enum ObjectType {
-	kObjDesk = 21,
-	kObjPlant = 22,
-	kObjCChair = 23,
-	kObjBed = 24,
-	kObjTable = 25,
-	kObjCouch = 26,
-	kObjChair = 27,
-	kObjTV = 28,
-	kObjScreen = 29,
-	kObjConsole = 30,
-	kObjPowerSuit = 31,
-	kObjForkLift = 32,
-	kObjCryo = 33,
-	kObjBox1 = 34,
-	kObjBox2 = 35,
-	kObjTeleport = 36,
-	kObjDrawer = 37,
-	kObjTub = 38,
-	kObjSink = 39,
-	kObjToilet = 40,
-	kObjPToilet = 43,
-	kObjProjector = 45,
-	kObjReactor = 46,
-	kObjFWall = 48,
-	kObjCWall = 49,
-	kObjBBed = 42
-};
-
-struct ColonyEngine::PrismPartDef {
-	int pointCount;
-	const int (*points)[3];
-	int surfaceCount;
-	const int (*surfaces)[8];
-};
-
 ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd) {
 	_level = 0;
 	_robotNum = 0;
@@ -268,2638 +200,6 @@ void ColonyEngine::initTrig() {
 	}
 }
 
-void ColonyEngine::rot_init(int x, int y) {
-	_rox = ((long)x * _tsin - (long)y * _tcos) >> 8;
-	_roy = ((long)y * _tsin + (long)x * _tcos) >> 8;
-}
-
-void ColonyEngine::perspective(int pnt[2], int rox, int roy) {
-	long p;
-
-	if (roy <= 0)
-		roy = 1;
-	p = _centerX + ((long)rox * 256) / roy;
-
-	if (p < -32000)
-		p = -32000;
-	else if (p > 32000)
-		p = 32000;
-	pnt[0] = (int)p;
-
-	if (_flip)
-		pnt[1] = _centerY + _rtable[roy];
-	else
-		pnt[1] = _centerY - _rtable[roy];
-}
-
-int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
-	if (x < 0 || x >= 32 || y < 0 || y >= 32)
-		return -1;
-	const int rnum = _robotArray[x][y];
-	if (rnum <= 0)
-		return 0;
-	if (pobject == &_me && rnum <= (int)_objects.size()) {
-		Thing &obj = _objects[rnum - 1];
-		if (obj.type <= BASEOBJECT)
-			obj.where.look = obj.where.ang = _me.ang + 128;
-	}
-	return rnum;
-}
-
-int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
-	const int xind2 = xnew >> 8;
-	const int yind2 = ynew >> 8;
-	_change = true;
-
-	auto occupied = [&]() -> int {
-		return occupiedObjectAt(xind2, yind2, pobject);
-	};
-	auto moveTo = [&]() -> int {
-		const int rnum = occupied();
-		if (rnum)
-			return rnum;
-		pobject->yindex = yind2;
-		pobject->xindex = xind2;
-		pobject->dx = xnew - pobject->xloc;
-		pobject->dy = ynew - pobject->yloc;
-		pobject->xloc = xnew;
-		pobject->yloc = ynew;
-		return 0;
-	};
-
-	if (xind2 == pobject->xindex) {
-		if (yind2 == pobject->yindex) {
-			pobject->dx = xnew - pobject->xloc;
-			pobject->dy = ynew - pobject->yloc;
-			pobject->xloc = xnew;
-			pobject->yloc = ynew;
-			return 0;
-		}
-
-		if (yind2 > pobject->yindex) {
-			if (!(_wall[pobject->xindex][yind2] & 1))
-				return moveTo();
-			if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirNorth, pobject))
-				return moveTo();
-			debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
-			return -1;
-		}
-
-		if (!(_wall[pobject->xindex][pobject->yindex] & 1))
-			return moveTo();
-		if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirSouth, pobject))
-			return moveTo();
-		debug("Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
-		return -1;
-	}
-
-	if (yind2 == pobject->yindex) {
-		if (xind2 > pobject->xindex) {
-			if (!(_wall[xind2][pobject->yindex] & 2))
-				return moveTo();
-			if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirEast, pobject))
-				return moveTo();
-			debug("Collision East at x=%d y=%d", xind2, pobject->yindex);
-			return -1;
-		}
-
-		if (!(_wall[pobject->xindex][pobject->yindex] & 2))
-			return moveTo();
-		if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirWest, pobject))
-			return moveTo();
-		debug("Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
-		return -1;
-	}
-
-	// Diagonal
-	if (xind2 > pobject->xindex) {
-		if (yind2 > pobject->yindex) {
-			if ((_wall[pobject->xindex][yind2] & 1) || (_wall[xind2][pobject->yindex] & 2) || (_wall[xind2][yind2] & 3)) {
-				debug("Collision Diagonal SE");
-				return -1;
-			}
-		} else {
-			if ((_wall[pobject->xindex][pobject->yindex] & 1) || (_wall[xind2][yind2] & 2) || (_wall[xind2][pobject->yindex] & 3)) {
-				debug("Collision Diagonal NE");
-				return -1;
-			}
-		}
-	} else {
-		if (yind2 > pobject->yindex) {
-			if ((_wall[xind2][yind2] & 1) || (_wall[pobject->xindex][pobject->yindex] & 2) || (_wall[pobject->xindex][yind2] & 3)) {
-				debug("Collision Diagonal SW");
-				return -1;
-			}
-		} else {
-			if ((_wall[xind2][pobject->yindex] & 1) || (_wall[pobject->xindex][yind2] & 2) || (_wall[pobject->xindex][pobject->yindex] & 3)) {
-				debug("Collision Diagonal NW");
-				return -1;
-			}
-		}
-	}
-
-	return moveTo();
-}
-
-bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
-	if (x < 0 || x >= 31 || y < 0 || y >= 31 || direction < 0 || direction > 3)
-		return false;
-
-	const uint8 wallType = _mapData[x][y][direction][0];
-	if (wallType != kWallFeatureDoor && wallType != kWallFeatureAirlock)
-		return false;
-
-	_mapData[x][y][direction][1] = (uint8)state;
-
-	int nx = x;
-	int ny = y;
-	int opposite = -1;
-	switch (direction) {
-	case kDirNorth:
-		ny += 1;
-		opposite = kDirSouth;
-		break;
-	case kDirEast:
-		nx += 1;
-		opposite = kDirWest;
-		break;
-	case kDirWest:
-		nx -= 1;
-		opposite = kDirEast;
-		break;
-	case kDirSouth:
-		ny -= 1;
-		opposite = kDirNorth;
-		break;
-	default:
-		return true;
-	}
-
-	if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31 && opposite >= 0) {
-		if (_mapData[nx][ny][opposite][0] == wallType)
-			_mapData[nx][ny][opposite][1] = (uint8)state;
-	}
-
-	return true;
-}
-
-int ColonyEngine::openAdjacentDoors(int x, int y) {
-	int opened = 0;
-	for (int dir = 0; dir < 4; dir++) {
-		const uint8 *map = mapFeatureAt(x, y, dir);
-		if (!map)
-			continue;
-		if (map[0] == kWallFeatureDoor && map[1] != 0) {
-			if (setDoorState(x, y, dir, 0))
-				opened++;
-		}
-	}
-	return opened;
-}
-
-bool ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject) {
-	const uint8 *map = mapFeatureAt(fromX, fromY, direction);
-	if (!map || map[0] == kWallFeatureNone)
-		return false;
-
-	switch (map[0]) {
-	case kWallFeatureDoor:
-		if (map[1] == 0)
-			return true;
-		if (pobject != &_me)
-			return false;
-		if (_hasKeycard || _unlocked) {
-			setDoorState(fromX, fromY, direction, 0);
-			return true;
-		}
-		return false;
-	case kWallFeatureAirlock:
-		if (map[1] == 0)
-			return true;
-		if (pobject == &_me && _unlocked) {
-			setDoorState(fromX, fromY, direction, 0);
-			return true;
-		}
-		return false;
-	default:
-		return false;
-	}
-}
-
-void ColonyEngine::interactWithObject(int objNum) {
-	if (objNum <= 0 || objNum > (int)_objects.size())
-		return;
-
-	const Thing &obj = _objects[objNum - 1];
-	if (!obj.alive)
-		return;
-
-	const int x = CLIP<int>(obj.where.xindex, 0, 30);
-	const int y = CLIP<int>(obj.where.yindex, 0, 30);
-	const int action0 = _mapData[x][y][4][3];
-	const int action1 = _mapData[x][y][4][4];
-
-	switch (obj.type) {
-	case kObjDesk:
-		if (!_hasKeycard) {
-			_hasKeycard = true;
-			debug("CCommand: DESK granted keycard");
-		} else {
-			debug("CCommand: DESK action (%d, %d)", action0, action1);
-		}
-		break;
-	case kObjConsole:
-		switch (action0) {
-		case 1:
-			debug("CCommand: CONSOLE reactor (%d)", action1);
-			break;
-		case 2:
-		{
-			const int opened = openAdjacentDoors(_me.xindex, _me.yindex);
-			debug("CCommand: CONSOLE controls opened %d nearby doors", opened);
-			break;
-		}
-		case 3:
-			_unlocked = true;
-			debug("CCommand: CONSOLE security unlocked");
-			break;
-		default:
-			debug("CCommand: CONSOLE action=%d", action0);
-			break;
-		}
-		break;
-	case kObjProjector:
-		switch (action0) {
-		case 1:
-			debug("CCommand: PROJECTOR creatures");
-			break;
-		case 2:
-			debug("CCommand: PROJECTOR teleporters");
-			break;
-		default:
-			debug("CCommand: PROJECTOR action=%d", action0);
-			break;
-		}
-		break;
-	case kObjPowerSuit:
-		_weapons = MAX(_weapons, 1);
-		_crosshair = true;
-		debug("CCommand: POWERSUIT");
-		break;
-	case kObjTeleport:
-	{
-		const int targetLevelRaw = _mapData[x][y][4][2];
-		const int targetLevel = (targetLevelRaw == 0) ? _level : targetLevelRaw;
-		const int targetX = _mapData[x][y][4][3];
-		const int targetY = _mapData[x][y][4][4];
-		if (targetLevel >= 100 || targetX <= 0 || targetX >= 31 || targetY <= 0 || targetY >= 31) {
-			debug("CCommand: TELEPORT ignored invalid target L%d (%d,%d)", targetLevelRaw, targetX, targetY);
-			break;
-		}
-		if (targetLevel != _level)
-			loadMap(targetLevel);
-		const int oldX = _me.xindex;
-		const int oldY = _me.yindex;
-		_me.xindex = targetX;
-		_me.yindex = targetY;
-		_me.xloc = (targetX << 8) + 128;
-		_me.yloc = (targetY << 8) + 128;
-		_me.ang = _me.look;
-		if (oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32)
-			_robotArray[oldX][oldY] = 0;
-		if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-			_robotArray[_me.xindex][_me.yindex] = MENUM;
-		_change = true;
-		debug("CCommand: TELEPORT to L%d (%d,%d)", targetLevel, targetX, targetY);
-		break;
-	}
-	case kObjDrawer:
-		if (!_hasKeycard) {
-			_hasKeycard = true;
-			debug("CCommand: DRAWER vanity=%d (picked keycard)", action0);
-		} else {
-			debug("CCommand: DRAWER vanity=%d", action0);
-		}
-		break;
-	case kObjScreen:
-		debug("CCommand: SCREEN");
-		break;
-	case kObjToilet:
-	case kObjPToilet:
-		debug("CCommand: TOILET");
-		break;
-	case kObjTub:
-		debug("CCommand: TUB");
-		break;
-	case kObjSink:
-		debug("CCommand: SINK");
-		break;
-	case kObjCryo:
-		debug("CCommand: CRYO text=%d", action0);
-		break;
-	case kObjTV:
-		debug("CCommand: TV level=%d", _level);
-		break;
-	case kObjForkLift:
-	case kObjReactor:
-	case kObjBox1:
-	case kObjBox2:
-		debug("CCommand: object type %d requires forklift/reactor flow", obj.type);
-		break;
-	case kObjPlant:
-	case kObjCChair:
-	case kObjBed:
-	case kObjTable:
-	case kObjCouch:
-	case kObjChair:
-	case kObjBBed:
-	case kObjFWall:
-	case kObjCWall:
-		// Matches DOS CCommand where these objects are non-interactive blockers.
-		break;
-	default:
-		debug("CCommand: object type %d", obj.type);
-		break;
-	}
-}
-
-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 robot = checkwall(xnew, ynew, &_me);
-	if (robot > 0 && allowInteraction)
-		interactWithObject(robot);
-
-	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-		_robotArray[_me.xindex][_me.yindex] = MENUM;
-}
-
-void ColonyEngine::quadrant() {
-	int remain;
-	int quad;
-
-	quad = _me.look >> 6;				/*divide by 64		*/
-	remain = _me.look - (quad << 6);			/*multiply by 64	*/
-	_tsin = _sint[remain];
-	_tcos = _cost[remain];
-
-	switch (quad) {
-	case 0:
-		rot_init((_me.xindex << 8) - _me.xloc, (_me.yindex << 8) - _me.yloc);
-		_direction = 0; // NORTH
-		break;
-	case 1:
-		rot_init((_me.yindex << 8) - _me.yloc, _me.xloc - ((_me.xindex + 1) << 8));
-		_direction = 2; // WEST
-		break;
-	case 2:
-		rot_init(_me.xloc - ((_me.xindex + 1) << 8), _me.yloc - ((_me.yindex + 1) << 8));
-		_direction = 3; // SOUTH
-		break;
-	case 3:
-		rot_init(_me.yloc - ((_me.yindex + 1) << 8), (_me.xindex << 8) - _me.xloc);
-		_direction = 1; // EAST
-		break;
-	}
-
-	_frntxWall = g_indexTable[quad][0];
-	_frntyWall = g_indexTable[quad][1];
-	_sidexWall = g_indexTable[quad][2];
-	_sideyWall = g_indexTable[quad][3];
-	_frntx = g_indexTable[quad][4];
-	_frnty = g_indexTable[quad][5];
-	_sidex = g_indexTable[quad][6];
-	_sidey = g_indexTable[quad][7];
-	_front = g_indexTable[quad][8];
-	_side = g_indexTable[quad][9];
-}
-
-void ColonyEngine::corridor() {
-	int length = 1;
-	int xFrontLeft, yFrontLeft;
-	int xFrontRight, yFrontRight;
-	int xsstart, ysstart;
-	int xfbehind, yfbehind;
-	int roxsave, roysave;
-	int left, right;
-	int left2, right2;
-	int cellx, celly;
-	int cellxsave, cellysave;
-	int dr[2];
-	const int screenLeft = (int)_screenR.left;
-	const int screenRight = (int)_screenR.right;
-
-	quadrant();
-
-	right = screenRight;
-	left = screenLeft;
-	right2 = right;
-	left2 = left;
-
-	xfbehind = _me.xindex + _frntxWall;
-	yfbehind = _me.yindex + _frntyWall;
-	xFrontLeft = xfbehind + _frntx;
-	yFrontLeft = yfbehind + _frnty;
-	xFrontRight = xFrontLeft + _sidex;
-	yFrontRight = yFrontLeft + _sidey;
-	xsstart = _me.xindex + _sidexWall;
-	ysstart = _me.yindex + _sideyWall;
-	cellxsave = cellx = _me.xindex;
-	cellysave = celly = _me.yindex;
-
-	int rox = _rox;
-	int roy = _roy;
-
-	if (_change) {
-		perspective(dr, rox, roy);
-		if (xfbehind >= 0 && xfbehind < 34 && yfbehind >= 0 && yfbehind < 34) {
-			_drY[xfbehind][yfbehind] = dr[1];
-			if (dr[0] > _screenR.left)
-				_drX[xfbehind][yfbehind] = -32000;
-			else
-				_drX[xfbehind][yfbehind] = dr[0];
-		}
-
-		perspective(dr, rox + _tsin, roy + _tcos);
-		if (xfbehind + _sidex >= 0 && xfbehind + _sidex < 34 && yfbehind + _sidey >= 0 && yfbehind + _sidey < 34) {
-			_drY[xfbehind + _sidex][yfbehind + _sidey] = dr[1];
-			if (dr[0] < _screenR.right)
-				_drX[xfbehind + _sidex][yfbehind + _sidey] = 32000;
-			else
-				_drX[xfbehind + _sidex][yfbehind + _sidey] = dr[0];
-		}
-	}
-
-	roxsave = rox;
-	roysave = roy;
-
-	// Move to the first wall in front of the observer.
-	rox -= _tcos;
-	roy += _tsin;
-
-	if (_change) {
-		perspective(dr, rox, roy);
-		if (xFrontLeft >= 0 && xFrontLeft < 34 && yFrontLeft >= 0 && yFrontLeft < 34) {
-			_drX[xFrontLeft][yFrontLeft] = dr[0];
-			_drY[xFrontLeft][yFrontLeft] = dr[1];
-		}
-		perspective(dr, rox + _tsin, roy + _tcos);
-		if (xFrontRight >= 0 && xFrontRight < 34 && yFrontRight >= 0 && yFrontRight < 34) {
-			_drX[xFrontRight][yFrontRight] = dr[0];
-			_drY[xFrontRight][yFrontRight] = dr[1];
-		}
-	}
-
-	if (wallAt(cellx + _sidexWall, celly + _sideyWall) & _side)
-		left2 = MAX(_drX[xFrontLeft][yFrontLeft], screenLeft);
-	else
-		left2 = MAX(left, left2);
-	left2 = MAX(left2, screenLeft);
-
-	if (wallAt(cellx + _sidexWall + _sidex, celly + _sideyWall + _sidey) & _side)
-		right2 = _drX[xFrontRight][yFrontRight];
-	else
-		right2 = MIN(right, right2);
-
-	uint32 white = _gfx->white();
-	_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
-	               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
-	if (wallAt(cellx, celly) & ~0x03)
-		frontfeature(cellx, celly, xFrontLeft, yFrontLeft, left2, right2, rox, roy);
-
-	while (!(wallAt(xFrontLeft, yFrontLeft) & _front)) {
-		rox -= _tcos;
-		roy += _tsin;
-		xFrontLeft += _frntx;
-		yFrontLeft += _frnty;
-		xFrontRight += _frntx;
-		yFrontRight += _frnty;
-		if (_change) {
-			perspective(dr, rox, roy);
-			if (xFrontLeft >= 0 && xFrontLeft < 34 && yFrontLeft >= 0 && yFrontLeft < 34) {
-				_drX[xFrontLeft][yFrontLeft] = dr[0];
-				_drY[xFrontLeft][yFrontLeft] = dr[1];
-			}
-			perspective(dr, rox + _tsin, roy + _tcos);
-			if (xFrontRight >= 0 && xFrontRight < 34 && yFrontRight >= 0 && yFrontRight < 34) {
-				_drX[xFrontRight][yFrontRight] = dr[0];
-				_drY[xFrontRight][yFrontRight] = dr[1];
-			}
-		}
-
-		cellx += _frntx;
-		celly += _frnty;
-		if (wallAt(cellx + _sidexWall, celly + _sideyWall) & _side)
-			left2 = MAX(screenLeft, _drX[xFrontLeft][yFrontLeft]);
-		else
-			left2 = MAX(left, left2);
-		left2 = MAX(left2, screenLeft);
-		if (wallAt(cellx + _sidexWall + _sidex, celly + _sideyWall + _sidey) & _side)
-			right2 = _drX[xFrontRight][yFrontRight];
-		else
-			right2 = MIN(right, right2);
-		if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
-			if (_robotArray[cellx][celly])
-				setRobot(left2, right2, _robotArray[cellx][celly]);
-			if (_foodArray[cellx][celly])
-				setRobot(left2, right2, _foodArray[cellx][celly]);
-		}
-		if (wallAt(cellx, celly) & ~0x03)
-			features(cellx, celly, xFrontLeft, yFrontLeft, left2, right2, rox, roy);
-
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
-
-		length++;
-		if (length > 30)
-			break; // Safety break
-	}
-	drawend(xfbehind, yfbehind, xFrontLeft, yFrontLeft);
-
-	left = screenLeft;
-	right = MIN(right, _drX[xFrontLeft][yFrontLeft]);
-	if (left < right)
-		checkleft(xsstart, ysstart, xfbehind, yfbehind, left, right, roxsave, roysave, cellxsave, cellysave, length);
-
-	left = MAX(left, _drX[xFrontRight][yFrontRight]);
-	if (left < screenLeft)
-		left = screenLeft;
-	right = screenRight;
-	xsstart += _sidex;
-	ysstart += _sidey;
-	xfbehind += _sidex;
-	yfbehind += _sidey;
-	if (left < right)
-		checkright(xsstart, ysstart, xfbehind, yfbehind, left, right, roxsave + _tsin, roysave + _tcos, cellxsave, cellysave, length);
-
-	_change = false;
-}
-
-void ColonyEngine::drawend(int xstart, int ystart, int xFrontLeft, int yFrontLeft) {
-	int xFrontRight, yFrontRight;
-
-	xFrontRight = xFrontLeft + _sidex;
-	yFrontRight = yFrontLeft + _sidey;
-
-	uint32 white = _gfx->white();
-
-	if ((xstart != xFrontLeft) || (ystart != yFrontLeft)) {
-		if (_drY[xstart + _frntx][ystart + _frnty] > 0) {
-			_gfx->drawLine(_drX[xstart][ystart], _drY[xstart][ystart],
-			               _drX[xstart + _frntx][ystart + _frnty], _drY[xstart + _frntx][ystart + _frnty], white);
-		}
-		_gfx->drawLine(_drX[xstart + _frntx][ystart + _frnty], _drY[xstart + _frntx][ystart + _frnty],
-		               _drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft], white);
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
-		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight],
-		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
-		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight],
-		               _drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey], white);
-		if (_drY[xstart + _sidex][ystart + _sidey] > 0) {
-			_gfx->drawLine(_drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey],
-			               _drX[xstart + _frntx + _sidex][ystart + _frnty + _sidey], _drY[xstart + _frntx + _sidex][ystart + _frnty + _sidey], white);
-		}
-	} else {
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
-		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight],
-		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
-	}
-}
-
-uint8 ColonyEngine::wallAt(int x, int y) const {
-	if (x < 0 || x >= 32 || y < 0 || y >= 32)
-		return 3;
-	return _wall[x][y];
-}
-
-void ColonyEngine::checkleft(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len) {
-	int i = 0, j;
-	int xf2, yf2;
-	int rox, roy;
-	int xsstart, ysstart;
-	int xfstart, yfstart;
-	int xestart, yestart;
-	int cellxsave, cellysave;
-	int dr[2];
-	uint32 white = _gfx->white();
-
-	cellx -= _sidex;
-	celly -= _sidey;
-	rx = rx - _tsin;
-	ry = ry - _tcos;
-
-	while (i < len && left <= right) {
-		if (wallAt(xs, ys) & _side) {
-			if (_flip)
-				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-
-			while ((wallAt(xs, ys) & _side) && i < len && left <= right) {
-				_gfx->drawLine(_drX[xf][yf], _drY[xf][yf], _drX[xf][yf], _height - _drY[xf][yf], white);
-				_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf],
-				               _drX[xf + _frntx][yf + _frnty], _height - _drY[xf + _frntx][yf + _frnty], white);
-
-				left = MAX(_drX[xf][yf], left);
-				xf += _frntx;
-				yf += _frnty;
-				xs += _frntx;
-				ys += _frnty;
-				cellx += _frntx;
-				celly += _frnty;
-				rx -= _tcos;
-				ry += _tsin;
-				i++;
-			}
-
-			if (_flip)
-				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-			_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf], _drX[xf][yf], _drY[xf][yf], white);
-			left = MAX(_drX[xf][yf], left);
-		}
-
-		if (i < len && left <= right) {
-			j = 0;
-			xf2 = xf - _sidex;
-			yf2 = yf - _sidey;
-			xfstart = xf2;
-			yfstart = yf2;
-			xsstart = xs - _sidex;
-			ysstart = ys - _sidey;
-			cellxsave = cellx;
-			cellysave = celly;
-
-			rox = rx;
-			roy = ry;
-			if (_change) {
-				perspective(dr, rx, ry);
-				_drX[xf2][yf2] = dr[0];
-				_drY[xf2][yf2] = dr[1];
-			}
-
-			while (!(wallAt(xs, ys) & _side) && i < len) {
-				rx -= _tcos;
-				ry += _tsin;
-				if (_change) {
-					perspective(dr, rx, ry);
-					_drX[xf2 + _frntx][yf2 + _frnty] = dr[0];
-					_drY[xf2 + _frntx][yf2 + _frnty] = dr[1];
-				}
-
-				if (_drX[xf + _frntx][yf + _frnty] > left) {
-					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2],
-					               _drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty], white);
-					_gfx->drawLine(_drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty],
-					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
-					if (wallAt(cellx, celly) & ~0x03)
-						features(cellx, celly, xf2 + _frntx, yf2 + _frnty, left, right, rx, ry);
-					if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
-						if (_robotArray[cellx][celly])
-							setRobot(left, right, _robotArray[cellx][celly]);
-						if (_foodArray[cellx][celly])
-							setRobot(left, right, _foodArray[cellx][celly]);
-					}
-				} else {
-					j = 0;
-					xfstart = xf2;
-					yfstart = yf2;
-					xsstart = xs - _sidex;
-					ysstart = ys - _sidey;
-					rox = rx + _tcos;
-					roy = ry - _tsin;
-					cellxsave = cellx;
-					cellysave = celly;
-				}
-
-				xf2 += _frntx;
-				yf2 += _frnty;
-				xf += _frntx;
-				yf += _frnty;
-				xs += _frntx;
-				ys += _frnty;
-				cellx += _frntx;
-				celly += _frnty;
-				i++;
-				j++;
-			}
-
-			if (wallAt(xf - _sidex, yf - _sidey) & _front) {
-				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2], _drX[xf][yf], _height - _drY[xf][yf], white);
-
-				if (MIN(_drX[xf2][yf2], right) >= left) {
-					checkleft(xsstart, ysstart, xfstart, yfstart, left, MIN(right, _drX[xf2][yf2]),
-					          rox, roy, cellxsave, cellysave, j);
-				}
-			} else {
-				if (_flip)
-					_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-				xestart = xf2;
-				yestart = yf2;
-
-				while (!(wallAt(xf2, yf2) & _front)) {
-					rx -= _tcos;
-					ry += _tsin;
-					cellx += _frntx;
-					celly += _frnty;
-					xf2 += _frntx;
-					yf2 += _frnty;
-					xf += _frntx;
-					yf += _frnty;
-					xs += _frntx;
-					ys += _frnty;
-					if (_change) {
-						perspective(dr, rx, ry);
-						_drX[xf2][yf2] = dr[0];
-						_drY[xf2][yf2] = dr[1];
-					}
-					if (_change) {
-						perspective(dr, rx + _tsin, ry + _tcos);
-						_drX[xf][yf] = dr[0];
-						_drY[xf][yf] = dr[1];
-					}
-					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
-					if (wallAt(cellx - _frntx, celly - _frnty) & ~0x03)
-						features(cellx - _frntx, celly - _frnty, xf2, yf2, left, right, rx, ry);
-					const int objxL = cellx - _frntx;
-					const int objyL = celly - _frnty;
-					if (objxL >= 0 && objxL < 32 && objyL >= 0 && objyL < 32) {
-						if (_robotArray[objxL][objyL])
-							setRobot(left, right, _robotArray[objxL][objyL]);
-						if (_foodArray[objxL][objyL])
-							setRobot(left, right, _foodArray[objxL][objyL]);
-					}
-					i++;
-					j++;
-				}
-
-				_gfx->drawLine(_drX[xestart][yestart], _drY[xestart][yestart], _drX[xf2][yf2], _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2],
-				               _drX[xf2 + _sidex][yf2 + _sidey], _height - _drY[xf2 + _sidex][yf2 + _sidey], white);
-
-				if (MIN(_drX[xf2][yf2], right) >= left) {
-					checkleft(xsstart, ysstart, xfstart, yfstart, left, MIN(_drX[xf2][yf2], right),
-					          rox, roy, cellxsave, cellysave, j);
-				}
-			}
-		}
-	}
-}
-
-void ColonyEngine::checkright(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len) {
-	int i = 0, j;
-	int xf2, yf2;
-	int rox, roy;
-	int xsstart, ysstart;
-	int xfstart, yfstart;
-	int xestart, yestart;
-	int cellxsave, cellysave;
-	int dr[2];
-	uint32 white = _gfx->white();
-
-	cellx += _sidex;
-	celly += _sidey;
-	rx = rx + _tsin;
-	ry = ry + _tcos;
-
-	while (i < len && left < right) {
-		if (wallAt(xs, ys) & _side) {
-			if (_flip)
-				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-
-			while ((wallAt(xs, ys) & _side) && i < len && left < right) {
-				_gfx->drawLine(_drX[xf][yf], _drY[xf][yf], _drX[xf][yf], _height - _drY[xf][yf], white);
-				_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf],
-				               _drX[xf + _frntx][yf + _frnty], _height - _drY[xf + _frntx][yf + _frnty], white);
-
-				right = MIN(_drX[xf][yf], right);
-				xf += _frntx;
-				yf += _frnty;
-				xs += _frntx;
-				ys += _frnty;
-				cellx += _frntx;
-				celly += _frnty;
-				rx -= _tcos;
-				ry += _tsin;
-				i++;
-			}
-
-			if (_flip)
-				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-			_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf], _drX[xf][yf], _drY[xf][yf], white);
-			right = MIN(_drX[xf][yf], right);
-		}
-
-		if (i < len && left < right) {
-			j = 0;
-			xf2 = xf + _sidex;
-			yf2 = yf + _sidey;
-			xfstart = xf2;
-			yfstart = yf2;
-			xsstart = xs + _sidex;
-			ysstart = ys + _sidey;
-			cellxsave = cellx;
-			cellysave = celly;
-
-			rox = rx;
-			roy = ry;
-			if (_change) {
-				perspective(dr, rx, ry);
-				_drX[xf2][yf2] = dr[0];
-				_drY[xf2][yf2] = dr[1];
-			}
-
-			while (!(wallAt(xs, ys) & _side) && i < len) {
-				rx -= _tcos;
-				ry += _tsin;
-				if (_change) {
-					perspective(dr, rx, ry);
-					_drX[xf2 + _frntx][yf2 + _frnty] = dr[0];
-					_drY[xf2 + _frntx][yf2 + _frnty] = dr[1];
-				}
-
-				if (_drX[xf + _frntx][yf + _frnty] < right) {
-					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2],
-					               _drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty], white);
-					_gfx->drawLine(_drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty],
-					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
-					if (wallAt(cellx, celly) & ~0x03)
-						features(cellx, celly, xf + _frntx, yf + _frnty, left, right, rx - _tsin, ry - _tcos);
-					if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
-						if (_robotArray[cellx][celly])
-							setRobot(left, right, _robotArray[cellx][celly]);
-						if (_foodArray[cellx][celly])
-							setRobot(left, right, _foodArray[cellx][celly]);
-					}
-				} else {
-					j = 0;
-					xfstart = xf2;
-					yfstart = yf2;
-					xsstart = xs + _sidex;
-					ysstart = ys + _sidey;
-					rox = rx + _tcos;
-					roy = ry - _tsin;
-					cellxsave = cellx;
-					cellysave = celly;
-				}
-
-				xf2 += _frntx;
-				yf2 += _frnty;
-				xf += _frntx;
-				yf += _frnty;
-				xs += _frntx;
-				ys += _frnty;
-				cellx += _frntx;
-				celly += _frnty;
-				i++;
-				j++;
-			}
-
-			if (wallAt(xf, yf) & _front) {
-				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2], _drX[xf][yf], _height - _drY[xf][yf], white);
-
-				if (MAX(_drX[xf2][yf2], left) < right) {
-					checkright(xsstart, ysstart, xfstart, yfstart, MAX(left, _drX[xf2][yf2]), right,
-					           rox, roy, cellxsave, cellysave, j);
-				}
-			} else {
-				if (_flip)
-					_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-				xestart = xf2;
-				yestart = yf2;
-
-				while (!(wallAt(xf, yf) & _front)) {
-					rx -= _tcos;
-					ry += _tsin;
-					cellx += _frntx;
-					celly += _frnty;
-					xf2 += _frntx;
-					yf2 += _frnty;
-					xf += _frntx;
-					yf += _frnty;
-					xs += _frntx;
-					ys += _frnty;
-					if (_change) {
-						perspective(dr, rx, ry);
-						_drX[xf2][yf2] = dr[0];
-						_drY[xf2][yf2] = dr[1];
-					}
-					if (_change) {
-						perspective(dr, rx - _tsin, ry - _tcos);
-						_drX[xf][yf] = dr[0];
-						_drY[xf][yf] = dr[1];
-					}
-					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
-					if (wallAt(cellx - _frntx, celly - _frnty) & ~0x03)
-						features(cellx - _frntx, celly - _frnty, xf, yf, left, right, rx - _tsin, ry - _tcos);
-					const int objxR = cellx - _frntx;
-					const int objyR = celly - _frnty;
-					if (objxR >= 0 && objxR < 32 && objyR >= 0 && objyR < 32) {
-						if (_robotArray[objxR][objyR])
-							setRobot(left, right, _robotArray[objxR][objyR]);
-						if (_foodArray[objxR][objyR])
-							setRobot(left, right, _foodArray[objxR][objyR]);
-					}
-					i++;
-					j++;
-				}
-
-				_gfx->drawLine(_drX[xestart][yestart], _drY[xestart][yestart], _drX[xf2][yf2], _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2],
-				               _drX[xf2 - _sidex][yf2 - _sidey], _height - _drY[xf2 - _sidex][yf2 - _sidey], white);
-
-				if (MAX(_drX[xf2][yf2], left) < right) {
-					checkright(xsstart, ysstart, xfstart, yfstart, MAX(_drX[xf2][yf2], left), right,
-					           rox, roy, cellxsave, cellysave, j);
-				}
-			}
-		}
-	}
-}
-
-const uint8 *ColonyEngine::mapFeatureAt(int x, int y, int direction) const {
-	if (x < 0 || x >= 31 || y < 0 || y >= 31 || direction < 0 || direction >= 5)
-		return nullptr;
-	return _mapData[x][y][direction];
-}
-
-void ColonyEngine::frontfeature(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry) {
-	int l[4], r[4];
-
-	l[0] = _drX[xFront][yFront];
-	l[2] = rx - _tcos;
-	l[3] = ry + _tsin;
-	r[0] = _drX[xFront + _sidex][yFront + _sidey];
-	r[2] = rx + _tsin - _tcos;
-	r[3] = ry + _tsin + _tcos;
-	if (_flip) {
-		l[1] = _height - _drY[xFront][yFront];
-		r[1] = _height - _drY[xFront + _sidex][yFront + _sidey];
-	} else {
-		l[1] = _drY[xFront][yFront];
-		r[1] = _drY[xFront + _sidex][yFront + _sidey];
-	}
-
-	if (MAX(left, l[0]) < MIN(right, r[0])) {
-		const uint8 *map = mapFeatureAt(cellx, celly, _direction);
-		if (map && map[0])
-			dowall(cellx, celly, _direction, l, r);
-	}
-}
-
-void ColonyEngine::features(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry) {
-	int l[4], r[4], ll[4], rr[4];
-
-	l[0] = _drX[xFront][yFront];
-	l[2] = rx - _tcos;
-	l[3] = ry + _tsin;
-	r[0] = _drX[xFront + _sidex][yFront + _sidey];
-	r[2] = rx + _tsin - _tcos;
-	r[3] = ry + _tsin + _tcos;
-	if (_flip) {
-		l[1] = _height - _drY[xFront][yFront];
-		r[1] = _height - _drY[xFront + _sidex][yFront + _sidey];
-	} else {
-		l[1] = _drY[xFront][yFront];
-		r[1] = _drY[xFront + _sidex][yFront + _sidey];
-	}
-
-	if (MAX(left, l[0]) + 1 < MIN(right, r[0]) - 1) {
-		const uint8 *map = mapFeatureAt(cellx, celly, _direction);
-		if (map && map[0])
-			dowall(cellx, celly, _direction, l, r);
-	}
-
-	ll[0] = r[0];
-	ll[1] = r[1];
-	ll[2] = rx + _tsin + _tsin;
-	ll[3] = ry + _tcos + _tcos;
-	rr[0] = _drX[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
-	if (_flip)
-		rr[1] = _height - _drY[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
-	else
-		rr[1] = _drY[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
-	rr[2] = rx + _tsin + _tsin + _tcos;
-	rr[3] = ry + _tcos + _tcos - _tsin;
-	if (MAX(left, ll[0]) + 1 < MIN(right, rr[0]) - 1) {
-		const uint8 *map = mapFeatureAt(cellx, celly, g_dirRight[_direction]);
-		if (map && map[0])
-			dowall(cellx, celly, g_dirRight[_direction], ll, rr);
-	}
-
-	ll[0] = _drX[xFront - _frntx][yFront - _frnty];
-	if (_flip)
-		ll[1] = _height - _drY[xFront - _frntx][yFront - _frnty];
-	else
-		ll[1] = _drY[xFront - _frntx][yFront - _frnty];
-	ll[2] = rx + _tcos - _tsin;
-	ll[3] = (ry - _tcos) - _tsin;
-	rr[0] = l[0];
-	rr[1] = l[1];
-	rr[2] = rx - _tsin;
-	rr[3] = ry - _tcos;
-	if (MAX(left, ll[0]) + 1 < MIN(right, rr[0]) - 1) {
-		const uint8 *map = mapFeatureAt(cellx, celly, g_dirLeft[_direction]);
-		if (map && map[0])
-			dowall(cellx, celly, g_dirLeft[_direction], ll, rr);
-	}
-}
-
-void ColonyEngine::dowall(int cellx, int celly, int direction, int left[4], int right[4]) {
-	const uint8 *map = mapFeatureAt(cellx, celly, direction);
-	int left2[2], right2[2];
-	if (!map)
-		return;
-
-	switch (map[0]) {
-	case kWallFeatureDoor:
-		if (_level == 1 || _level == 5 || _level == 6) {
-			if (map[1] == 0)
-				drawOpenSSDoor(left, right);
-			else
-				drawClosedSSDoor(left, right);
-		} else {
-			if (map[1] == 0) {
-				perspective(left2, left[2], left[3]);
-				perspective(right2, right[2], right[3]);
-				if (_flip) {
-					left2[1] = _height - left2[1];
-					right2[1] = _height - right2[1];
-				}
-				drawOpenDoor(left, right, left2, right2);
-			} else {
-				drawClosedDoor(left, right);
-			}
-		}
-		break;
-	case kWallFeatureWindow:
-		drawWindow(left, right);
-		break;
-	case kWallFeatureShelves:
-		perspective(left2, left[2], left[3]);
-		perspective(right2, right[2], right[3]);
-		if (_flip) {
-			left2[1] = _height - left2[1];
-			right2[1] = _height - right2[1];
-		}
-		drawBooks(left, right, left2, right2);
-		break;
-	case kWallFeatureUpStairs:
-		perspective(left2, left[2], left[3]);
-		perspective(right2, right[2], right[3]);
-		if (_flip) {
-			left2[1] = _height - left2[1];
-			right2[1] = _height - right2[1];
-		}
-		drawUpStairs(left, right, left2, right2);
-		break;
-	case kWallFeatureDnStairs:
-		perspective(left2, left[2], left[3]);
-		perspective(right2, right[2], right[3]);
-		if (_flip) {
-			left2[1] = _height - left2[1];
-			right2[1] = _height - right2[1];
-		}
-		drawDnStairs(left, right, left2, right2);
-		break;
-	case kWallFeatureGlyph:
-		drawGlyphs(left, right);
-		break;
-	case kWallFeatureElevator:
-		drawElevator(left, right);
-		break;
-	case kWallFeatureTunnel:
-		perspective(left2, left[2], left[3]);
-		perspective(right2, right[2], right[3]);
-		if (_flip) {
-			left2[1] = _height - left2[1];
-			right2[1] = _height - right2[1];
-		}
-		drawTunnel(left, right, left2, right2);
-		break;
-	case kWallFeatureAirlock:
-		if (map[1] == 0)
-			drawALOpen(left, right);
-		else
-			drawALClosed(left, right);
-		break;
-	case kWallFeatureColor:
-		drawColor(map, left, right);
-		break;
-	default:
-		break;
-	}
-}
-
-void ColonyEngine::drawWindow(int left[4], int right[4]) {
-	const uint32 dark = 160;
-	int x1 = left[0];
-	int x2 = right[0];
-	int y1 = left[1];
-	int y2 = right[1];
-	int y3 = _height - right[1];
-	int y4 = _height - left[1];
-	int xc = (x1 + x2) >> 1;
-	int xx1 = (xc + x1) >> 1;
-	int xx2 = (xc + x2) >> 1;
-	if (xx2 < _screenR.left || xx1 > _screenR.right)
-		return;
-	int yl = (y1 + y4) >> 1;
-	int yr = (y2 + y3) >> 1;
-	int yy1 = (yl + y1) >> 1;
-	int yy2 = (yr + y2) >> 1;
-	int yy3 = (yl + y3) >> 1;
-	int yy4 = (yr + y4) >> 1;
-	int yy[4];
-	yy[0] = _height - ((((yy1 + yy2) >> 1) + yy1) >> 1);
-	yy[1] = _height - ((((yy1 + yy2) >> 1) + yy2) >> 1);
-	yy[2] = _height - ((((yy3 + yy4) >> 1) + yy3) >> 1);
-	yy[3] = _height - ((((yy3 + yy4) >> 1) + yy4) >> 1);
-	_gfx->drawLine(xx1, yy[0], xx2, yy[1], dark);
-	_gfx->drawLine(xx2, yy[1], xx2, yy[2], dark);
-	_gfx->drawLine(xx2, yy[2], xx1, yy[3], dark);
-	_gfx->drawLine(xx1, yy[3], xx1, yy[0], dark);
-	_gfx->drawLine(xc, (yy[0] + yy[1]) >> 1, xc, (yy[2] + yy[3]) >> 1, dark);
-	_gfx->drawLine(xx1, yl, xx2, yr, dark);
-}
-
-void ColonyEngine::drawClosedDoor(int left[4], int right[4]) {
-	const uint32 dark = 160;
-	int x1 = left[0];
-	int x2 = right[0];
-	int y1 = left[1];
-	int y2 = right[1];
-	int y3 = _height - right[1];
-	int y4 = _height - left[1];
-	int xc = (x1 + x2) >> 1;
-	int xx1 = (xc + x1) >> 1;
-	int xx2 = (xc + x2) >> 1;
-	if (xx2 < _screenR.left || xx1 > _screenR.right)
-		return;
-
-	int yc = (y1 + y2) >> 1;
-	int ytl = (yc + y1) >> 1;
-	int ytr = (yc + y2) >> 1;
-	yc = (y4 + y3) >> 1;
-	int ybl = (yc + y4) >> 1;
-	int ybr = (yc + y3) >> 1;
-	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
-	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
-
-	_gfx->drawLine(xx1, ybl, xx1, ytl, dark);
-	_gfx->drawLine(xx1, ytl, xx2, ytr, dark);
-	_gfx->drawLine(xx2, ytr, xx2, ybr, dark);
-	_gfx->drawLine(xx2, ybr, xx1, ybl, dark);
-
-	ybl = (ybl + ytl) >> 1;
-	ybr = (ybr + ytr) >> 1;
-	yc = (ybl + ybr) >> 1;
-	ybl = (((yc + ybl) >> 1) + ybl) >> 1;
-	ybr = (((yc + ybr) >> 1) + ybr) >> 1;
-	xx1 = (((xx1 + xc) >> 1) + xx1) >> 1;
-	xx2 = (((xx2 + xc) >> 1) + xx2) >> 1;
-	_gfx->drawLine(xx1, ybl, xx2, ybr, dark);
-}
-
-void ColonyEngine::drawOpenDoor(int left[4], int right[4], int left2[2], int right2[2]) {
-	const uint32 dark = 160;
-	const uint32 light = 210;
-	int x1 = left[0];
-	int x2 = right[0];
-	int y1 = left[1];
-	int y2 = right[1];
-	int y3 = _height - right[1];
-	int y4 = _height - left[1];
-	int xc = (x1 + x2) >> 1;
-	int xl = (xc + x1) >> 1;
-	int xr = (xc + x2) >> 1;
-	int yc = (y1 + y2) >> 1;
-	int ytl = (yc + y1) >> 1;
-	int ytr = (yc + y2) >> 1;
-	yc = (y4 + y3) >> 1;
-	int ybl = (yc + y4) >> 1;
-	int ybr = (yc + y3) >> 1;
-	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
-	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
-	if (xr < _screenR.left || xl > _screenR.right)
-		return;
-
-	_gfx->drawLine(xl, ybl, xl, ytl, dark);
-	_gfx->drawLine(xl, ytl, xr, ytr, dark);
-	_gfx->drawLine(xr, ytr, xr, ybr, dark);
-	_gfx->drawLine(xr, ybr, xl, ybl, dark);
-
-	x1 = left2[0];
-	x2 = right2[0];
-	y1 = _height - left2[1];
-	y2 = _height - right2[1];
-	xc = (x1 + x2) >> 1;
-	int xfl = (xc + x1) >> 1;
-	int xfr = (xc + x2) >> 1;
-	yc = (y1 + y2) >> 1;
-	int yfl = (yc + y1) >> 1;
-	int yfr = (yc + y2) >> 1;
-
-	_gfx->drawLine(xl, ybl, xfl, yfl, light);
-	_gfx->drawLine(xfl, yfl, xfr, yfr, light);
-	_gfx->drawLine(xfr, yfr, xr, ybr, light);
-	_gfx->drawLine(xr, ybr, xl, ybl, light);
-}
-
-void ColonyEngine::drawTunnel(int left[4], int right[4], int left2[2], int right2[2]) {
-	const uint32 dark = 120;
-	int baseX[7], baseY[7], tunnelY[7][7];
-	int xl = left[0];
-	int xr = right[0];
-	int ytl = left[1];
-	int ytr = right[1];
-	int ybr = _height - right[1];
-	int ybl = _height - left[1];
-	int hl = ybl - ytl;
-	int hr = ybr - ytr;
-	(void)left2;
-	(void)right2;
-	(void)MAX(hl, hr);
-	split7(baseX, xl, xr);
-	if (baseX[0] > _screenR.right || baseX[6] < _screenR.left)
-		return;
-	split7(baseY, ybl, ybr);
-	for (int i = 0; i < 7; i++)
-		split7(tunnelY[i], baseY[i], _height - baseY[i]);
-
-	int x[6] = {baseX[0], baseX[0], baseX[1], baseX[5], baseX[6], baseX[6]};
-	int y[6] = {baseY[0], tunnelY[0][5], tunnelY[1][6], tunnelY[5][6], tunnelY[6][5], baseY[6]};
-	for (int i = 0; i < 6; i++) {
-		int n = (i + 1) % 6;
-		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
-	}
-}
-
-void ColonyEngine::drawGlyphs(int left[4], int right[4]) {
-	const uint32 dark = 170;
-	int xl = left[0];
-	int xr = right[0];
-	int y1 = left[1];
-	int y2 = right[1];
-	int y3 = _height - right[1];
-	int y4 = _height - left[1];
-	int xc = (xl + xr) >> 1;
-	xl = (((xc + xl) >> 1) + xl) >> 1;
-	xr = (((xc + xr) >> 1) + xr) >> 1;
-	int ytc = (y1 + y2) >> 1;
-	int ybc = (y3 + y4) >> 1;
-	int ytl = (((y1 + ytc) >> 1) + y1) >> 1;
-	int ytr = (((y2 + ytc) >> 1) + y2) >> 1;
-	int ybl = (((y4 + ybc) >> 1) + y4) >> 1;
-	int ybr = (((y3 + ybc) >> 1) + y3) >> 1;
-	int yl1 = (ytl + ybl) >> 1;
-	int yr1 = (ytr + ybr) >> 1;
-	int yl2 = (yl1 + ytl) >> 1;
-	int yr2 = (yr1 + ytr) >> 1;
-	int yl3 = (yl2 + yl1) >> 1;
-	int yr3 = (yr2 + yr1) >> 1;
-	int yl4 = (yl1 + ybl) >> 1;
-	int yr4 = (yr1 + ybr) >> 1;
-	int yr5 = (yr4 + yr1) >> 1;
-	int yl5 = (yl4 + yl1) >> 1;
-
-	_gfx->drawLine(xl, yl1, xr, yr1, dark);
-	_gfx->drawLine(xl, yl2, xr, yr2, dark);
-	_gfx->drawLine(xl, yl3, xr, yr3, dark);
-	_gfx->drawLine(xl, yl4, xr, yr4, dark);
-	_gfx->drawLine(xl, yl5, xr, yr5, dark);
-	_gfx->drawLine(xl, (yl2 + yl3) >> 1, xr, (yr2 + yr3) >> 1, dark);
-	_gfx->drawLine(xl, (yl3 + yl1) >> 1, xr, (yr3 + yr1) >> 1, dark);
-	_gfx->drawLine(xl, (yl1 + yl5) >> 1, xr, (yr1 + yr5) >> 1, dark);
-	_gfx->drawLine(xl, (yl4 + yl5) >> 1, xr, (yr4 + yr5) >> 1, dark);
-}
-
-void ColonyEngine::drawBooks(int left[4], int right[4], int left2[2], int right2[2]) {
-	const uint32 dark = 170;
-	int l2[2] = {left2[0], left2[1]};
-	int r2[2] = {right2[0], right2[1]};
-	for (int i = 0; i < 2; i++) {
-		l2[0] = (l2[0] + left[0]) >> 1;
-		l2[1] = (l2[1] + left[1]) >> 1;
-		r2[0] = (r2[0] + right[0]) >> 1;
-		r2[1] = (r2[1] + right[1]) >> 1;
-	}
-	_gfx->drawLine(l2[0], l2[1], l2[0], _height - l2[1], dark);
-	_gfx->drawLine(l2[0], _height - l2[1], r2[0], _height - r2[1], dark);
-	_gfx->drawLine(r2[0], _height - r2[1], r2[0], r2[1], dark);
-	_gfx->drawLine(r2[0], r2[1], l2[0], l2[1], dark);
-	_gfx->drawLine(left[0], left[1], l2[0], l2[1], dark);
-	_gfx->drawLine(left[0], _height - left[1], l2[0], _height - l2[1], dark);
-	_gfx->drawLine(right[0], right[1], r2[0], r2[1], dark);
-	_gfx->drawLine(right[0], _height - right[1], r2[0], _height - r2[1], dark);
-
-	int lf[7], rf[7], lb[7], rb[7];
-	split7(lf, left[1], _height - left[1]);
-	split7(rf, right[1], _height - right[1]);
-	split7(lb, l2[1], _height - l2[1]);
-	split7(rb, r2[1], _height - r2[1]);
-	for (int i = 0; i < 7; i++) {
-		_gfx->drawLine(left[0], lf[i], right[0], rf[i], dark);
-		_gfx->drawLine(right[0], rf[i], r2[0], rb[i], dark);
-		_gfx->drawLine(r2[0], rb[i], l2[0], lb[i], dark);
-		_gfx->drawLine(l2[0], lb[i], left[0], lf[i], dark);
-	}
-}
-
-void ColonyEngine::drawUpStairs(int left[4], int right[4], int left2[2], int right2[2]) {
-	const uint32 dark = 170;
-	int xl[7], xr[7], yl[7], yr[7];
-	split7(xl, left[0], left2[0]);
-	split7(xr, right[0], right2[0]);
-	split7(yl, _height - left[1], left2[1]);
-	split7(yr, _height - right[1], right2[1]);
-	for (int i = 0; i < 6; i++) {
-		_gfx->drawLine(xl[i], yl[i], xl[i + 1], yl[i + 1], dark);
-		_gfx->drawLine(xr[i], yr[i], xr[i + 1], yr[i + 1], dark);
-		_gfx->drawLine(xl[i], yl[i], xr[i], yr[i], dark);
-	}
-}
-
-void ColonyEngine::drawDnStairs(int left[4], int right[4], int left2[2], int right2[2]) {
-	const uint32 dark = 170;
-	int xl[7], xr[7], yl[7], yr[7];
-	split7(xl, left[0], left2[0]);
-	split7(xr, right[0], right2[0]);
-	split7(yl, left[1], left2[1]);
-	split7(yr, right[1], right2[1]);
-	for (int i = 0; i < 6; i++) {
-		_gfx->drawLine(xl[i], yl[i], xl[i + 1], yl[i + 1], dark);
-		_gfx->drawLine(xr[i], yr[i], xr[i + 1], yr[i + 1], dark);
-		_gfx->drawLine(xl[i], _height - yl[i], xr[i], _height - yr[i], dark);
-	}
-}
-
-void ColonyEngine::drawALOpen(int left[4], int right[4]) {
-	const uint32 dark = 150;
-	int lr[7], ud[7][7];
-	split7x7(left, right, lr, ud);
-	int x[8] = {lr[0], lr[1], lr[3], lr[5], lr[6], lr[5], lr[3], lr[1]};
-	int y[8] = {ud[3][0], ud[5][1], ud[6][3], ud[5][5], ud[3][6], ud[1][5], ud[0][3], ud[1][1]};
-	for (int i = 0; i < 8; i++) {
-		int n = (i + 1) % 8;
-		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
-	}
-}
-
-void ColonyEngine::drawALClosed(int left[4], int right[4]) {
-	const uint32 dark = 170;
-	int lr[7], ud[7][7];
-	split7x7(left, right, lr, ud);
-	int x[8] = {lr[0], lr[1], lr[3], lr[5], lr[6], lr[5], lr[3], lr[1]};
-	int y[8] = {ud[3][0], ud[5][1], ud[6][3], ud[5][5], ud[3][6], ud[1][5], ud[0][3], ud[1][1]};
-	for (int i = 0; i < 8; i++) {
-		int n = (i + 1) % 8;
-		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
-	}
-	_gfx->drawLine(lr[0], ud[3][0], lr[3], ud[3][3], dark);
-	_gfx->drawLine(lr[3], ud[6][3], lr[3], ud[3][3], dark);
-	_gfx->drawLine(lr[6], ud[3][6], lr[3], ud[3][3], dark);
-	_gfx->drawLine(lr[3], ud[0][3], lr[3], ud[3][3], dark);
-}
-
-void ColonyEngine::drawOpenSSDoor(int left[4], int right[4]) {
-	const uint32 dark = 140;
-	int lr[7], ud[7][7];
-	split7x7(left, right, lr, ud);
-	int x[8] = {lr[2], lr[1], lr[1], lr[2], lr[4], lr[5], lr[5], lr[4]};
-	int y[8] = {ud[0][2], ud[1][1], ud[5][1], ud[6][2], ud[6][4], ud[5][5], ud[1][5], ud[0][4]};
-	for (int i = 0; i < 8; i++) {
-		int n = (i + 1) % 8;
-		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
-	}
-}
-
-void ColonyEngine::drawClosedSSDoor(int left[4], int right[4]) {
-	const uint32 dark = 170;
-	int lr[7], ud[7][7];
-	split7x7(left, right, lr, ud);
-	int x[8] = {lr[2], lr[1], lr[1], lr[2], lr[4], lr[5], lr[5], lr[4]};
-	int y[8] = {ud[0][2], ud[1][1], ud[5][1], ud[6][2], ud[6][4], ud[5][5], ud[1][5], ud[0][4]};
-	for (int i = 0; i < 8; i++) {
-		int n = (i + 1) % 8;
-		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
-	}
-	_gfx->drawLine(lr[2], ud[1][2], lr[2], ud[5][2], dark);
-	_gfx->drawLine(lr[2], ud[5][2], lr[4], ud[5][4], dark);
-	_gfx->drawLine(lr[4], ud[5][4], lr[4], ud[1][4], dark);
-	_gfx->drawLine(lr[4], ud[1][4], lr[2], ud[1][2], dark);
-}
-
-void ColonyEngine::drawElevator(int left[4], int right[4]) {
-	const uint32 dark = 170;
-	int x1 = left[0];
-	int x2 = right[0];
-	int y1 = left[1];
-	int y2 = right[1];
-	int y3 = _height - right[1];
-	int y4 = _height - left[1];
-	int xc = (x1 + x2) >> 1;
-	int xx1 = (xc + x1) >> 1;
-	xx1 = (x1 + xx1) >> 1;
-	int xx2 = (xc + x2) >> 1;
-	xx2 = (x2 + xx2) >> 1;
-	if (xx2 < _screenR.left || xx1 > _screenR.right)
-		return;
-	int ytc = (y1 + y2) >> 1;
-	int ytl = (ytc + y1) >> 1;
-	ytl = (ytl + y1) >> 1;
-	int ytr = (ytc + y2) >> 1;
-	ytr = (ytr + y2) >> 1;
-	int ybc = (y4 + y3) >> 1;
-	int ybl = (ybc + y4) >> 1;
-	ybl = (ybl + y4) >> 1;
-	int ybr = (ybc + y3) >> 1;
-	ybr = (ybr + y3) >> 1;
-	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
-	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
-	_gfx->drawLine(xx1, ybl, xx1, ytl, dark);
-	_gfx->drawLine(xx1, ytl, xx2, ytr, dark);
-	_gfx->drawLine(xx2, ytr, xx2, ybr, dark);
-	_gfx->drawLine(xx2, ybr, xx1, ybl, dark);
-	_gfx->drawLine(xc, ybc, xc, (ytl + ytr) >> 1, dark);
-}
-
-void ColonyEngine::drawColor(const uint8 *map, int left[4], int right[4]) {
-	int xl = left[0];
-	int xr = right[0];
-	int yl[5], yr[5];
-	yl[0] = left[1];
-	yr[0] = right[1];
-	yl[4] = _height - yl[0];
-	yr[4] = _height - yr[0];
-	yl[2] = (yl[0] + yl[4]) >> 1;
-	yr[2] = (yr[0] + yr[4]) >> 1;
-	yl[1] = (yl[0] + yl[2]) >> 1;
-	yl[3] = (yl[2] + yl[4]) >> 1;
-	yr[1] = (yr[0] + yr[2]) >> 1;
-	yr[3] = (yr[2] + yr[4]) >> 1;
-
-	if (map[1] || map[2] || map[3] || map[4]) {
-		for (int i = 1; i <= 3; i++) {
-			uint32 c = 120 + map[i] * 20;
-			_gfx->drawLine(xl, yl[i], xr, yr[i], c);
-		}
-	} else {
-		uint32 c = 100 + (_level * 15);
-		_gfx->drawLine(xl, yl[1], xr, yr[1], c);
-		_gfx->drawLine(xl, yl[2], xr, yr[2], c);
-		_gfx->drawLine(xl, yl[3], xr, yr[3], c);
-	}
-}
-
-void ColonyEngine::split7(int arr[7], int x1, int x2) const {
-	arr[3] = (x1 + x2) >> 1;
-	arr[1] = (x1 + arr[3]) >> 1;
-	arr[0] = (x1 + arr[1]) >> 1;
-	arr[2] = (arr[1] + arr[3]) >> 1;
-	arr[5] = (arr[3] + x2) >> 1;
-	arr[6] = (arr[5] + x2) >> 1;
-	arr[4] = (arr[3] + arr[5]) >> 1;
-}
-
-void ColonyEngine::split7x7(int left[4], int right[4], int lr[7], int ud[7][7]) const {
-	int leftX, rightX, leftY, rightY;
-	int lud[7], rud[7];
-	if (right[0] < left[0]) {
-		rightX = left[0];
-		leftX = right[0];
-		rightY = left[1];
-		leftY = right[1];
-	} else {
-		leftX = left[0];
-		rightX = right[0];
-		leftY = left[1];
-		rightY = right[1];
-	}
-	split7(lr, leftX, rightX);
-	if (_flip) {
-		split7(lud, leftY, _height - leftY);
-		split7(rud, rightY, _height - rightY);
-	} else {
-		split7(lud, _height - leftY, leftY);
-		split7(rud, _height - rightY, rightY);
-	}
-	for (int i = 0; i < 7; i++)
-		split7(ud[i], lud[i], rud[i]);
-}
-
-bool ColonyEngine::projectWorld(int worldX, int worldY, int &screenX, int &depth) const {
-	long x = worldX - _me.xloc;
-	long y = worldY - _me.yloc;
-	long tsin = _cost[_me.look];
-	long tcos = _sint[_me.look];
-	long xx = (x * tcos - y * tsin) >> 7;
-	long yy = (x * tsin + y * tcos) >> 7;
-
-	if (yy <= 16)
-		return false;
-	if (yy >= 11585)
-		yy = 11584;
-
-	screenX = _centerX + (int)(((int64)xx * 256) / yy);
-	depth = (int)yy;
-	return true;
-}
-
-uint32 ColonyEngine::objectColor(int type) const {
-	switch (type) {
-	case 21: // DESK
-		return 220;
-	case 22: // PLANT
-		return 100;
-	case 24: // BED
-	case 42: // BBED
-		return 180;
-	case 29: // SCREEN
-	case 30: // CONSOLE
-		return 240;
-	case 31: // POWERSUIT
-	case 46: // REACTOR
-		return 255;
-	case 36: // TELEPORT
-		return 140;
-	default:
-		return 160 + ((uint32)(type * 7) & 0x3F);
-	}
-}
-
-bool ColonyEngine::isSurfaceClockwise(const ProjectedPrismPart &part, const int surface[8]) const {
-	const int n = surface[1];
-	for (int i = 2; i < n; i++) {
-		const int ia = surface[i];
-		const int ib = surface[i + 1];
-		const int ic = surface[i + 2];
-		if (ia < 0 || ia >= part.pointCount || ib < 0 || ib >= part.pointCount || ic < 0 || ic >= part.pointCount)
-			continue;
-		const long dx = part.x[ia] - part.x[ib];
-		const long dy = part.y[ia] - part.y[ib];
-		const long dxp = part.x[ic] - part.x[ib];
-		const long dyp = part.y[ic] - part.y[ib];
-		if (dx < 0) {
-			if (dy == 0) {
-				if (dyp > 0)
-					return false;
-				if (dyp < 0)
-					return true;
-			} else {
-				const long b = dy * dxp - dx * dyp;
-				if (b > 0)
-					return false;
-				if (b < 0)
-					return true;
-			}
-		} else if (dx > 0) {
-			if (dy == 0) {
-				if (dyp < 0)
-					return false;
-				if (dyp > 0)
-					return true;
-			} else {
-				const long b = dx * dyp - dy * dxp;
-				if (b < 0)
-					return false;
-				if (b > 0)
-					return true;
-			}
-		} else {
-			if (dy < 0) {
-				if (dxp > 0)
-					return true;
-				if (dxp < 0)
-					return false;
-			}
-			if (dy > 0) {
-				if (dxp < 0)
-					return true;
-				if (dxp > 0)
-					return false;
-			}
-		}
-	}
-	return false;
-}
-
-bool ColonyEngine::projectPrismPart(const Thing &obj, const PrismPartDef &part, bool useLook, ProjectedPrismPart &out) const {
-	out.pointCount = CLIP<int>(part.pointCount, 0, ProjectedPrismPart::kMaxPoints);
-	for (int i = 0; i < ProjectedPrismPart::kMaxSurfaces; i++)
-		out.vsurface[i] = false;
-	out.visible = false;
-	if (out.pointCount <= 0 || !part.points || !part.surfaces)
-		return false;
-
-	const uint8 ang = useLook ? obj.where.look : obj.where.ang;
-	const long rotCos = _cost[ang];
-	const long rotSin = _sint[ang];
-	const long viewSin = _cost[_me.look];
-	const long viewCos = _sint[_me.look];
-
-	int minX = 32000;
-	int maxX = -32000;
-	int minY = 32000;
-	int maxY = -32000;
-	// DOS InitObj() applies: Robot[i][j].pnt[k][2] -= Floor, with Floor == 160.
-	// We keep source geometry unmodified and apply the same offset at projection time.
-	static const int kFloorShift = 160;
-	for (int i = 0; i < out.pointCount; i++) {
-		const int px = part.points[i][0];
-		const int py = part.points[i][1];
-		const int pz = part.points[i][2];
-		const long rx = ((long)px * rotCos - (long)py * rotSin) >> 7;
-		const long ry = ((long)px * rotSin + (long)py * rotCos) >> 7;
-		const long worldX = rx + obj.where.xloc;
-		const long worldY = ry + obj.where.yloc;
-
-		const long tx = worldX - _me.xloc;
-		const long ty = worldY - _me.yloc;
-		const long xx = (tx * viewCos - ty * viewSin) >> 7;
-		long yy = (tx * viewSin + ty * viewCos) >> 7;
-		if (yy <= 16)
-			yy = 16;
-
-		out.x[i] = _centerX + (int)(((int64)xx * 256) / yy);
-		out.depth[i] = (int)yy;
-		const long zrel = (long)pz - kFloorShift;
-		out.y[i] = _centerY - (int)(((int64)zrel * 256) / yy);
-		minX = MIN(minX, out.x[i]);
-		maxX = MAX(maxX, out.x[i]);
-		minY = MIN(minY, out.y[i]);
-		maxY = MAX(maxY, out.y[i]);
-	}
-
-	out.visible = !(maxX < _screenR.left || minX >= _screenR.right || maxY < _screenR.top || minY >= _screenR.bottom);
-	if (!out.visible)
-		return false;
-
-	const int surfCount = CLIP<int>(part.surfaceCount, 0, ProjectedPrismPart::kMaxSurfaces);
-	for (int i = 0; i < surfCount; i++)
-		out.vsurface[i] = isSurfaceClockwise(out, part.surfaces[i]);
-	return true;
-}
-
-bool ColonyEngine::clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const {
-	if (clip.left >= clip.right || clip.top >= clip.bottom)
-		return false;
-	const int l = clip.left;
-	const int r = clip.right - 1;
-	const int t = clip.top;
-	const int b = clip.bottom - 1;
-	auto outCode = [&](int x, int y) {
-		int code = 0;
-		if (x < l)
-			code |= 1;
-		else if (x > r)
-			code |= 2;
-		if (y < t)
-			code |= 4;
-		else if (y > b)
-			code |= 8;
-		return code;
-	};
-
-	int c1 = outCode(x1, y1);
-	int c2 = outCode(x2, y2);
-	while (true) {
-		if ((c1 | c2) == 0)
-			return true;
-		if (c1 & c2)
-			return false;
-
-		const int cOut = c1 ? c1 : c2;
-		int x = 0;
-		int y = 0;
-		if (cOut & 8) {
-			if (y2 == y1)
-				return false;
-			x = x1 + (x2 - x1) * (b - y1) / (y2 - y1);
-			y = b;
-		} else if (cOut & 4) {
-			if (y2 == y1)
-				return false;
-			x = x1 + (x2 - x1) * (t - y1) / (y2 - y1);
-			y = t;
-		} else if (cOut & 2) {
-			if (x2 == x1)
-				return false;
-			y = y1 + (y2 - y1) * (r - x1) / (x2 - x1);
-			x = r;
-		} else {
-			if (x2 == x1)
-				return false;
-			y = y1 + (y2 - y1) * (l - x1) / (x2 - x1);
-			x = l;
-		}
-
-		if (cOut == c1) {
-			x1 = x;
-			y1 = y;
-			c1 = outCode(x1, y1);
-		} else {
-			x2 = x;
-			y2 = y;
-			c2 = outCode(x2, y2);
-		}
-	}
-}
-
-void ColonyEngine::drawProjectedPrism(const ProjectedPrismPart &part, const PrismPartDef &def, int force, uint32 color, const Common::Rect &clip) {
-	const int surfCount = CLIP<int>(def.surfaceCount, 0, ProjectedPrismPart::kMaxSurfaces);
-	for (int i = 0; i < surfCount; i++) {
-		if (!(part.vsurface[i] || force))
-			continue;
-		const int n = def.surfaces[i][1];
-		if (n < 2)
-			continue;
-		int first = def.surfaces[i][2];
-		if (first < 0 || first >= part.pointCount)
-			continue;
-		int prev = first;
-		for (int j = 1; j < n; j++) {
-			const int cur = def.surfaces[i][j + 2];
-			if (cur < 0 || cur >= part.pointCount)
-				continue;
-			int x1 = part.x[prev];
-			int y1 = part.y[prev];
-			int x2 = part.x[cur];
-			int y2 = part.y[cur];
-			if (clipLineToRect(x1, y1, x2, y2, clip))
-				_gfx->drawLine(x1, y1, x2, y2, color);
-			prev = cur;
-		}
-		int x1 = part.x[prev];
-		int y1 = part.y[prev];
-		int x2 = part.x[first];
-		int y2 = part.y[first];
-		if (clipLineToRect(x1, y1, x2, y2, clip))
-			_gfx->drawLine(x1, y1, x2, y2, color);
-	}
-}
-
-bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
-	// DOS object geometry from SCREEN.H / TABLE.H / BED.H / DESK.H.
-	static const int kScreenPts[8][3] = {
-		{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
-		{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
-	};
-	static const int kScreenSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
-	};
-
-	static const int kTableTopPts[4][3] = {
-		{-128, 128, 100}, {128, 128, 100}, {128, -128, 100}, {-128, -128, 100}
-	};
-	static const int kTableTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kTableBasePts[8][3] = {
-		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-		{-5, 5, 100}, {5, 5, 100}, {5, -5, 100}, {-5, -5, 100}
-	};
-	static const int kTableBaseSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
-	};
-
-	static const int kBedPostPts[4][3] = {
-		{-82, 128, 100}, {82, 128, 100}, {82, 128, 0}, {-82, 128, 0}
-	};
-	static const int kBBedPostPts[4][3] = {
-		{-130, 128, 100}, {130, 128, 100}, {130, 128, 0}, {-130, 128, 0}
-	};
-	static const int kBedPostSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kBlanketSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
-	};
-	static const int kSheetSurf[3][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-		{0, 4, 7, 6, 5, 4, 0, 0}
-	};
-	static const int kBedBlanketPts[8][3] = {
-		{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
-		{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
-	};
-	static const int kBedSheetPts[8][3] = {
-		{-80, 128, 30}, {80, 128, 30}, {80, 70, 30}, {-80, 70, 30},
-		{-80, 128, 60}, {80, 128, 60}, {80, 70, 60}, {-80, 70, 60}
-	};
-	static const int kBBedBlanketPts[8][3] = {
-		{-128, 70, 0}, {128, 70, 0}, {128, -175, 0}, {-128, -175, 0},
-		{-128, 70, 60}, {128, 70, 60}, {128, -175, 60}, {-128, -175, 60}
-	};
-	static const int kBBedSheetPts[8][3] = {
-		{-128, 128, 30}, {128, 128, 30}, {128, 70, 30}, {-128, 70, 30},
-		{-128, 128, 60}, {128, 128, 60}, {128, 70, 60}, {-128, 70, 60}
-	};
-
-	static const int kDeskTopPts[4][3] = {
-		{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
-	};
-	static const int kDeskTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kDeskLeftPts[8][3] = {
-		{-135, 95, 0}, {-55, 95, 0}, {-55, -95, 0}, {-135, -95, 0},
-		{-135, 95, 100}, {-55, 95, 100}, {-55, -95, 100}, {-135, -95, 100}
-	};
-	static const int kDeskRightPts[8][3] = {
-		{55, 95, 0}, {135, 95, 0}, {135, -95, 0}, {55, -95, 0},
-		{55, 95, 100}, {135, 95, 100}, {135, -95, 100}, {55, -95, 100}
-	};
-	static const int kDeskCabSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
-	};
-	static const int kSeatPts[4][3] = {
-		{-40, 210, 60}, {40, 210, 60}, {40, 115, 60}, {-40, 115, 60}
-	};
-	static const int kSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kArmLeftPts[4][3] = {
-		{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
-	};
-	static const int kArmRightPts[4][3] = {
-		{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
-	};
-	static const int kArmSurf[2][8] = {
-		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
-	};
-	static const int kBackPts[4][3] = {
-		{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
-	};
-	static const int kBackSurf[2][8] = {
-		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
-	};
-	static const int kComputerPts[8][3] = {
-		{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
-		{70, 25, 120}, {120, 25, 120}, {120, -25, 120}, {70, -25, 120}
-	};
-	static const int kMonitorPts[8][3] = {
-		{75, 20, 120}, {115, 20, 120}, {115, -20, 120}, {75, -20, 120},
-		{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
-	};
-	static const int kComputerSurf[5][8] = {
-		{0, 4, 7, 6, 5, 4, 0, 0}, {0, 4, 0, 3, 7, 4, 0, 0},
-		{0, 4, 3, 2, 6, 7, 0, 0}, {0, 4, 1, 0, 4, 5, 0, 0},
-		{0, 4, 2, 1, 5, 6, 0, 0}
-	};
-	static const int kDeskScreenPts[4][3] = {
-		{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
-	};
-	static const int kDeskScreenSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-
-	static const int kCSeatPts[4][3] = {
-		{-40, 40, 60}, {40, 40, 60}, {40, -40, 60}, {-40, -40, 60}
-	};
-	static const int kCSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kCArmLeftPts[4][3] = {
-		{-50, 40, 90}, {-40, 40, 60}, {-40, -40, 60}, {-50, -40, 90}
-	};
-	static const int kCArmLeftSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kCArmRightPts[4][3] = {
-		{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
-	};
-	static const int kCArmRightSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kCBackPts[4][3] = {
-		{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
-	};
-	static const int kCBackSurf[2][8] = {
-		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
-	};
-	static const int kCBasePts[8][3] = {
-		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-		{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
-	};
-	static const int kCBaseSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
-	};
-
-	static const int kConsolePts[8][3] = {
-		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-		{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
-	};
-	static const int kConsoleSurf[5][8] = {
-		{0, 4, 4, 0, 3, 7, 0, 0}, {0, 4, 7, 3, 2, 6, 0, 0},
-		{0, 4, 5, 1, 0, 4, 0, 0}, {0, 4, 6, 2, 1, 5, 0, 0},
-		{0, 4, 7, 6, 5, 4, 0, 0}
-	};
-
-	static const int kCouchSurf[5][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-		{0, 4, 7, 6, 5, 4, 0, 0}
-	};
-	static const int kACouchPts[8][3] = {
-		{-50, 150, 0}, {50, 150, 0}, {50, -150, 0}, {-50, -150, 0},
-		{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50}
-	};
-	static const int kBCouchPts[8][3] = {
-		{-80, 150, 0}, {-45, 150, 0}, {-45, -150, 0}, {-80, -150, 0},
-		{-80, 150, 120}, {-55, 150, 120}, {-55, -150, 120}, {-80, -150, 120}
-	};
-	static const int kCCouchPts[8][3] = {
-		{-70, 170, 0}, {50, 170, 0}, {50, 150, 0}, {-70, 150, 0},
-		{-70, 170, 80}, {50, 170, 80}, {50, 150, 80}, {-70, 150, 80}
-	};
-	static const int kDCouchPts[8][3] = {
-		{-70, -150, 0}, {50, -150, 0}, {50, -170, 0}, {-70, -170, 0},
-		{-70, -150, 80}, {50, -150, 80}, {50, -170, 80}, {-70, -170, 80}
-	};
-
-	static const int kAChairPts[8][3] = {
-		{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
-		{-50, 50, 50}, {50, 50, 50}, {50, -50, 50}, {-50, -50, 50}
-	};
-	static const int kBChairPts[8][3] = {
-		{-80, 50, 0}, {-45, 50, 0}, {-45, -50, 0}, {-80, -50, 0},
-		{-80, 50, 120}, {-55, 50, 120}, {-55, -50, 120}, {-80, -50, 120}
-	};
-	static const int kCChairPts2[8][3] = {
-		{-70, 70, 0}, {50, 70, 0}, {50, 50, 0}, {-70, 50, 0},
-		{-70, 70, 80}, {50, 70, 80}, {50, 50, 80}, {-70, 50, 80}
-	};
-	static const int kDChairPts[8][3] = {
-		{-70, -50, 0}, {50, -50, 0}, {50, -70, 0}, {-70, -70, 0},
-		{-70, -50, 80}, {50, -50, 80}, {50, -70, 80}, {-70, -70, 80}
-	};
-
-	static const int kTVBodyPts[8][3] = {
-		{-30, 60, 0}, {30, 60, 0}, {30, -60, 0}, {-30, -60, 0},
-		{-30, 60, 120}, {30, 60, 120}, {30, -60, 120}, {-30, -60, 120}
-	};
-	static const int kTVBodySurf[5][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-		{0, 4, 7, 6, 5, 4, 0, 0}
-	};
-	static const int kTVScreenPts[4][3] = {
-		{30, 50, 10}, {30, -50, 10}, {30, 50, 110}, {30, -50, 110}
-	};
-	static const int kTVScreenSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
-
-	static const int kDrawerPts[8][3] = {
-		{-80, 70, 0}, {0, 70, 0}, {0, -70, 0}, {-80, -70, 0},
-		{-80, 70, 100}, {0, 70, 100}, {0, -70, 100}, {-80, -70, 100}
-	};
-	static const int kDrawerSurf[5][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-		{0, 4, 7, 6, 5, 4, 0, 0}
-	};
-	static const int kMirrorPts[4][3] = {
-		{-80, 65, 100}, {-80, -65, 100}, {-80, 65, 210}, {-80, -65, 210}
-	};
-	static const int kMirrorSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
-
-	static const PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
-	static const PrismPartDef kTableParts[2] = {
-		{4, kTableTopPts, 1, kTableTopSurf},
-		{8, kTableBasePts, 4, kTableBaseSurf}
-	};
-	static const PrismPartDef kBedParts[3] = {
-		{4, kBedPostPts, 1, kBedPostSurf},
-		{8, kBedBlanketPts, 4, kBlanketSurf},
-		{8, kBedSheetPts, 3, kSheetSurf}
-	};
-	static const PrismPartDef kBBedParts[3] = {
-		{4, kBBedPostPts, 1, kBedPostSurf},
-		{8, kBBedBlanketPts, 4, kBlanketSurf},
-		{8, kBBedSheetPts, 3, kSheetSurf}
-	};
-	static const PrismPartDef kDeskParts[10] = {
-		{4, kDeskTopPts, 1, kDeskTopSurf},
-		{8, kDeskLeftPts, 4, kDeskCabSurf},
-		{8, kDeskRightPts, 4, kDeskCabSurf},
-		{4, kSeatPts, 1, kSeatSurf},
-		{4, kArmLeftPts, 2, kArmSurf},
-		{4, kArmRightPts, 2, kArmSurf},
-		{4, kBackPts, 2, kBackSurf},
-		{8, kComputerPts, 5, kComputerSurf},
-		{8, kMonitorPts, 5, kComputerSurf},
-		{4, kDeskScreenPts, 1, kDeskScreenSurf}
-	};
-	static const PrismPartDef kCChairParts[5] = {
-		{4, kCSeatPts, 1, kCSeatSurf},
-		{4, kCArmLeftPts, 1, kCArmLeftSurf},
-		{4, kCArmRightPts, 1, kCArmRightSurf},
-		{4, kCBackPts, 2, kCBackSurf},
-		{8, kCBasePts, 4, kCBaseSurf}
-	};
-	static const PrismPartDef kConsolePart = {8, kConsolePts, 5, kConsoleSurf};
-	static const PrismPartDef kCouchParts[4] = {
-		{8, kACouchPts, 5, kCouchSurf},
-		{8, kBCouchPts, 5, kCouchSurf},
-		{8, kCCouchPts, 5, kCouchSurf},
-		{8, kDCouchPts, 5, kCouchSurf}
-	};
-	static const PrismPartDef kChairParts[4] = {
-		{8, kAChairPts, 5, kCouchSurf},
-		{8, kBChairPts, 5, kCouchSurf},
-		{8, kCChairPts2, 5, kCouchSurf},
-		{8, kDChairPts, 5, kCouchSurf}
-	};
-	static const PrismPartDef kTVParts[2] = {
-		{8, kTVBodyPts, 5, kTVBodySurf},
-		{4, kTVScreenPts, 1, kTVScreenSurf}
-	};
-	static const PrismPartDef kDrawerParts[2] = {
-		{8, kDrawerPts, 5, kDrawerSurf},
-		{4, kMirrorPts, 1, kMirrorSurf}
-	};
-
-	/* CWALL (object 49) - ported from DOS INITOBJ.C: simple corner/quarter-wall prism */
-	static const int kCWallPts[8][3] = {
-		{-128, 128, -160}, {0, 112, -160}, {112, 0, -160}, {128, -128, -160},
-		{-128, 128, 160},  {0, 112, 160},  {112, 0, 160},  {128, -128, 160}
-	};
-	static const int kCWallSurf[3][8] = {
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0}
-	};
-	static const PrismPartDef kCWallParts[1] = {{8, kCWallPts, 3, kCWallSurf}};
-
-	/* PLANT (new) - simple pot + stem + two leaf plates (approximates DOS plant) */
-	static const int kPlantPotPts[8][3] = {
-		{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0},
-		{-20, 20, 40}, {20, 20, 40}, {20, -20, 40}, {-20, -20, 40}
-	};
-	static const int kPlantPotSurf[5][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
-	};
-
-	static const int kPlantStemPts[8][3] = {
-		{-5, 5, 40}, {5, 5, 40}, {5, -5, 40}, {-5, -5, 40},
-		{-5, 5, 120}, {5, 5, 120}, {5, -5, 120}, {-5, -5, 120}
-	};
-	static const int kPlantStemSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
-	};
-
-	static const int kPlantLeaf1Pts[4][3] = {
-		{-40, 0, 120}, {40, 0, 120}, {40, 0, 140}, {-40, 0, 140}
-	};
-	static const int kPlantLeaf2Pts[4][3] = {
-		{0, 40, 120}, {0, -40, 120}, {0, 40, 140}, {0, -40, 140}
-	};
-	static const int kPlantLeafSurf[1][8] = {{0, 4, 0, 1, 2, 3, 0, 0}};
-
-	static const PrismPartDef kPlantParts[4] = {
-		{8, kPlantPotPts, 5, kPlantPotSurf},
-		{8, kPlantStemPts, 4, kPlantStemSurf},
-		{4, kPlantLeaf1Pts, 1, kPlantLeafSurf},
-		{4, kPlantLeaf2Pts, 1, kPlantLeafSurf}
-	};
-
-	const int clipLeft = MAX<int>((int)obj.clip.left, (int)_screenR.left);
-	const int clipTop = MAX<int>((int)obj.clip.top, (int)_screenR.top);
-	const int clipRight = MIN<int>((int)obj.clip.right, (int)_screenR.right);
-	const int clipBottom = MIN<int>((int)obj.clip.bottom, (int)_screenR.bottom);
-	if (clipLeft >= clipRight || clipTop >= clipBottom)
-		return false;
-	Common::Rect drawClip(clipLeft, clipTop, clipRight, clipBottom);
-
-	auto tint = [](uint32 base, int delta) -> uint32 {
-		return (uint32)CLIP<int>((int)base + delta, 0, 255);
-	};
-
-	ProjectedPrismPart p[10];
-	switch (obj.type) {
-	case kObjConsole:
-		if (!projectPrismPart(obj, kConsolePart, false, p[0]) || !p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kConsolePart, 0, tint(baseColor, 0), drawClip);
-		return true;
-	case kObjCChair:
-		for (int i = 0; i < 5; i++)
-			projectPrismPart(obj, kCChairParts[i], false, p[i]);
-		if (p[4].visible)
-			drawProjectedPrism(p[4], kCChairParts[4], 0, tint(baseColor, -10), drawClip);
-		if (!p[0].visible)
-			return p[4].visible;
-		drawProjectedPrism(p[0], kCChairParts[0], 0, tint(baseColor, 0), drawClip);
-		if (p[3].vsurface[0]) {
-			drawProjectedPrism(p[3], kCChairParts[3], 1, tint(baseColor, 0), drawClip);
-			if (p[1].vsurface[0]) {
-				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
-				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
-			} else {
-				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
-				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
-			}
-		} else {
-			if (p[1].vsurface[0]) {
-				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
-				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
-			} else {
-				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
-				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
-			}
-			drawProjectedPrism(p[3], kCChairParts[3], 1, tint(baseColor, 0), drawClip);
-		}
-		return true;
-	case kObjPlant: {
-		for (int i = 0; i < 4; i++)
-			projectPrismPart(obj, kPlantParts[i], false, p[i]);
-		// Pot must be visible for object to appear
-		if (!p[0].visible)
-			return false;
-		// Draw pot, stem and leaves (pot darker, foliage lighter)
-		drawProjectedPrism(p[0], kPlantParts[0], 0, tint(baseColor, -30), drawClip);
-		if (p[1].visible)
-			drawProjectedPrism(p[1], kPlantParts[1], 0, tint(baseColor, 10), drawClip);
-		if (p[2].vsurface[0])
-			drawProjectedPrism(p[2], kPlantParts[2], 0, tint(baseColor, 30), drawClip);
-		if (p[3].vsurface[0])
-			drawProjectedPrism(p[3], kPlantParts[3], 0, tint(baseColor, 30), drawClip);
-		return true;
-	}
-	case kObjCouch:
-	case kObjChair: {
-		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
-		for (int i = 0; i < 4; i++)
-			projectPrismPart(obj, parts[i], false, p[i]);
-		if (!p[0].visible)
-			return false;
-		if (p[2].vsurface[1] && p[3].vsurface[2]) {
-			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
-			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
-			if (p[0].vsurface[3]) {
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-			} else {
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-			}
-		} else if (p[3].vsurface[1]) {
-			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
-			if (p[0].vsurface[3]) {
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-			} else {
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-			}
-			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
-		} else {
-			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
-			if (p[0].vsurface[3]) {
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-			} else {
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-			}
-			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
-		}
-		return true;
-	}
-	case kObjTV:
-		projectPrismPart(obj, kTVParts[0], false, p[0]);
-		projectPrismPart(obj, kTVParts[1], false, p[1]);
-		if (!p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kTVParts[0], 0, tint(baseColor, 0), drawClip);
-		if (p[1].vsurface[0])
-			drawProjectedPrism(p[1], kTVParts[1], 0, tint(baseColor, 35), drawClip);
-		return true;
-	case kObjDrawer:
-		projectPrismPart(obj, kDrawerParts[0], false, p[0]);
-		projectPrismPart(obj, kDrawerParts[1], false, p[1]);
-		if (!p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kDrawerParts[0], 0, tint(baseColor, 0), drawClip);
-		drawProjectedPrism(p[1], kDrawerParts[1], 1, tint(baseColor, 30), drawClip);
-		return true;
-case kObjFWall:
-		// Simple flat wall part (fallback handled by prism below)
-		if (!projectPrismPart(obj, kCWallParts[0], false, p[0]) || !p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kCWallParts[0], 0, tint(baseColor, -10), drawClip);
-		return true;
-case kObjCWall:
-		// Corner wall (CWALL) — mirror of DOS CWallparts
-		if (!projectPrismPart(obj, kCWallParts[0], false, p[0]) || !p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kCWallParts[0], 0, tint(baseColor, -5), drawClip);
-		return true;
-	case kObjScreen:
-		if (!projectPrismPart(obj, kScreenPart, false, p[0]) || !p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kScreenPart, 0, tint(baseColor, 0), drawClip);
-		return true;
-	case kObjTable:
-		projectPrismPart(obj, kTableParts[0], false, p[0]);
-		projectPrismPart(obj, kTableParts[1], false, p[1]);
-		if (!p[1].visible)
-			return false;
-		drawProjectedPrism(p[1], kTableParts[1], 0, tint(baseColor, -10), drawClip);
-		drawProjectedPrism(p[0], kTableParts[0], 0, tint(baseColor, 20), drawClip);
-		return true;
-	case kObjBed:
-	case kObjBBed: {
-		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
-		projectPrismPart(obj, parts[0], false, p[0]);
-		projectPrismPart(obj, parts[1], false, p[1]);
-		projectPrismPart(obj, parts[2], false, p[2]);
-		if (!p[1].visible)
-			return false;
-		if (p[0].vsurface[0]) {
-			drawProjectedPrism(p[0], parts[0], 1, tint(baseColor, 15), drawClip);
-			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, 5), drawClip);
-			drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -10), drawClip);
-		} else {
-			drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -10), drawClip);
-			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, 5), drawClip);
-			drawProjectedPrism(p[0], parts[0], 1, tint(baseColor, 15), drawClip);
-		}
-		return true;
-	}
-	case kObjDesk:
-		for (int i = 0; i < 10; i++)
-			projectPrismPart(obj, kDeskParts[i], false, p[i]);
-		if (!p[0].visible)
-			return false;
-		if (p[6].vsurface[1]) {
-			if (p[1].vsurface[3] || p[2].vsurface[3]) {
-				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
-				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
-			} else {
-				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
-				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
-			}
-			drawProjectedPrism(p[0], kDeskParts[0], 0, tint(baseColor, 15), drawClip);
-			drawProjectedPrism(p[7], kDeskParts[7], 0, tint(baseColor, 25), drawClip);
-			drawProjectedPrism(p[8], kDeskParts[8], 0, tint(baseColor, 25), drawClip);
-			if (p[9].vsurface[0])
-				drawProjectedPrism(p[9], kDeskParts[9], 0, tint(baseColor, 40), drawClip);
-			if (p[4].vsurface[0]) {
-				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
-			} else {
-				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
-			}
-		} else {
-			if (p[4].vsurface[0]) {
-				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
-			} else {
-				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
-			}
-			if (p[1].vsurface[3] || p[2].vsurface[3]) {
-				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
-				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
-			} else {
-				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
-				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
-			}
-			drawProjectedPrism(p[0], kDeskParts[0], 0, tint(baseColor, 15), drawClip);
-			drawProjectedPrism(p[7], kDeskParts[7], 0, tint(baseColor, 25), drawClip);
-			drawProjectedPrism(p[8], kDeskParts[8], 0, tint(baseColor, 25), drawClip);
-			if (p[9].vsurface[0])
-				drawProjectedPrism(p[9], kDeskParts[9], 0, tint(baseColor, 40), drawClip);
-		}
-		return true;
-	default:
-		return false;
-	}
-}
-
-void ColonyEngine::drawStaticObjectFallback(const Thing &obj, uint32 color, int depth, int sx) {
-	int scale = _rtable[depth];
-	int baseY = _height - (_centerY - scale);
-	int h = CLIP<int>(scale, 4, 96);
-	int w = CLIP<int>(h >> 1, 3, 64);
-	Common::Rect body(sx - w, baseY - h, sx + w, baseY);
-	const int bodyLeft = (int)body.left;
-	const int bodyTop = (int)body.top;
-	const int bodyRight = (int)body.right;
-	const int bodyBottom = (int)body.bottom;
-	const int clipLeft = MAX(bodyLeft, MAX((int)obj.clip.left, (int)_screenR.left));
-	const int clipTop = MAX(bodyTop, MAX((int)obj.clip.top, (int)_screenR.top));
-	const int clipRight = MIN(bodyRight, MIN((int)obj.clip.right, (int)_screenR.right));
-	const int clipBottom = MIN(bodyBottom, MIN((int)obj.clip.bottom, (int)_screenR.bottom));
-	if (clipLeft >= clipRight || clipTop >= clipBottom)
-		return;
-	Common::Rect clipped(clipLeft, clipTop, clipRight, clipBottom);
-	_gfx->drawRect(clipped, color);
-	_gfx->drawLine(clipped.left, clipped.bottom - 1, clipped.right - 1, clipped.bottom - 1, color);
-}
-
-void ColonyEngine::drawStaticObjects() {
-	struct DrawCmd {
-		int depth;
-		int index;
-		int screenX;
-	};
-
-	Common::Array<DrawCmd> drawList;
-	drawList.reserve(_objects.size());
-
-	for (uint i = 0; i < _objects.size(); i++) {
-		const Thing &obj = _objects[i];
-		if (!obj.alive || !obj.visible)
-			continue;
-		int sx, depth;
-		if (!projectWorld(obj.where.xloc, obj.where.yloc, sx, depth))
-			continue;
-		if (depth > 11000)
-			continue;
-		DrawCmd cmd;
-		cmd.depth = depth;
-		cmd.index = (int)i;
-		cmd.screenX = sx;
-		drawList.push_back(cmd);
-	}
-
-	Common::sort(drawList.begin(), drawList.end(), [](const DrawCmd &a, const DrawCmd &b) {
-		return a.depth > b.depth; // far to near
-	});
-
-	for (uint i = 0; i < drawList.size(); i++) {
-		const DrawCmd &d = drawList[i];
-		const Thing &obj = _objects[d.index];
-		const uint32 color = objectColor(obj.type);
-		if (!drawStaticObjectPrisms(obj, color))
-			drawStaticObjectFallback(obj, color, d.depth, d.screenX);
-	}
-}
-
-void ColonyEngine::setRobot(int l, int r, int num) {
-	if (num <= 0 || num > (int)_objects.size())
-		return;
-	if (l < _screenR.left)
-		l = _screenR.left;
-	if (r > _screenR.right)
-		r = _screenR.right;
-	if (l >= r)
-		return;
-	const int clipLeft = l + 1;
-	const int clipRight = r - 2;
-	if (clipLeft >= clipRight)
-		return;
-
-	Thing &obj = _objects[num - 1];
-	if (!obj.alive)
-		return;
-	obj.visible = 1;
-	obj.clip.left = clipLeft;
-	obj.clip.right = clipRight;
-	obj.clip.top = _clip.top;
-	obj.clip.bottom = _clip.bottom;
-}
-
-void ColonyEngine::updateViewportLayout() {
-	auto makeSafeRect = [](int left, int top, int right, int bottom) {
-		if (right < left)
-			right = left;
-		if (bottom < top)
-			bottom = top;
-		return Common::Rect(left, top, right, bottom);
-	};
-
-	int dashWidth = 0;
-	if (_showDashBoard) {
-		dashWidth = CLIP<int>(_width / 6, 72, 140);
-		if (_width - dashWidth < 160)
-			dashWidth = 0;
-	}
-
-	_screenR = makeSafeRect(dashWidth, 0, _width, _height);
-	_clip = _screenR;
-	_centerX = (_screenR.left + _screenR.right) >> 1;
-	_centerY = (_screenR.top + _screenR.bottom) >> 1;
-
-	_dashBoardRect = makeSafeRect(0, 0, dashWidth, _height);
-	if (dashWidth == 0) {
-		_compassRect = Common::Rect(0, 0, 0, 0);
-		_headsUpRect = Common::Rect(0, 0, 0, 0);
-		_powerRect = Common::Rect(0, 0, 0, 0);
-		return;
-	}
-
-	const int pad = 2;
-	const int unit = MAX(8, (dashWidth - (pad * 2)) / 4);
-	const int blockLeft = pad;
-	const int blockRight = MIN(dashWidth - pad, blockLeft + unit * 4);
-
-	const int compassBottom = _height - MAX(2, unit / 4);
-	const int compassTop = MAX(pad, compassBottom - unit * 4);
-	_compassRect = makeSafeRect(blockLeft, compassTop, blockRight, compassBottom);
-
-	const int headsUpBottom = _compassRect.top - MAX(2, unit / 4) - 4;
-	const int headsUpTop = headsUpBottom - unit * 4;
-	_headsUpRect = makeSafeRect(blockLeft, MAX(pad, headsUpTop), blockRight, MAX(pad, headsUpBottom));
-
-	_powerRect = makeSafeRect(blockLeft, pad, blockRight, _headsUpRect.top - 4);
-}
-
-void ColonyEngine::drawDashboardStep1() {
-	if (_dashBoardRect.width() <= 0 || _dashBoardRect.height() <= 0)
-		return;
-
-	const int kFWALLType = 48;
-	const uint32 panelBg = 24;
-	const uint32 frame = 190;
-	const uint32 accent = 220;
-	const uint32 mark = 255;
-	const uint32 miniMapObj = 235;
-	const uint32 miniMapActor = 255;
-
-	_gfx->fillRect(_dashBoardRect, panelBg);
-	_gfx->drawRect(_dashBoardRect, frame);
-	if (_screenR.left > 0)
-		_gfx->drawLine(_screenR.left - 1, _screenR.top, _screenR.left - 1, _screenR.bottom - 1, mark);
-
-	if (_compassRect.width() > 4 && _compassRect.height() > 4) {
-		const int cx = (_compassRect.left + _compassRect.right) >> 1;
-		const int cy = (_compassRect.top + _compassRect.bottom) >> 1;
-		const int rx = MAX(2, (_compassRect.width() - 6) >> 1);
-		const int ry = MAX(2, (_compassRect.height() - 6) >> 1);
-		const int irx = MAX(1, rx - 2);
-		const int iry = MAX(1, ry - 2);
-		auto drawEllipse = [&](int erx, int ery, uint32 color) {
-			const int segments = 48;
-			int px = cx + erx;
-			int py = cy;
-			for (int i = 1; i <= segments; i++) {
-				const double t = (6.28318530717958647692 * (double)i) / (double)segments;
-				const int x = cx + (int)((double)erx * cos(t));
-				const int y = cy + (int)((double)ery * sin(t));
-				_gfx->drawLine(px, py, x, y, color);
-				px = x;
-				py = y;
-			}
-		};
-		drawEllipse(rx, ry, frame);
-		drawEllipse(irx, iry, accent);
-
-		const int ex = cx + ((_cost[_me.look] * rx) >> 8);
-		const int ey = cy - ((_sint[_me.look] * ry) >> 8);
-		_gfx->drawLine(cx, cy, ex, ey, mark);
-		_gfx->drawLine(cx - 2, cy, cx + 2, cy, accent);
-		_gfx->drawLine(cx, cy - 2, cx, cy + 2, accent);
-	}
-
-		if (_headsUpRect.width() > 4 && _headsUpRect.height() > 4) {
-			_gfx->drawRect(_headsUpRect, frame);
-			const Common::Rect miniMapClip(_headsUpRect.left + 1, _headsUpRect.top + 1, _headsUpRect.right - 1, _headsUpRect.bottom - 1);
-			auto drawMiniMapLine = [&](int x1, int y1, int x2, int y2, uint32 color) {
-				if (clipLineToRect(x1, y1, x2, y2, miniMapClip))
-					_gfx->drawLine(x1, y1, x2, y2, color);
-			};
-
-		const int lExtBase = _dashBoardRect.width() >> 1;
-		int lExt = lExtBase + (lExtBase >> 1);
-		if (lExt & 1)
-			lExt--;
-		const int sExt = lExt >> 1;
-		const int xloc = (lExt * ((_me.xindex << 8) - _me.xloc)) >> 8;
-		const int yloc = (lExt * ((_me.yindex << 8) - _me.yloc)) >> 8;
-		const int ccenterx = (_headsUpRect.left + _headsUpRect.right) >> 1;
-		const int ccentery = (_headsUpRect.top + _headsUpRect.bottom) >> 1;
-		const int tsin = _sint[_me.look];
-		const int tcos = _cost[_me.look];
-
-		int xcorner[6];
-		int ycorner[6];
-		xcorner[0] = ccenterx + (((long)xloc * tsin - (long)yloc * tcos) >> 8);
-		ycorner[0] = ccentery - (((long)yloc * tsin + (long)xloc * tcos) >> 8);
-		xcorner[1] = ccenterx + (((long)(xloc + lExt) * tsin - (long)yloc * tcos) >> 8);
-		ycorner[1] = ccentery - (((long)yloc * tsin + (long)(xloc + lExt) * tcos) >> 8);
-		xcorner[2] = ccenterx + (((long)(xloc + lExt) * tsin - (long)(yloc + lExt) * tcos) >> 8);
-		ycorner[2] = ccentery - (((long)(yloc + lExt) * tsin + (long)(xloc + lExt) * tcos) >> 8);
-		xcorner[3] = ccenterx + (((long)xloc * tsin - (long)(yloc + lExt) * tcos) >> 8);
-		ycorner[3] = ccentery - (((long)(yloc + lExt) * tsin + (long)xloc * tcos) >> 8);
-		xcorner[4] = ccenterx + (((long)(xloc + sExt) * tsin - (long)(yloc + sExt) * tcos) >> 8);
-		ycorner[4] = ccentery - (((long)(yloc + sExt) * tsin + (long)(xloc + sExt) * tcos) >> 8);
-		xcorner[5] = ccenterx + (((long)(xloc + sExt) * tsin - (long)yloc * tcos) >> 8);
-		ycorner[5] = ccentery - (((long)yloc * tsin + (long)(xloc + sExt) * tcos) >> 8);
-
-			const int dx = xcorner[1] - xcorner[0];
-			const int dy = ycorner[0] - ycorner[1];
-			drawMiniMapLine(xcorner[0] - dx, ycorner[0] + dy, xcorner[1] + dx, ycorner[1] - dy, accent);
-			drawMiniMapLine(xcorner[1] + dy, ycorner[1] + dx, xcorner[2] - dy, ycorner[2] - dx, accent);
-			drawMiniMapLine(xcorner[2] + dx, ycorner[2] - dy, xcorner[3] - dx, ycorner[3] + dy, accent);
-			drawMiniMapLine(xcorner[3] - dy, ycorner[3] - dx, xcorner[0] + dy, ycorner[0] + dx, accent);
-
-		auto drawMarker = [&](int x, int y, int halfSize, uint32 color) {
-			const int l = MAX<int>(_headsUpRect.left + 1, x - halfSize);
-			const int t = MAX<int>(_headsUpRect.top + 1, y - halfSize);
-			const int r = MIN<int>(_headsUpRect.right - 1, x + halfSize + 1);
-			const int b = MIN<int>(_headsUpRect.bottom - 1, y + halfSize + 1);
-			if (l >= r || t >= b)
-				return;
-			_gfx->drawRect(Common::Rect(l, t, r, b), color);
-		};
-
-		auto hasRobotAt = [&](int x, int y) -> bool {
-			if (x < 0 || x >= 32 || y < 0 || y >= 32)
-				return false;
-			return _robotArray[x][y] != 0;
-		};
-		auto hasFoodAt = [&](int x, int y) -> bool {
-			if (x < 0 || x >= 32 || y < 0 || y >= 32)
-				return false;
-			const uint8 num = _foodArray[x][y];
-			if (num == 0)
-				return false;
-			if (num <= _objects.size())
-				return _objects[num - 1].type < kFWALLType;
-			return true;
-		};
-
-		if (hasFoodAt(_me.xindex, _me.yindex))
-			drawMarker(xcorner[4], ycorner[4], 1, miniMapObj);
-
-		if (_me.yindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x01)) {
-			if (hasFoodAt(_me.xindex, _me.yindex - 1))
-				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 1, miniMapObj);
-			if (hasRobotAt(_me.xindex, _me.yindex - 1))
-				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 2, miniMapActor);
-		}
-		if (_me.xindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x02)) {
-			if (hasFoodAt(_me.xindex - 1, _me.yindex))
-				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 1, miniMapObj);
-			if (hasRobotAt(_me.xindex - 1, _me.yindex))
-				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 2, miniMapActor);
-		}
-		if (_me.yindex < 30 && !(_wall[_me.xindex][_me.yindex + 1] & 0x01)) {
-			if (hasFoodAt(_me.xindex, _me.yindex + 1))
-				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 1, miniMapObj);
-			if (hasRobotAt(_me.xindex, _me.yindex + 1))
-				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 2, miniMapActor);
-		}
-		if (_me.xindex < 30 && !(_wall[_me.xindex + 1][_me.yindex] & 0x02)) {
-			if (hasFoodAt(_me.xindex + 1, _me.yindex))
-				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 1, miniMapObj);
-			if (hasRobotAt(_me.xindex + 1, _me.yindex))
-				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 2, miniMapActor);
-		}
-
-		drawMarker(ccenterx, ccentery, 2, mark);
-		drawMarker(ccenterx, ccentery, 1, accent);
-	}
-
-	if (_powerRect.width() > 4 && _powerRect.height() > 4) {
-		_gfx->drawRect(_powerRect, frame);
-		const int barY = _powerRect.bottom - MAX(3, _powerRect.width() / 8);
-		_gfx->drawLine(_powerRect.left + 1, barY, _powerRect.right - 2, barY, accent);
-	}
-}
-
-void ColonyEngine::drawCrosshair() {
-	if (!_crosshair || _screenR.width() <= 0 || _screenR.height() <= 0)
-		return;
-
-	const uint32 color = (_weapons > 0) ? 255 : 220;
-	const int cx = _centerX;
-	const int cy = _centerY;
-	const int qx = MAX(2, _screenR.width() / 32);
-	const int qy = MAX(2, _screenR.height() / 32);
-	const int fx = (qx * 3) >> 1;
-	const int fy = (qy * 3) >> 1;
-	auto drawCrossLine = [&](int x1, int y1, int x2, int y2) {
-		if (clipLineToRect(x1, y1, x2, y2, _screenR))
-			_gfx->drawLine(x1, y1, x2, y2, color);
-	};
-
-	if (_weapons > 0) {
-		const int yTop = _insight ? (cy - qy) : (cy - fy);
-		const int yBottom = _insight ? (cy + qy) : (cy + fy);
-
-		drawCrossLine(cx - qx, yTop, cx - fx, yTop);
-		drawCrossLine(cx - fx, yTop, cx - fx, yBottom);
-		drawCrossLine(cx - fx, yBottom, cx - qx, yBottom);
-		drawCrossLine(cx + qx, yTop, cx + fx, yTop);
-		drawCrossLine(cx + fx, yTop, cx + fx, yBottom);
-		drawCrossLine(cx + fx, yBottom, cx + qx, yBottom);
-		_insight = false;
-	} else {
-		drawCrossLine(cx - qx, cy, cx + qx, cy);
-		drawCrossLine(cx, cy - qy, cx, cy + qy);
-	}
-}
-
 Common::Error ColonyEngine::run() {
 	Graphics::PixelFormat format8bpp = Graphics::PixelFormat::createFormatCLUT8();
 	initGraphics(_width, _height, &format8bpp);
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index a0635f55218..93f82a5d773 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -30,6 +30,57 @@
 
 namespace Colony {
 
+enum WallFeatureType {
+	kWallFeatureNone = 0,
+	kWallFeatureDoor = 2,
+	kWallFeatureWindow = 3,
+	kWallFeatureShelves = 4,
+	kWallFeatureUpStairs = 5,
+	kWallFeatureDnStairs = 6,
+	kWallFeatureChar = 7,
+	kWallFeatureGlyph = 8,
+	kWallFeatureElevator = 9,
+	kWallFeatureTunnel = 10,
+	kWallFeatureAirlock = 11,
+	kWallFeatureColor = 12
+};
+
+enum MapDirection {
+	kDirNorth = 0,
+	kDirEast = 1,
+	kDirWest = 2,
+	kDirSouth = 3
+};
+
+enum ObjectType {
+	kObjDesk = 21,
+	kObjPlant = 22,
+	kObjCChair = 23,
+	kObjBed = 24,
+	kObjTable = 25,
+	kObjCouch = 26,
+	kObjChair = 27,
+	kObjTV = 28,
+	kObjScreen = 29,
+	kObjConsole = 30,
+	kObjPowerSuit = 31,
+	kObjForkLift = 32,
+	kObjCryo = 33,
+	kObjBox1 = 34,
+	kObjBox2 = 35,
+	kObjTeleport = 36,
+	kObjDrawer = 37,
+	kObjTub = 38,
+	kObjSink = 39,
+	kObjToilet = 40,
+	kObjPToilet = 43,
+	kObjProjector = 45,
+	kObjReactor = 46,
+	kObjFWall = 48,
+	kObjCWall = 49,
+	kObjBBed = 42
+};
+
 #define BASEOBJECT 20
 #define MENUM 101
 
@@ -168,7 +219,12 @@ private:
 		bool vsurface[kMaxSurfaces];
 		bool visible;
 	};
-	struct PrismPartDef;
+	struct PrismPartDef {
+		int pointCount;
+		const int (*points)[3];
+		int surfaceCount;
+		const int (*surfaces)[8];
+	};
 	bool projectPrismPart(const Thing &obj, const PrismPartDef &part, bool useLook, ProjectedPrismPart &out) const;
 	bool isSurfaceClockwise(const ProjectedPrismPart &part, const int surface[8]) const;
 	bool clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const;
diff --git a/engines/colony/module.mk b/engines/colony/module.mk
index 94f5330614a..16f53b98645 100644
--- a/engines/colony/module.mk
+++ b/engines/colony/module.mk
@@ -3,7 +3,9 @@ MODULE := engines/colony
 MODULE_OBJS := \
 	colony.o \
 	gfx.o \
-	metaengine.o
+	metaengine.o \
+	render.o \
+	ui.o
 
 MODULE_DIRS += \
 	engines/colony
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
new file mode 100644
index 00000000000..064e72a3c96
--- /dev/null
+++ b/engines/colony/render.cpp
@@ -0,0 +1,2104 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "colony/gfx.h"
+#include "common/system.h"
+#include "common/util.h"
+#include "common/algorithm.h"
+#include "common/debug.h"
+#include <math.h>
+
+namespace Colony {
+
+static const int g_indexTable[4][10] = {
+	{0, 0,  0, 0,  0,  1,  1,  0,  1, 2},
+	{1, 0,  0, 0, -1,  0,  0,  1,  2, 1},
+	{0, 1,  1, 0,  0, -1, -1,  0,  1, 2},
+	{0, 0,  0, 1,  1,  0,  0, -1,  2, 1}
+};
+
+static const int g_dirRight[4] = {1, 3, 0, 2};
+static const int g_dirLeft[4] = {2, 0, 3, 1};
+
+void ColonyEngine::rot_init(int x, int y) {
+	_rox = ((long)x * _tsin - (long)y * _tcos) >> 8;
+	_roy = ((long)y * _tsin + (long)x * _tcos) >> 8;
+}
+
+void ColonyEngine::perspective(int pnt[2], int rox, int roy) {
+	long p;
+
+	if (roy <= 0)
+		roy = 1;
+	p = _centerX + ((long)rox * 256) / roy;
+
+	if (p < -32000)
+		p = -32000;
+	else if (p > 32000)
+		p = 32000;
+	pnt[0] = (int)p;
+
+	if (_flip)
+		pnt[1] = _centerY + _rtable[roy];
+	else
+		pnt[1] = _centerY - _rtable[roy];
+}
+
+void ColonyEngine::quadrant() {
+	int remain;
+	int quad;
+
+	quad = _me.look >> 6;				/*divide by 64		*/
+	remain = _me.look - (quad << 6);			/*multiply by 64	*/
+	_tsin = _sint[remain];
+	_tcos = _cost[remain];
+
+	switch (quad) {
+	case 0:
+		rot_init((_me.xindex << 8) - _me.xloc, (_me.yindex << 8) - _me.yloc);
+		_direction = 0; // NORTH
+		break;
+	case 1:
+		rot_init((_me.yindex << 8) - _me.yloc, _me.xloc - ((_me.xindex + 1) << 8));
+		_direction = 2; // WEST
+		break;
+	case 2:
+		rot_init(_me.xloc - ((_me.xindex + 1) << 8), _me.yloc - ((_me.yindex + 1) << 8));
+		_direction = 3; // SOUTH
+		break;
+	case 3:
+		rot_init(_me.yloc - ((_me.yindex + 1) << 8), (_me.xindex << 8) - _me.xloc);
+		_direction = 1; // EAST
+		break;
+	}
+
+	_frntxWall = g_indexTable[quad][0];
+	_frntyWall = g_indexTable[quad][1];
+	_sidexWall = g_indexTable[quad][2];
+	_sideyWall = g_indexTable[quad][3];
+	_frntx = g_indexTable[quad][4];
+	_frnty = g_indexTable[quad][5];
+	_sidex = g_indexTable[quad][6];
+	_sidey = g_indexTable[quad][7];
+	_front = g_indexTable[quad][8];
+	_side = g_indexTable[quad][9];
+}
+
+void ColonyEngine::corridor() {
+	int length = 1;
+	int xFrontLeft, yFrontLeft;
+	int xFrontRight, yFrontRight;
+	int xsstart, ysstart;
+	int xfbehind, yfbehind;
+	int roxsave, roysave;
+	int left, right;
+	int left2, right2;
+	int cellx, celly;
+	int cellxsave, cellysave;
+	int dr[2];
+	const int screenLeft = (int)_screenR.left;
+	const int screenRight = (int)_screenR.right;
+
+	quadrant();
+
+	right = screenRight;
+	left = screenLeft;
+	right2 = right;
+	left2 = left;
+
+	xfbehind = _me.xindex + _frntxWall;
+	yfbehind = _me.yindex + _frntyWall;
+	xFrontLeft = xfbehind + _frntx;
+	yFrontLeft = yfbehind + _frnty;
+	xFrontRight = xFrontLeft + _sidex;
+	yFrontRight = yFrontLeft + _sidey;
+	xsstart = _me.xindex + _sidexWall;
+	ysstart = _me.yindex + _sideyWall;
+	cellxsave = cellx = _me.xindex;
+	cellysave = celly = _me.yindex;
+
+	int rox = _rox;
+	int roy = _roy;
+
+	if (_change) {
+		perspective(dr, rox, roy);
+		if (xfbehind >= 0 && xfbehind < 34 && yfbehind >= 0 && yfbehind < 34) {
+			_drY[xfbehind][yfbehind] = dr[1];
+			if (dr[0] > _screenR.left)
+				_drX[xfbehind][yfbehind] = -32000;
+			else
+				_drX[xfbehind][yfbehind] = dr[0];
+		}
+
+		perspective(dr, rox + _tsin, roy + _tcos);
+		if (xfbehind + _sidex >= 0 && xfbehind + _sidex < 34 && yfbehind + _sidey >= 0 && yfbehind + _sidey < 34) {
+			_drY[xfbehind + _sidex][yfbehind + _sidey] = dr[1];
+			if (dr[0] < _screenR.right)
+				_drX[xfbehind + _sidex][yfbehind + _sidey] = 32000;
+			else
+				_drX[xfbehind + _sidex][yfbehind + _sidey] = dr[0];
+		}
+	}
+
+	roxsave = rox;
+	roysave = roy;
+
+	// Move to the first wall in front of the observer.
+	rox -= _tcos;
+	roy += _tsin;
+
+	if (_change) {
+		perspective(dr, rox, roy);
+		if (xFrontLeft >= 0 && xFrontLeft < 34 && yFrontLeft >= 0 && yFrontLeft < 34) {
+			_drX[xFrontLeft][yFrontLeft] = dr[0];
+			_drY[xFrontLeft][yFrontLeft] = dr[1];
+		}
+		perspective(dr, rox + _tsin, roy + _tcos);
+		if (xFrontRight >= 0 && xFrontRight < 34 && yFrontRight >= 0 && yFrontRight < 34) {
+			_drX[xFrontRight][yFrontRight] = dr[0];
+			_drY[xFrontRight][yFrontRight] = dr[1];
+		}
+	}
+
+	if (wallAt(cellx + _sidexWall, celly + _sideyWall) & _side)
+		left2 = MAX(_drX[xFrontLeft][yFrontLeft], screenLeft);
+	else
+		left2 = MAX(left, left2);
+	left2 = MAX(left2, screenLeft);
+
+	if (wallAt(cellx + _sidexWall + _sidex, celly + _sideyWall + _sidey) & _side)
+		right2 = _drX[xFrontRight][yFrontRight];
+	else
+		right2 = MIN(right, right2);
+
+	uint32 white = _gfx->white();
+	_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
+	               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+	if (wallAt(cellx, celly) & ~0x03)
+		frontfeature(cellx, celly, xFrontLeft, yFrontLeft, left2, right2, rox, roy);
+
+	while (!(wallAt(xFrontLeft, yFrontLeft) & _front)) {
+		rox -= _tcos;
+		roy += _tsin;
+		xFrontLeft += _frntx;
+		yFrontLeft += _frnty;
+		xFrontRight += _frntx;
+		yFrontRight += _frnty;
+		if (_change) {
+			perspective(dr, rox, roy);
+			if (xFrontLeft >= 0 && xFrontLeft < 34 && yFrontLeft >= 0 && yFrontLeft < 34) {
+				_drX[xFrontLeft][yFrontLeft] = dr[0];
+				_drY[xFrontLeft][yFrontLeft] = dr[1];
+			}
+			perspective(dr, rox + _tsin, roy + _tcos);
+			if (xFrontRight >= 0 && xFrontRight < 34 && yFrontRight >= 0 && yFrontRight < 34) {
+				_drX[xFrontRight][yFrontRight] = dr[0];
+				_drY[xFrontRight][yFrontRight] = dr[1];
+			}
+		}
+
+		cellx += _frntx;
+		celly += _frnty;
+		if (wallAt(cellx + _sidexWall, celly + _sideyWall) & _side)
+			left2 = MAX(screenLeft, _drX[xFrontLeft][yFrontLeft]);
+		else
+			left2 = MAX(left, left2);
+		left2 = MAX(left2, screenLeft);
+		if (wallAt(cellx + _sidexWall + _sidex, celly + _sideyWall + _sidey) & _side)
+			right2 = _drX[xFrontRight][yFrontRight];
+		else
+			right2 = MIN(right, right2);
+		if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
+			if (_robotArray[cellx][celly])
+				setRobot(left2, right2, _robotArray[cellx][celly]);
+			if (_foodArray[cellx][celly])
+				setRobot(left2, right2, _foodArray[cellx][celly]);
+		}
+		if (wallAt(cellx, celly) & ~0x03)
+			features(cellx, celly, xFrontLeft, yFrontLeft, left2, right2, rox, roy);
+
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+
+		length++;
+		if (length > 30)
+			break; // Safety break
+	}
+	drawend(xfbehind, yfbehind, xFrontLeft, yFrontLeft);
+
+	left = screenLeft;
+	right = MIN(right, _drX[xFrontLeft][yFrontLeft]);
+	if (left < right)
+		checkleft(xsstart, ysstart, xfbehind, yfbehind, left, right, roxsave, roysave, cellxsave, cellysave, length);
+
+	left = MAX(left, _drX[xFrontRight][yFrontRight]);
+	if (left < screenLeft)
+		left = screenLeft;
+	right = screenRight;
+	xsstart += _sidex;
+	ysstart += _sidey;
+	xfbehind += _sidex;
+	yfbehind += _sidey;
+	if (left < right)
+		checkright(xsstart, ysstart, xfbehind, yfbehind, left, right, roxsave + _tsin, roysave + _tcos, cellxsave, cellysave, length);
+
+	_change = false;
+}
+
+void ColonyEngine::drawend(int xstart, int ystart, int xFrontLeft, int yFrontLeft) {
+	int xFrontRight, yFrontRight;
+
+	xFrontRight = xFrontLeft + _sidex;
+	yFrontRight = yFrontLeft + _sidey;
+
+	uint32 white = _gfx->white();
+
+	if ((xstart != xFrontLeft) || (ystart != yFrontLeft)) {
+		if (_drY[xstart + _frntx][ystart + _frnty] > 0) {
+			_gfx->drawLine(_drX[xstart][ystart], _drY[xstart][ystart],
+			               _drX[xstart + _frntx][ystart + _frnty], _drY[xstart + _frntx][ystart + _frnty], white);
+		}
+		_gfx->drawLine(_drX[xstart + _frntx][ystart + _frnty], _drY[xstart + _frntx][ystart + _frnty],
+		               _drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft], white);
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
+		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight],
+		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight],
+		               _drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey], white);
+		if (_drY[xstart + _sidex][ystart + _sidey] > 0) {
+			_gfx->drawLine(_drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey],
+			               _drX[xstart + _frntx + _sidex][ystart + _frnty + _sidey], _drY[xstart + _frntx + _sidex][ystart + _frnty + _sidey], white);
+		}
+	} else {
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
+		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft],
+		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
+		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight],
+		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+	}
+}
+
+uint8 ColonyEngine::wallAt(int x, int y) const {
+	if (x < 0 || x >= 32 || y < 0 || y >= 32)
+		return 3;
+	return _wall[x][y];
+}
+
+void ColonyEngine::checkleft(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len) {
+	int i = 0, j;
+	int xf2, yf2;
+	int rox, roy;
+	int xsstart, ysstart;
+	int xfstart, yfstart;
+	int xestart, yestart;
+	int cellxsave, cellysave;
+	int dr[2];
+	uint32 white = _gfx->white();
+
+	cellx -= _sidex;
+	celly -= _sidey;
+	rx = rx - _tsin;
+	ry = ry - _tcos;
+
+	while (i < len && left <= right) {
+		if (wallAt(xs, ys) & _side) {
+			if (_flip)
+				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+
+			while ((wallAt(xs, ys) & _side) && i < len && left <= right) {
+				_gfx->drawLine(_drX[xf][yf], _drY[xf][yf], _drX[xf][yf], _height - _drY[xf][yf], white);
+				_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf],
+				               _drX[xf + _frntx][yf + _frnty], _height - _drY[xf + _frntx][yf + _frnty], white);
+
+				left = MAX(_drX[xf][yf], left);
+				xf += _frntx;
+				yf += _frnty;
+				xs += _frntx;
+				ys += _frnty;
+				cellx += _frntx;
+				celly += _frnty;
+				rx -= _tcos;
+				ry += _tsin;
+				i++;
+			}
+
+			if (_flip)
+				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+			_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf], _drX[xf][yf], _drY[xf][yf], white);
+			left = MAX(_drX[xf][yf], left);
+		}
+
+		if (i < len && left <= right) {
+			j = 0;
+			xf2 = xf - _sidex;
+			yf2 = yf - _sidey;
+			xfstart = xf2;
+			yfstart = yf2;
+			xsstart = xs - _sidex;
+			ysstart = ys - _sidey;
+			cellxsave = cellx;
+			cellysave = celly;
+
+			rox = rx;
+			roy = ry;
+			if (_change) {
+				perspective(dr, rx, ry);
+				_drX[xf2][yf2] = dr[0];
+				_drY[xf2][yf2] = dr[1];
+			}
+
+			while (!(wallAt(xs, ys) & _side) && i < len) {
+				rx -= _tcos;
+				ry += _tsin;
+				if (_change) {
+					perspective(dr, rx, ry);
+					_drX[xf2 + _frntx][yf2 + _frnty] = dr[0];
+					_drY[xf2 + _frntx][yf2 + _frnty] = dr[1];
+				}
+
+				if (_drX[xf + _frntx][yf + _frnty] > left) {
+					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2],
+					               _drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty], white);
+					_gfx->drawLine(_drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty],
+					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
+					if (wallAt(cellx, celly) & ~0x03)
+						features(cellx, celly, xf2 + _frntx, yf2 + _frnty, left, right, rx, ry);
+					if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
+						if (_robotArray[cellx][celly])
+							setRobot(left, right, _robotArray[cellx][celly]);
+						if (_foodArray[cellx][celly])
+							setRobot(left, right, _foodArray[cellx][celly]);
+					}
+				} else {
+					j = 0;
+					xfstart = xf2;
+					yfstart = yf2;
+					xsstart = xs - _sidex;
+					ysstart = ys - _sidey;
+					rox = rx + _tcos;
+					roy = ry - _tsin;
+					cellxsave = cellx;
+					cellysave = celly;
+				}
+
+				xf2 += _frntx;
+				yf2 += _frnty;
+				xf += _frntx;
+				yf += _frnty;
+				xs += _frntx;
+				ys += _frnty;
+				cellx += _frntx;
+				celly += _frnty;
+				i++;
+				j++;
+			}
+
+			if (wallAt(xf - _sidex, yf - _sidey) & _front) {
+				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2], _drX[xf][yf], _height - _drY[xf][yf], white);
+
+				if (MIN(_drX[xf2][yf2], right) >= left) {
+					checkleft(xsstart, ysstart, xfstart, yfstart, left, MIN(right, _drX[xf2][yf2]),
+					          rox, roy, cellxsave, cellysave, j);
+				}
+			} else {
+				if (_flip)
+					_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+				xestart = xf2;
+				yestart = yf2;
+
+				while (!(wallAt(xf2, yf2) & _front)) {
+					rx -= _tcos;
+					ry += _tsin;
+					cellx += _frntx;
+					celly += _frnty;
+					xf2 += _frntx;
+					yf2 += _frnty;
+					xf += _frntx;
+					yf += _frnty;
+					xs += _frntx;
+					ys += _frnty;
+					if (_change) {
+						perspective(dr, rx, ry);
+						_drX[xf2][yf2] = dr[0];
+						_drY[xf2][yf2] = dr[1];
+					}
+					if (_change) {
+						perspective(dr, rx + _tsin, ry + _tcos);
+						_drX[xf][yf] = dr[0];
+						_drY[xf][yf] = dr[1];
+					}
+					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
+					if (wallAt(cellx - _frntx, celly - _frnty) & ~0x03)
+						features(cellx - _frntx, celly - _frnty, xf2, yf2, left, right, rx, ry);
+					const int objxL = cellx - _frntx;
+					const int objyL = celly - _frnty;
+					if (objxL >= 0 && objxL < 32 && objyL >= 0 && objyL < 32) {
+						if (_robotArray[objxL][objyL])
+							setRobot(left, right, _robotArray[objxL][objyL]);
+						if (_foodArray[objxL][objyL])
+							setRobot(left, right, _foodArray[objxL][objyL]);
+					}
+					i++;
+					j++;
+				}
+
+				_gfx->drawLine(_drX[xestart][yestart], _drY[xestart][yestart], _drX[xf2][yf2], _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2],
+				               _drX[xf2 + _sidex][yf2 + _sidey], _height - _drY[xf2 + _sidex][yf2 + _sidey], white);
+
+				if (MIN(_drX[xf2][yf2], right) >= left) {
+					checkleft(xsstart, ysstart, xfstart, yfstart, left, MIN(_drX[xf2][yf2], right),
+					          rox, roy, cellxsave, cellysave, j);
+				}
+			}
+		}
+	}
+}
+
+void ColonyEngine::checkright(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len) {
+	int i = 0, j;
+	int xf2, yf2;
+	int rox, roy;
+	int xsstart, ysstart;
+	int xfstart, yfstart;
+	int xestart, yestart;
+	int cellxsave, cellysave;
+	int dr[2];
+	uint32 white = _gfx->white();
+
+	cellx += _sidex;
+	celly += _sidey;
+	rx = rx + _tsin;
+	ry = ry + _tcos;
+
+	while (i < len && left < right) {
+		if (wallAt(xs, ys) & _side) {
+			if (_flip)
+				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+
+			while ((wallAt(xs, ys) & _side) && i < len && left < right) {
+				_gfx->drawLine(_drX[xf][yf], _drY[xf][yf], _drX[xf][yf], _height - _drY[xf][yf], white);
+				_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf],
+				               _drX[xf + _frntx][yf + _frnty], _height - _drY[xf + _frntx][yf + _frnty], white);
+
+				right = MIN(_drX[xf][yf], right);
+				xf += _frntx;
+				yf += _frnty;
+				xs += _frntx;
+				ys += _frnty;
+				cellx += _frntx;
+				celly += _frnty;
+				rx -= _tcos;
+				ry += _tsin;
+				i++;
+			}
+
+			if (_flip)
+				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+			_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf], _drX[xf][yf], _drY[xf][yf], white);
+			right = MIN(_drX[xf][yf], right);
+		}
+
+		if (i < len && left < right) {
+			j = 0;
+			xf2 = xf + _sidex;
+			yf2 = yf + _sidey;
+			xfstart = xf2;
+			yfstart = yf2;
+			xsstart = xs + _sidex;
+			ysstart = ys + _sidey;
+			cellxsave = cellx;
+			cellysave = celly;
+
+			rox = rx;
+			roy = ry;
+			if (_change) {
+				perspective(dr, rx, ry);
+				_drX[xf2][yf2] = dr[0];
+				_drY[xf2][yf2] = dr[1];
+			}
+
+			while (!(wallAt(xs, ys) & _side) && i < len) {
+				rx -= _tcos;
+				ry += _tsin;
+				if (_change) {
+					perspective(dr, rx, ry);
+					_drX[xf2 + _frntx][yf2 + _frnty] = dr[0];
+					_drY[xf2 + _frntx][yf2 + _frnty] = dr[1];
+				}
+
+				if (_drX[xf + _frntx][yf + _frnty] < right) {
+					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2],
+					               _drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty], white);
+					_gfx->drawLine(_drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty],
+					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
+					if (wallAt(cellx, celly) & ~0x03)
+						features(cellx, celly, xf + _frntx, yf + _frnty, left, right, rx - _tsin, ry - _tcos);
+					if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
+						if (_robotArray[cellx][celly])
+							setRobot(left, right, _robotArray[cellx][celly]);
+						if (_foodArray[cellx][celly])
+							setRobot(left, right, _foodArray[cellx][celly]);
+					}
+				} else {
+					j = 0;
+					xfstart = xf2;
+					yfstart = yf2;
+					xsstart = xs + _sidex;
+					ysstart = ys + _sidey;
+					rox = rx + _tcos;
+					roy = ry - _tsin;
+					cellxsave = cellx;
+					cellysave = celly;
+				}
+
+				xf2 += _frntx;
+				yf2 += _frnty;
+				xf += _frntx;
+				yf += _frnty;
+				xs += _frntx;
+				ys += _frnty;
+				cellx += _frntx;
+				celly += _frnty;
+				i++;
+				j++;
+			}
+
+			if (wallAt(xf, yf) & _front) {
+				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2], _drX[xf][yf], _height - _drY[xf][yf], white);
+
+				if (MAX(_drX[xf2][yf2], left) < right) {
+					checkright(xsstart, ysstart, xfstart, yfstart, MAX(left, _drX[xf2][yf2]), right,
+					           rox, roy, cellxsave, cellysave, j);
+				}
+			} else {
+				if (_flip)
+					_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
+				xestart = xf2;
+				yestart = yf2;
+
+				while (!(wallAt(xf, yf) & _front)) {
+					rx -= _tcos;
+					ry += _tsin;
+					cellx += _frntx;
+					celly += _frnty;
+					xf2 += _frntx;
+					yf2 += _frnty;
+					xf += _frntx;
+					yf += _frnty;
+					xs += _frntx;
+					ys += _frnty;
+					if (_change) {
+						perspective(dr, rx, ry);
+						_drX[xf2][yf2] = dr[0];
+						_drY[xf2][yf2] = dr[1];
+					}
+					if (_change) {
+						perspective(dr, rx - _tsin, ry - _tcos);
+						_drX[xf][yf] = dr[0];
+						_drY[xf][yf] = dr[1];
+					}
+					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
+					if (wallAt(cellx - _frntx, celly - _frnty) & ~0x03)
+						features(cellx - _frntx, celly - _frnty, xf, yf, left, right, rx - _tsin, ry - _tcos);
+					const int objxR = cellx - _frntx;
+					const int objyR = celly - _frnty;
+					if (objxR >= 0 && objxR < 32 && objyR >= 0 && objyR < 32) {
+						if (_robotArray[objxR][objyR])
+							setRobot(left, right, _robotArray[objxR][objyR]);
+						if (_foodArray[objxR][objyR])
+							setRobot(left, right, _foodArray[objxR][objyR]);
+					}
+					i++;
+					j++;
+				}
+
+				_gfx->drawLine(_drX[xestart][yestart], _drY[xestart][yestart], _drX[xf2][yf2], _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
+				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2],
+				               _drX[xf2 - _sidex][yf2 - _sidey], _height - _drY[xf2 - _sidex][yf2 - _sidey], white);
+
+				if (MAX(_drX[xf2][yf2], left) < right) {
+					checkright(xsstart, ysstart, xfstart, yfstart, MAX(_drX[xf2][yf2], left), right,
+					           rox, roy, cellxsave, cellysave, j);
+				}
+			}
+		}
+	}
+}
+
+const uint8 *ColonyEngine::mapFeatureAt(int x, int y, int direction) const {
+	if (x < 0 || x >= 31 || y < 0 || y >= 31 || direction < 0 || direction >= 5)
+		return nullptr;
+	return _mapData[x][y][direction];
+}
+
+void ColonyEngine::frontfeature(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry) {
+	int l[4], r[4];
+
+	l[0] = _drX[xFront][yFront];
+	l[2] = rx - _tcos;
+	l[3] = ry + _tsin;
+	r[0] = _drX[xFront + _sidex][yFront + _sidey];
+	r[2] = rx + _tsin - _tcos;
+	r[3] = ry + _tsin + _tcos;
+	if (_flip) {
+		l[1] = _height - _drY[xFront][yFront];
+		r[1] = _height - _drY[xFront + _sidex][yFront + _sidey];
+	} else {
+		l[1] = _drY[xFront][yFront];
+		r[1] = _drY[xFront + _sidex][yFront + _sidey];
+	}
+
+	if (MAX(left, l[0]) < MIN(right, r[0])) {
+		const uint8 *map = mapFeatureAt(cellx, celly, _direction);
+		if (map && map[0])
+			dowall(cellx, celly, _direction, l, r);
+	}
+}
+
+void ColonyEngine::features(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry) {
+	int l[4], r[4], ll[4], rr[4];
+
+	l[0] = _drX[xFront][yFront];
+	l[2] = rx - _tcos;
+	l[3] = ry + _tsin;
+	r[0] = _drX[xFront + _sidex][yFront + _sidey];
+	r[2] = rx + _tsin - _tcos;
+	r[3] = ry + _tsin + _tcos;
+	if (_flip) {
+		l[1] = _height - _drY[xFront][yFront];
+		r[1] = _height - _drY[xFront + _sidex][yFront + _sidey];
+	} else {
+		l[1] = _drY[xFront][yFront];
+		r[1] = _drY[xFront + _sidex][yFront + _sidey];
+	}
+
+	if (MAX(left, l[0]) + 1 < MIN(right, r[0]) - 1) {
+		const uint8 *map = mapFeatureAt(cellx, celly, _direction);
+		if (map && map[0])
+			dowall(cellx, celly, _direction, l, r);
+	}
+
+	ll[0] = r[0];
+	ll[1] = r[1];
+	ll[2] = rx + _tsin + _tsin;
+	ll[3] = ry + _tcos + _tcos;
+	rr[0] = _drX[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
+	if (_flip)
+		rr[1] = _height - _drY[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
+	else
+		rr[1] = _drY[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
+	rr[2] = rx + _tsin + _tsin + _tcos;
+	rr[3] = ry + _tsin + _tsin - _tcos; // wait, let me check colony.cpp line 1291. ry + _tcos + _tcos - _tsin; 
+	// Ah, I see: rr[3] = ry + _tsin + _tsin - _tcos; wait, I copied it wrong.
+	// colony.cpp:1291: 	rr[3] = ry + _tcos + _tcos - _tsin;
+	// Let me re-copy accurately.
+	
+	rr[3] = ry + _tcos + _tcos - _tsin;
+	if (MAX(left, ll[0]) + 1 < MIN(right, rr[0]) - 1) {
+		const uint8 *map = mapFeatureAt(cellx, celly, g_dirRight[_direction]);
+		if (map && map[0])
+			dowall(cellx, celly, g_dirRight[_direction], ll, rr);
+	}
+
+	ll[0] = _drX[xFront - _frntx][yFront - _frnty];
+	if (_flip)
+		ll[1] = _height - _drY[xFront - _frntx][yFront - _frnty];
+	else
+		ll[1] = _drY[xFront - _frntx][yFront - _frnty];
+	ll[2] = rx + _tcos - _tsin;
+	ll[3] = (ry - _tcos) - _tsin;
+	rr[0] = l[0];
+	rr[1] = l[1];
+	rr[2] = rx - _tsin;
+	rr[3] = ry - _tcos;
+	if (MAX(left, ll[0]) + 1 < MIN(right, rr[0]) - 1) {
+		const uint8 *map = mapFeatureAt(cellx, celly, g_dirLeft[_direction]);
+		if (map && map[0])
+			dowall(cellx, celly, g_dirLeft[_direction], ll, rr);
+	}
+}
+
+void ColonyEngine::dowall(int cellx, int celly, int direction, int left[4], int right[4]) {
+	const uint8 *map = mapFeatureAt(cellx, celly, direction);
+	int left2[2], right2[2];
+	if (!map)
+		return;
+
+	switch (map[0]) {
+	case kWallFeatureDoor:
+		if (_level == 1 || _level == 5 || _level == 6) {
+			if (map[1] == 0)
+				drawOpenSSDoor(left, right);
+			else
+				drawClosedSSDoor(left, right);
+		} else {
+			if (map[1] == 0) {
+				perspective(left2, left[2], left[3]);
+				perspective(right2, right[2], right[3]);
+				if (_flip) {
+					left2[1] = _height - left2[1];
+					right2[1] = _height - right2[1];
+				}
+				drawOpenDoor(left, right, left2, right2);
+			} else {
+				drawClosedDoor(left, right);
+			}
+		}
+		break;
+	case kWallFeatureWindow:
+		drawWindow(left, right);
+		break;
+	case kWallFeatureShelves:
+		perspective(left2, left[2], left[3]);
+		perspective(right2, right[2], right[3]);
+		if (_flip) {
+			left2[1] = _height - left2[1];
+			right2[1] = _height - right2[1];
+		}
+		drawBooks(left, right, left2, right2);
+		break;
+	case kWallFeatureUpStairs:
+		perspective(left2, left[2], left[3]);
+		perspective(right2, right[2], right[3]);
+		if (_flip) {
+			left2[1] = _height - left2[1];
+			right2[1] = _height - right2[1];
+		}
+		drawUpStairs(left, right, left2, right2);
+		break;
+	case kWallFeatureDnStairs:
+		perspective(left2, left[2], left[3]);
+		perspective(right2, right[2], right[3]);
+		if (_flip) {
+			left2[1] = _height - left2[1];
+			right2[1] = _height - right2[1];
+		}
+		drawDnStairs(left, right, left2, right2);
+		break;
+	case kWallFeatureGlyph:
+		drawGlyphs(left, right);
+		break;
+	case kWallFeatureElevator:
+		drawElevator(left, right);
+		break;
+	case kWallFeatureTunnel:
+		perspective(left2, left[2], left[3]);
+		perspective(right2, right[2], right[3]);
+		if (_flip) {
+			left2[1] = _height - left2[1];
+			right2[1] = _height - right2[1];
+		}
+		drawTunnel(left, right, left2, right2);
+		break;
+	case kWallFeatureAirlock:
+		if (map[1] == 0)
+			drawALOpen(left, right);
+		else
+			drawALClosed(left, right);
+		break;
+	case kWallFeatureColor:
+		drawColor(map, left, right);
+		break;
+	default:
+		break;
+	}
+}
+
+void ColonyEngine::drawWindow(int left[4], int right[4]) {
+	const uint32 dark = 160;
+	int x1 = left[0];
+	int x2 = right[0];
+	int y1 = left[1];
+	int y2 = right[1];
+	int y3 = _height - right[1];
+	int y4 = _height - left[1];
+	int xc = (x1 + x2) >> 1;
+	int xx1 = (xc + x1) >> 1;
+	int xx2 = (xc + x2) >> 1;
+	if (xx2 < _screenR.left || xx1 > _screenR.right)
+		return;
+	int yl = (y1 + y4) >> 1;
+	int yr = (y2 + y3) >> 1;
+	int yy1 = (yl + y1) >> 1;
+	int yy2 = (yr + y2) >> 1;
+	int yy3 = (yl + y3) >> 1;
+	int yy4 = (yr + y4) >> 1;
+	int yy[4];
+	yy[0] = _height - ((((yy1 + yy2) >> 1) + yy1) >> 1);
+	yy[1] = _height - ((((yy1 + yy2) >> 1) + yy2) >> 1);
+	yy[2] = _height - ((((yy3 + yy4) >> 1) + yy3) >> 1);
+	yy[3] = _height - ((((yy3 + yy4) >> 1) + yy4) >> 1);
+	_gfx->drawLine(xx1, yy[0], xx2, yy[1], dark);
+	_gfx->drawLine(xx2, yy[1], xx2, yy[2], dark);
+	_gfx->drawLine(xx2, yy[2], xx1, yy[3], dark);
+	_gfx->drawLine(xx1, yy[3], xx1, yy[0], dark);
+	_gfx->drawLine(xc, (yy[0] + yy[1]) >> 1, xc, (yy[2] + yy[3]) >> 1, dark);
+	_gfx->drawLine(xx1, yl, xx2, yr, dark);
+}
+
+void ColonyEngine::drawClosedDoor(int left[4], int right[4]) {
+	const uint32 dark = 160;
+	int x1 = left[0];
+	int x2 = right[0];
+	int y1 = left[1];
+	int y2 = right[1];
+	int y3 = _height - right[1];
+	int y4 = _height - left[1];
+	int xc = (x1 + x2) >> 1;
+	int xx1 = (xc + x1) >> 1;
+	int xx2 = (xc + x2) >> 1;
+	if (xx2 < _screenR.left || xx1 > _screenR.right)
+		return;
+
+	int yc = (y1 + y2) >> 1;
+	int ytl = (yc + y1) >> 1;
+	int ytr = (yc + y2) >> 1;
+	yc = (y4 + y3) >> 1;
+	int ybl = (yc + y4) >> 1;
+	int ybr = (yc + y3) >> 1;
+	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
+	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
+
+	_gfx->drawLine(xx1, ybl, xx1, ytl, dark);
+	_gfx->drawLine(xx1, ytl, xx2, ytr, dark);
+	_gfx->drawLine(xx2, ytr, xx2, ybr, dark);
+	_gfx->drawLine(xx2, ybr, xx1, ybl, dark);
+
+	ybl = (ybl + ytl) >> 1;
+	ybr = (ybr + ytr) >> 1;
+	yc = (ybl + ybr) >> 1;
+	ybl = (((yc + ybl) >> 1) + ybl) >> 1;
+	ybr = (((yc + ybr) >> 1) + ybr) >> 1;
+	xx1 = (((xx1 + xc) >> 1) + xx1) >> 1;
+	xx2 = (((xx2 + xc) >> 1) + xx2) >> 1;
+	_gfx->drawLine(xx1, ybl, xx2, ybr, dark);
+}
+
+void ColonyEngine::drawOpenDoor(int left[4], int right[4], int left2[2], int right2[2]) {
+	const uint32 dark = 160;
+	const uint32 light = 210;
+	int x1 = left[0];
+	int x2 = right[0];
+	int y1 = left[1];
+	int y2 = right[1];
+	int y3 = _height - right[1];
+	int y4 = _height - left[1];
+	int xc = (x1 + x2) >> 1;
+	int xl = (xc + x1) >> 1;
+	int xr = (xc + x2) >> 1;
+	int yc = (y1 + y2) >> 1;
+	int ytl = (yc + y1) >> 1;
+	int ytr = (yc + y2) >> 1;
+	yc = (y4 + y3) >> 1;
+	int ybl = (yc + y4) >> 1;
+	int ybr = (yc + y3) >> 1;
+	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
+	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
+	if (xr < _screenR.left || xl > _screenR.right)
+		return;
+
+	_gfx->drawLine(xl, ybl, xl, ytl, dark);
+	_gfx->drawLine(xl, ytl, xr, ytr, dark);
+	_gfx->drawLine(xr, ytr, xr, ybr, dark);
+	_gfx->drawLine(xr, ybr, xl, ybl, dark);
+
+	x1 = left2[0];
+	x2 = right2[0];
+	y1 = _height - left2[1];
+	y2 = _height - right2[1];
+	xc = (x1 + x2) >> 1;
+	int xfl = (xc + x1) >> 1;
+	int xfr = (xc + x2) >> 1;
+	yc = (y1 + y2) >> 1;
+	int yfl = (yc + y1) >> 1;
+	int yfr = (yc + y2) >> 1;
+
+	_gfx->drawLine(xl, ybl, xfl, yfl, light);
+	_gfx->drawLine(xfl, yfl, xfr, yfr, light);
+	_gfx->drawLine(xfr, yfr, xr, ybr, light);
+	_gfx->drawLine(xr, ybr, xl, ybl, light);
+}
+
+void ColonyEngine::drawTunnel(int left[4], int right[4], int left2[2], int right2[2]) {
+	const uint32 dark = 120;
+	int baseX[7], baseY[7], tunnelY[7][7];
+	int xl = left[0];
+	int xr = right[0];
+	int ytl = left[1];
+	int ytr = right[1];
+	int ybr = _height - right[1];
+	int ybl = _height - left[1];
+	int hl = ybl - ytl;
+	int hr = ybr - ytr;
+	(void)left2;
+	(void)right2;
+	(void)MAX(hl, hr);
+	split7(baseX, xl, xr);
+	if (baseX[0] > _screenR.right || baseX[6] < _screenR.left)
+		return;
+	split7(baseY, ybl, ybr);
+	for (int i = 0; i < 7; i++)
+		split7(tunnelY[i], baseY[i], _height - baseY[i]);
+
+	int x[6] = {baseX[0], baseX[0], baseX[1], baseX[5], baseX[6], baseX[6]};
+	int y[6] = {baseY[0], tunnelY[0][5], tunnelY[1][6], tunnelY[5][6], tunnelY[6][5], baseY[6]};
+	for (int i = 0; i < 6; i++) {
+		int n = (i + 1) % 6;
+		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
+	}
+}
+
+void ColonyEngine::drawGlyphs(int left[4], int right[4]) {
+	const uint32 dark = 170;
+	int xl = left[0];
+	int xr = right[0];
+	int y1 = left[1];
+	int y2 = right[1];
+	int y3 = _height - right[1];
+	int y4 = _height - left[1];
+	int xc = (xl + xr) >> 1;
+	xl = (((xc + xl) >> 1) + xl) >> 1;
+	xr = (((xc + xr) >> 1) + xr) >> 1;
+	int ytc = (y1 + y2) >> 1;
+	int ybc = (y3 + y4) >> 1;
+	int ytl = (((y1 + ytc) >> 1) + y1) >> 1;
+	int ytr = (((y2 + ytc) >> 1) + y2) >> 1;
+	int ybl = (((y4 + ybc) >> 1) + y4) >> 1;
+	int ybr = (((y3 + ybc) >> 1) + y3) >> 1;
+	int yl1 = (ytl + ybl) >> 1;
+	int yr1 = (ytr + ybr) >> 1;
+	int yl2 = (yl1 + ytl) >> 1;
+	int yr2 = (yr1 + ytr) >> 1;
+	int yl3 = (yl2 + yl1) >> 1;
+	int yr3 = (yr2 + yr1) >> 1;
+	int yl4 = (yl1 + ybl) >> 1;
+	int yr4 = (yr1 + ybr) >> 1;
+	int yr5 = (yr4 + yr1) >> 1;
+	int yl5 = (yl4 + yl1) >> 1;
+
+	_gfx->drawLine(xl, yl1, xr, yr1, dark);
+	_gfx->drawLine(xl, yl2, xr, yr2, dark);
+	_gfx->drawLine(xl, yl3, xr, yr3, dark);
+	_gfx->drawLine(xl, yl4, xr, yr4, dark);
+	_gfx->drawLine(xl, yl5, xr, yr5, dark);
+	_gfx->drawLine(xl, (yl2 + yl3) >> 1, xr, (yr2 + yr3) >> 1, dark);
+	_gfx->drawLine(xl, (yl3 + yl1) >> 1, xr, (yr3 + yr1) >> 1, dark);
+	_gfx->drawLine(xl, (yl1 + yl5) >> 1, xr, (yr1 + yr5) >> 1, dark);
+	_gfx->drawLine(xl, (yl4 + yl5) >> 1, xr, (yr4 + yr5) >> 1, dark);
+}
+
+void ColonyEngine::drawBooks(int left[4], int right[4], int left2[2], int right2[2]) {
+	const uint32 dark = 170;
+	int l2[2] = {left2[0], left2[1]};
+	int r2[2] = {right2[0], right2[1]};
+	for (int i = 0; i < 2; i++) {
+		l2[0] = (l2[0] + left[0]) >> 1;
+		l2[1] = (l2[1] + left[1]) >> 1;
+		r2[0] = (r2[0] + right[0]) >> 1;
+		r2[1] = (r2[1] + right[1]) >> 1;
+	}
+	_gfx->drawLine(l2[0], l2[1], l2[0], _height - l2[1], dark);
+	_gfx->drawLine(l2[0], _height - l2[1], r2[0], _height - r2[1], dark);
+	_gfx->drawLine(r2[0], _height - r2[1], r2[0], r2[1], dark);
+	_gfx->drawLine(r2[0], r2[1], l2[0], l2[1], dark);
+	_gfx->drawLine(left[0], left[1], l2[0], l2[1], dark);
+	_gfx->drawLine(left[0], _height - left[1], l2[0], _height - l2[1], dark);
+	_gfx->drawLine(right[0], right[1], r2[0], r2[1], dark);
+	_gfx->drawLine(right[0], _height - right[1], r2[0], _height - r2[1], dark);
+
+	int lf[7], rf[7], lb[7], rb[7];
+	split7(lf, left[1], _height - left[1]);
+	split7(rf, right[1], _height - right[1]);
+	split7(lb, l2[1], _height - l2[1]);
+	split7(rb, r2[1], _height - r2[1]);
+	for (int i = 0; i < 7; i++) {
+		_gfx->drawLine(left[0], lf[i], right[0], rf[i], dark);
+		_gfx->drawLine(right[0], rf[i], r2[0], rb[i], dark);
+		_gfx->drawLine(r2[0], rb[i], l2[0], lb[i], dark);
+		_gfx->drawLine(l2[0], lb[i], left[0], lf[i], dark);
+	}
+}
+
+void ColonyEngine::drawUpStairs(int left[4], int right[4], int left2[2], int right2[2]) {
+	const uint32 dark = 170;
+	int xl[7], xr[7], yl[7], yr[7];
+	split7(xl, left[0], left2[0]);
+	split7(xr, right[0], right2[0]);
+	split7(yl, _height - left[1], left2[1]);
+	split7(yr, _height - right[1], right2[1]);
+	for (int i = 0; i < 6; i++) {
+		_gfx->drawLine(xl[i], yl[i], xl[i + 1], yl[i + 1], dark);
+		_gfx->drawLine(xr[i], yr[i], xr[i + 1], yr[i + 1], dark);
+		_gfx->drawLine(xl[i], yl[i], xr[i], yr[i], dark);
+	}
+}
+
+void ColonyEngine::drawDnStairs(int left[4], int right[4], int left2[2], int right2[2]) {
+	const uint32 dark = 170;
+	int xl[7], xr[7], yl[7], yr[7];
+	split7(xl, left[0], left2[0]);
+	split7(xr, right[0], right2[0]);
+	split7(yl, left[1], left2[1]);
+	split7(yr, right[1], right2[1]);
+	for (int i = 0; i < 6; i++) {
+		_gfx->drawLine(xl[i], yl[i], xl[i + 1], yl[i + 1], dark);
+		_gfx->drawLine(xr[i], yr[i], xr[i + 1], yr[i + 1], dark);
+		_gfx->drawLine(xl[i], _height - yl[i], xr[i], _height - yr[i], dark);
+	}
+}
+
+void ColonyEngine::drawALOpen(int left[4], int right[4]) {
+	const uint32 dark = 150;
+	int lr[7], ud[7][7];
+	split7x7(left, right, lr, ud);
+	int x[8] = {lr[0], lr[1], lr[3], lr[5], lr[6], lr[5], lr[3], lr[1]};
+	int y[8] = {ud[3][0], ud[5][1], ud[6][3], ud[5][5], ud[3][6], ud[1][5], ud[0][3], ud[1][1]};
+	for (int i = 0; i < 8; i++) {
+		int n = (i + 1) % 8;
+		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
+	}
+}
+
+void ColonyEngine::drawALClosed(int left[4], int right[4]) {
+	const uint32 dark = 170;
+	int lr[7], ud[7][7];
+	split7x7(left, right, lr, ud);
+	int x[8] = {lr[0], lr[1], lr[3], lr[5], lr[6], lr[5], lr[3], lr[1]};
+	int y[8] = {ud[3][0], ud[5][1], ud[6][3], ud[5][5], ud[3][6], ud[1][5], ud[0][3], ud[1][1]};
+	for (int i = 0; i < 8; i++) {
+		int n = (i + 1) % 8;
+		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
+	}
+	_gfx->drawLine(lr[0], ud[3][0], lr[3], ud[3][3], dark);
+	_gfx->drawLine(lr[3], ud[6][3], lr[3], ud[3][3], dark);
+	_gfx->drawLine(lr[6], ud[3][6], lr[3], ud[3][3], dark);
+	_gfx->drawLine(lr[3], ud[0][3], lr[3], ud[3][3], dark);
+}
+
+void ColonyEngine::drawOpenSSDoor(int left[4], int right[4]) {
+	const uint32 dark = 140;
+	int lr[7], ud[7][7];
+	split7x7(left, right, lr, ud);
+	int x[8] = {lr[2], lr[1], lr[1], lr[2], lr[4], lr[5], lr[5], lr[4]};
+	int y[8] = {ud[0][2], ud[1][1], ud[5][1], ud[6][2], ud[6][4], ud[5][5], ud[1][5], ud[0][4]};
+	for (int i = 0; i < 8; i++) {
+		int n = (i + 1) % 8;
+		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
+	}
+}
+
+void ColonyEngine::drawClosedSSDoor(int left[4], int right[4]) {
+	const uint32 dark = 170;
+	int lr[7], ud[7][7];
+	split7x7(left, right, lr, ud);
+	int x[8] = {lr[2], lr[1], lr[1], lr[2], lr[4], lr[5], lr[5], lr[4]};
+	int y[8] = {ud[0][2], ud[1][1], ud[5][1], ud[6][2], ud[6][4], ud[5][5], ud[1][5], ud[0][4]};
+	for (int i = 0; i < 8; i++) {
+		int n = (i + 1) % 8;
+		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
+	}
+	_gfx->drawLine(lr[2], ud[1][2], lr[2], ud[5][2], dark);
+	_gfx->drawLine(lr[2], ud[5][2], lr[4], ud[5][4], dark);
+	_gfx->drawLine(lr[4], ud[5][4], lr[4], ud[1][4], dark);
+	_gfx->drawLine(lr[4], ud[1][4], lr[2], ud[1][2], dark);
+}
+
+void ColonyEngine::drawElevator(int left[4], int right[4]) {
+	const uint32 dark = 170;
+	int x1 = left[0];
+	int x2 = right[0];
+	int y1 = left[1];
+	int y2 = right[1];
+	int y3 = _height - right[1];
+	int y4 = _height - left[1];
+	int xc = (x1 + x2) >> 1;
+	int xx1 = (xc + x1) >> 1;
+	xx1 = (x1 + xx1) >> 1;
+	int xx2 = (xc + x2) >> 1;
+	xx2 = (x2 + xx2) >> 1;
+	if (xx2 < _screenR.left || xx1 > _screenR.right)
+		return;
+	int ytc = (y1 + y2) >> 1;
+	int ytl = (ytc + y1) >> 1;
+	ytl = (ytl + y1) >> 1;
+	int ytr = (ytc + y2) >> 1;
+	ytr = (ytr + y2) >> 1;
+	int ybc = (y4 + y3) >> 1;
+	int ybl = (ybc + y4) >> 1;
+	ybl = (ybl + y4) >> 1;
+	int ybr = (ybc + y3) >> 1;
+	ybr = (ybr + y3) >> 1;
+	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
+	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
+	_gfx->drawLine(xx1, ybl, xx1, ytl, dark);
+	_gfx->drawLine(xx1, ytl, xx2, ytr, dark);
+	_gfx->drawLine(xx2, ytr, xx2, ybr, dark);
+	_gfx->drawLine(xx2, ybr, xx1, ybl, dark);
+	_gfx->drawLine(xc, ybc, xc, (ytl + ytr) >> 1, dark);
+}
+
+void ColonyEngine::drawColor(const uint8 *map, int left[4], int right[4]) {
+	int xl = left[0];
+	int xr = right[0];
+	int yl[5], yr[5];
+	yl[0] = left[1];
+	yr[0] = right[1];
+	yl[4] = _height - yl[0];
+	yr[4] = _height - yr[0];
+	yl[2] = (yl[0] + yl[4]) >> 1;
+	yr[2] = (yr[0] + yr[4]) >> 1;
+	yl[1] = (yl[0] + yl[2]) >> 1;
+	yl[3] = (yl[2] + yl[4]) >> 1;
+	yr[1] = (yr[0] + yr[2]) >> 1;
+	yr[3] = (yr[2] + yr[4]) >> 1;
+
+	if (map[1] || map[2] || map[3] || map[4]) {
+		for (int i = 1; i <= 3; i++) {
+			uint32 c = 120 + map[i] * 20;
+			_gfx->drawLine(xl, yl[i], xr, yr[i], c);
+		}
+	} else {
+		uint32 c = 100 + (_level * 15);
+		_gfx->drawLine(xl, yl[1], xr, yr[1], c);
+		_gfx->drawLine(xl, yl[2], xr, yr[2], c);
+		_gfx->drawLine(xl, yl[3], xr, yr[3], c);
+	}
+}
+
+void ColonyEngine::split7(int arr[7], int x1, int x2) const {
+	arr[3] = (x1 + x2) >> 1;
+	arr[1] = (x1 + arr[3]) >> 1;
+	arr[0] = (x1 + arr[1]) >> 1;
+	arr[2] = (arr[1] + arr[3]) >> 1;
+	arr[5] = (arr[3] + x2) >> 1;
+	arr[6] = (arr[5] + x2) >> 1;
+	arr[4] = (arr[3] + arr[5]) >> 1;
+}
+
+void ColonyEngine::split7x7(int left[4], int right[4], int lr[7], int ud[7][7]) const {
+	int leftX, rightX, leftY, rightY;
+	int lud[7], rud[7];
+	if (right[0] < left[0]) {
+		rightX = left[0];
+		leftX = right[0];
+		rightY = left[1];
+		leftY = right[1];
+	} else {
+		leftX = left[0];
+		rightX = right[0];
+		leftY = left[1];
+		rightY = right[1];
+	}
+	split7(lr, leftX, rightX);
+	if (_flip) {
+		split7(lud, leftY, _height - leftY);
+		split7(rud, rightY, _height - rightY);
+	} else {
+		split7(lud, _height - leftY, leftY);
+		split7(rud, _height - rightY, rightY);
+	}
+	for (int i = 0; i < 7; i++)
+		split7(ud[i], lud[i], rud[i]);
+}
+
+bool ColonyEngine::projectWorld(int worldX, int worldY, int &screenX, int &depth) const {
+	long x = worldX - _me.xloc;
+	long y = worldY - _me.yloc;
+	long tsin = _cost[_me.look];
+	long tcos = _sint[_me.look];
+	long xx = (x * tcos - y * tsin) >> 7;
+	long yy = (x * tsin + y * tcos) >> 7;
+
+	if (yy <= 16)
+		return false;
+	if (yy >= 11585)
+		yy = 11584;
+
+	screenX = _centerX + (int)(((int64)xx * 256) / yy);
+	depth = (int)yy;
+	return true;
+}
+
+uint32 ColonyEngine::objectColor(int type) const {
+	switch (type) {
+	case 21: // DESK
+		return 220;
+	case 22: // PLANT
+		return 100;
+	case 24: // BED
+	case 42: // BBED
+		return 180;
+	case 29: // SCREEN
+	case 30: // CONSOLE
+		return 240;
+	case 31: // POWERSUIT
+	case 46: // REACTOR
+		return 255;
+	case 36: // TELEPORT
+		return 140;
+	default:
+		return 160 + ((uint32)(type * 7) & 0x3F);
+	}
+}
+
+bool ColonyEngine::isSurfaceClockwise(const ProjectedPrismPart &part, const int surface[8]) const {
+	const int n = surface[1];
+	for (int i = 2; i < n; i++) {
+		const int ia = surface[i];
+		const int ib = surface[i + 1];
+		const int ic = surface[i + 2];
+		if (ia < 0 || ia >= part.pointCount || ib < 0 || ib >= part.pointCount || ic < 0 || ic >= part.pointCount)
+			continue;
+		const long dx = part.x[ia] - part.x[ib];
+		const long dy = part.y[ia] - part.y[ib];
+		const long dxp = part.x[ic] - part.x[ib];
+		const long dyp = part.y[ic] - part.y[ib];
+		if (dx < 0) {
+			if (dy == 0) {
+				if (dyp > 0)
+					return false;
+				if (dyp < 0)
+					return true;
+			} else {
+				const long b = dy * dxp - dx * dyp;
+				if (b > 0)
+					return false;
+				if (b < 0)
+					return true;
+			}
+		} else if (dx > 0) {
+			if (dy == 0) {
+				if (dyp < 0)
+					return false;
+				if (dyp > 0)
+					return true;
+			} else {
+				const long b = dx * dyp - dy * dxp;
+				if (b < 0)
+					return false;
+				if (b > 0)
+					return true;
+			}
+		} else {
+			if (dy < 0) {
+				if (dxp > 0)
+					return true;
+				if (dxp < 0)
+					return false;
+			}
+			if (dy > 0) {
+				if (dxp < 0)
+					return true;
+				if (dxp > 0)
+					return false;
+			}
+		}
+	}
+	return false;
+}
+
+bool ColonyEngine::projectPrismPart(const Thing &obj, const PrismPartDef &part, bool useLook, ProjectedPrismPart &out) const {
+	out.pointCount = CLIP<int>(part.pointCount, 0, ProjectedPrismPart::kMaxPoints);
+	for (int i = 0; i < ProjectedPrismPart::kMaxSurfaces; i++)
+		out.vsurface[i] = false;
+	out.visible = false;
+	if (out.pointCount <= 0 || !part.points || !part.surfaces)
+		return false;
+
+	const uint8 ang = useLook ? obj.where.look : obj.where.ang;
+	const long rotCos = _cost[ang];
+	const long rotSin = _sint[ang];
+	const long viewSin = _cost[_me.look];
+	const long viewCos = _sint[_me.look];
+
+	int minX = 32000;
+	int maxX = -32000;
+	int minY = 32000;
+	int maxY = -32000;
+	// DOS InitObj() applies: Robot[i][j].pnt[k][2] -= Floor, with Floor == 160.
+	// We keep source geometry unmodified and apply the same offset at projection time.
+	static const int kFloorShift = 160;
+	for (int i = 0; i < out.pointCount; i++) {
+		const int px = part.points[i][0];
+		const int py = part.points[i][1];
+		const int pz = part.points[i][2];
+		const long rx = ((long)px * rotCos - (long)py * rotSin) >> 7;
+		const long ry = ((long)px * rotSin + (long)py * rotCos) >> 7;
+		const long worldX = rx + obj.where.xloc;
+		const long worldY = ry + obj.where.yloc;
+
+		const long tx = worldX - _me.xloc;
+		const long ty = worldY - _me.yloc;
+		const long xx = (tx * viewCos - ty * viewSin) >> 7;
+		long yy = (tx * viewSin + ty * viewCos) >> 7;
+		if (yy <= 16)
+			yy = 16;
+
+		out.x[i] = _centerX + (int)(((int64)xx * 256) / yy);
+		out.depth[i] = (int)yy;
+		const long zrel = (long)pz - kFloorShift;
+		out.y[i] = _centerY - (int)(((int64)zrel * 256) / yy);
+		minX = MIN(minX, out.x[i]);
+		maxX = MAX(maxX, out.x[i]);
+		minY = MIN(minY, out.y[i]);
+		maxY = MAX(maxY, out.y[i]);
+	}
+
+	out.visible = !(maxX < _screenR.left || minX >= _screenR.right || maxY < _screenR.top || minY >= _screenR.bottom);
+	if (!out.visible)
+		return false;
+
+	const int surfCount = CLIP<int>(part.surfaceCount, 0, ProjectedPrismPart::kMaxSurfaces);
+	for (int i = 0; i < surfCount; i++)
+		out.vsurface[i] = isSurfaceClockwise(out, part.surfaces[i]);
+	return true;
+}
+
+bool ColonyEngine::clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const {
+	if (clip.left >= clip.right || clip.top >= clip.bottom)
+		return false;
+	const int l = clip.left;
+	const int r = clip.right - 1;
+	const int t = clip.top;
+	const int b = clip.bottom - 1;
+	auto outCode = [&](int x, int y) {
+		int code = 0;
+		if (x < l)
+			code |= 1;
+		else if (x > r)
+			code |= 2;
+		if (y < t)
+			code |= 4;
+		else if (y > b)
+			code |= 8;
+		return code;
+	};
+
+	int c1 = outCode(x1, y1);
+	int c2 = outCode(x2, y2);
+	while (true) {
+		if ((c1 | c2) == 0)
+			return true;
+		if (c1 & c2)
+			return false;
+
+		const int cOut = c1 ? c1 : c2;
+		int x = 0;
+		int y = 0;
+		if (cOut & 8) {
+			if (y2 == y1)
+				return false;
+			x = x1 + (x2 - x1) * (b - y1) / (y2 - y1);
+			y = b;
+		} else if (cOut & 4) {
+			if (y2 == y1)
+				return false;
+			x = x1 + (x2 - x1) * (t - y1) / (y2 - y1);
+			y = t;
+		} else if (cOut & 2) {
+			if (x2 == x1)
+				return false;
+			y = y1 + (y2 - y1) * (r - x1) / (x2 - x1);
+			x = r;
+		} else {
+			if (x2 == x1)
+				return false;
+			y = y1 + (y2 - y1) * (l - x1) / (x2 - x1);
+			x = l;
+		}
+
+		if (cOut == c1) {
+			x1 = x;
+			y1 = y;
+			c1 = outCode(x1, y1);
+		} else {
+			x2 = x;
+			y2 = y;
+			c2 = outCode(x2, y2);
+		}
+	}
+}
+
+void ColonyEngine::drawProjectedPrism(const ProjectedPrismPart &part, const PrismPartDef &def, int force, uint32 color, const Common::Rect &clip) {
+	const int surfCount = CLIP<int>(def.surfaceCount, 0, ProjectedPrismPart::kMaxSurfaces);
+	for (int i = 0; i < surfCount; i++) {
+		if (!(part.vsurface[i] || force))
+			continue;
+		const int n = def.surfaces[i][1];
+		if (n < 2)
+			continue;
+		int first = def.surfaces[i][2];
+		if (first < 0 || first >= part.pointCount)
+			continue;
+		int prev = first;
+		for (int j = 1; j < n; j++) {
+			const int cur = def.surfaces[i][j + 2];
+			if (cur < 0 || cur >= part.pointCount)
+				continue;
+			int x1 = part.x[prev];
+			int y1 = part.y[prev];
+			int x2 = part.x[cur];
+			int y2 = part.y[cur];
+			if (clipLineToRect(x1, y1, x2, y2, clip))
+				_gfx->drawLine(x1, y1, x2, y2, color);
+			prev = cur;
+		}
+		int x1 = part.x[prev];
+		int y1 = part.y[prev];
+		int x2 = part.x[first];
+		int y2 = part.y[first];
+		if (clipLineToRect(x1, y1, x2, y2, clip))
+			_gfx->drawLine(x1, y1, x2, y2, color);
+	}
+}
+
+bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
+	// DOS object geometry from SCREEN.H / TABLE.H / BED.H / DESK.H.
+	static const int kScreenPts[8][3] = {
+		{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
+		{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
+	};
+	static const int kScreenSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	};
+
+	static const int kTableTopPts[4][3] = {
+		{-128, 128, 100}, {128, 128, 100}, {128, -128, 100}, {-128, -128, 100}
+	};
+	static const int kTableTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kTableBasePts[8][3] = {
+		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+		{-5, 5, 100}, {5, 5, 100}, {5, -5, 100}, {-5, -5, 100}
+	};
+	static const int kTableBaseSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	};
+
+	static const int kBedPostPts[4][3] = {
+		{-82, 128, 100}, {82, 128, 100}, {82, 128, 0}, {-82, 128, 0}
+	};
+	static const int kBBedPostPts[4][3] = {
+		{-130, 128, 100}, {130, 128, 100}, {130, 128, 0}, {-130, 128, 0}
+	};
+	static const int kBedPostSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kBlanketSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
+	};
+	static const int kSheetSurf[3][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+		{0, 4, 7, 6, 5, 4, 0, 0}
+	};
+	static const int kBedBlanketPts[8][3] = {
+		{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
+		{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
+	};
+	static const int kBedSheetPts[8][3] = {
+		{-80, 128, 30}, {80, 128, 30}, {80, 70, 30}, {-80, 70, 30},
+		{-80, 128, 60}, {80, 128, 60}, {80, 70, 60}, {-80, 70, 60}
+	};
+	static const int kBBedBlanketPts[8][3] = {
+		{-128, 70, 0}, {128, 70, 0}, {128, -175, 0}, {-128, -175, 0},
+		{-128, 70, 60}, {128, 70, 60}, {128, -175, 60}, {-128, -175, 60}
+	};
+	static const int kBBedSheetPts[8][3] = {
+		{-128, 128, 30}, {128, 128, 30}, {128, 70, 30}, {-128, 70, 30},
+		{-128, 128, 60}, {128, 128, 60}, {128, 70, 60}, {-128, 70, 60}
+	};
+
+	static const int kDeskTopPts[4][3] = {
+		{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
+	};
+	static const int kDeskTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kDeskLeftPts[8][3] = {
+		{-135, 95, 0}, {-55, 95, 0}, {-55, -95, 0}, {-135, -95, 0},
+		{-135, 95, 100}, {-55, 95, 100}, {-55, -95, 100}, {-135, -95, 100}
+	};
+	static const int kDeskRightPts[8][3] = {
+		{55, 95, 0}, {135, 95, 0}, {135, -95, 0}, {55, -95, 0},
+		{55, 95, 100}, {135, 95, 100}, {135, -95, 100}, {55, -95, 100}
+	};
+	static const int kDeskCabSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	};
+	static const int kSeatPts[4][3] = {
+		{-40, 210, 60}, {40, 210, 60}, {40, 115, 60}, {-40, 115, 60}
+	};
+	static const int kSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kArmLeftPts[4][3] = {
+		{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
+	};
+	static const int kArmRightPts[4][3] = {
+		{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
+	};
+	static const int kArmSurf[2][8] = {
+		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	};
+	static const int kBackPts[4][3] = {
+		{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
+	};
+	static const int kBackSurf[2][8] = {
+		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	};
+	static const int kComputerPts[8][3] = {
+		{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
+		{70, 25, 120}, {120, 25, 120}, {120, -25, 120}, {70, -25, 120}
+	};
+	static const int kMonitorPts[8][3] = {
+		{75, 20, 120}, {115, 20, 120}, {115, -20, 120}, {75, -20, 120},
+		{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
+	};
+	static const int kComputerSurf[5][8] = {
+		{0, 4, 7, 6, 5, 4, 0, 0}, {0, 4, 0, 3, 7, 4, 0, 0},
+		{0, 4, 3, 2, 6, 7, 0, 0}, {0, 4, 1, 0, 4, 5, 0, 0},
+		{0, 4, 2, 1, 5, 6, 0, 0}
+	};
+	static const int kDeskScreenPts[4][3] = {
+		{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
+	};
+	static const int kDeskScreenSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+
+	static const int kCSeatPts[4][3] = {
+		{-40, 40, 60}, {40, 40, 60}, {40, -40, 60}, {-40, -40, 60}
+	};
+	static const int kCSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kCArmLeftPts[4][3] = {
+		{-50, 40, 90}, {-40, 40, 60}, {-40, -40, 60}, {-50, -40, 90}
+	};
+	static const int kCArmLeftSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kCArmRightPts[4][3] = {
+		{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
+	};
+	static const int kCArmRightSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+	static const int kCBackPts[4][3] = {
+		{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
+	};
+	static const int kCBackSurf[2][8] = {
+		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	};
+	static const int kCBasePts[8][3] = {
+		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+		{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
+	};
+	static const int kCBaseSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	};
+
+	static const int kConsolePts[8][3] = {
+		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+		{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
+	};
+	static const int kConsoleSurf[5][8] = {
+		{0, 4, 4, 0, 3, 7, 0, 0}, {0, 4, 7, 3, 2, 6, 0, 0},
+		{0, 4, 5, 1, 0, 4, 0, 0}, {0, 4, 6, 2, 1, 5, 0, 0},
+		{0, 4, 7, 6, 5, 4, 0, 0}
+	};
+
+	static const int kCouchSurf[5][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+		{0, 4, 7, 6, 5, 4, 0, 0}
+	};
+	static const int kACouchPts[8][3] = {
+		{-50, 150, 0}, {50, 150, 0}, {50, -150, 0}, {-50, -150, 0},
+		{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50}
+	};
+	static const int kBCouchPts[8][3] = {
+		{-80, 150, 0}, {-45, 150, 0}, {-45, -150, 0}, {-80, -150, 0},
+		{-80, 150, 120}, {-55, 150, 120}, {-55, -150, 120}, {-80, -150, 120}
+	};
+	static const int kCCouchPts[8][3] = {
+		{-70, 170, 0}, {50, 170, 0}, {50, 150, 0}, {-70, 150, 0},
+		{-70, 170, 80}, {50, 170, 80}, {50, 150, 80}, {-70, 150, 80}
+	};
+	static const int kDCouchPts[8][3] = {
+		{-70, -150, 0}, {50, -150, 0}, {50, -170, 0}, {-70, -170, 0},
+		{-70, -150, 80}, {50, -150, 80}, {50, -170, 80}, {-70, -170, 80}
+	};
+
+	static const int kAChairPts[8][3] = {
+		{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
+		{-50, 50, 50}, {50, 50, 50}, {50, -50, 50}, {-50, -50, 50}
+	};
+	static const int kBChairPts[8][3] = {
+		{-80, 50, 0}, {-45, 50, 0}, {-45, -50, 0}, {-80, -50, 0},
+		{-80, 50, 120}, {-55, 50, 120}, {-55, -50, 120}, {-80, -50, 120}
+	};
+	static const int kCChairPts2[8][3] = {
+		{-70, 70, 0}, {50, 70, 0}, {50, 50, 0}, {-70, 50, 0},
+		{-70, 70, 80}, {50, 70, 80}, {50, 50, 80}, {-70, 50, 80}
+	};
+	static const int kDChairPts[8][3] = {
+		{-70, -50, 0}, {50, -50, 0}, {50, -70, 0}, {-70, -70, 0},
+		{-70, -50, 80}, {50, -50, 80}, {50, -70, 80}, {-70, -70, 80}
+	};
+
+	static const int kTVBodyPts[8][3] = {
+		{-30, 60, 0}, {30, 60, 0}, {30, -60, 0}, {-30, -60, 0},
+		{-30, 60, 120}, {30, 60, 120}, {30, -60, 120}, {-30, -60, 120}
+	};
+	static const int kTVBodySurf[5][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+		{0, 4, 7, 6, 5, 4, 0, 0}
+	};
+	static const int kTVScreenPts[4][3] = {
+		{30, 50, 10}, {30, -50, 10}, {30, 50, 110}, {30, -50, 110}
+	};
+	static const int kTVScreenSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
+
+	static const int kDrawerPts[8][3] = {
+		{-80, 70, 0}, {0, 70, 0}, {0, -70, 0}, {-80, -70, 0},
+		{-80, 70, 100}, {0, 70, 100}, {0, -70, 100}, {-80, -70, 100}
+	};
+	static const int kDrawerSurf[5][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+		{0, 4, 7, 6, 5, 4, 0, 0}
+	};
+	static const int kMirrorPts[4][3] = {
+		{-80, 65, 100}, {-80, -65, 100}, {-80, 65, 210}, {-80, -65, 210}
+	};
+	static const int kMirrorSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
+
+	static const PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
+	static const PrismPartDef kTableParts[2] = {
+		{4, kTableTopPts, 1, kTableTopSurf},
+		{8, kTableBasePts, 4, kTableBaseSurf}
+	};
+	static const PrismPartDef kBedParts[3] = {
+		{4, kBedPostPts, 1, kBedPostSurf},
+		{8, kBedBlanketPts, 4, kBlanketSurf},
+		{8, kBedSheetPts, 3, kSheetSurf}
+	};
+	static const PrismPartDef kBBedParts[3] = {
+		{4, kBBedPostPts, 1, kBedPostSurf},
+		{8, kBBedBlanketPts, 4, kBlanketSurf},
+		{8, kBBedSheetPts, 3, kSheetSurf}
+	};
+	static const PrismPartDef kDeskParts[10] = {
+		{4, kDeskTopPts, 1, kDeskTopSurf},
+		{8, kDeskLeftPts, 4, kDeskCabSurf},
+		{8, kDeskRightPts, 4, kDeskCabSurf},
+		{4, kSeatPts, 1, kSeatSurf},
+		{4, kArmLeftPts, 2, kArmSurf},
+		{4, kArmRightPts, 2, kArmSurf},
+		{4, kBackPts, 2, kBackSurf},
+		{8, kComputerPts, 5, kComputerSurf},
+		{8, kMonitorPts, 5, kComputerSurf},
+		{4, kDeskScreenPts, 1, kDeskScreenSurf}
+	};
+	static const PrismPartDef kCChairParts[5] = {
+		{4, kCSeatPts, 1, kCSeatSurf},
+		{4, kCArmLeftPts, 1, kCArmLeftSurf},
+		{4, kCArmRightPts, 1, kCArmRightSurf},
+		{4, kCBackPts, 2, kCBackSurf},
+		{8, kCBasePts, 4, kCBaseSurf}
+	};
+	static const PrismPartDef kConsolePart = {8, kConsolePts, 5, kConsoleSurf};
+	static const PrismPartDef kCouchParts[4] = {
+		{8, kACouchPts, 5, kCouchSurf},
+		{8, kBCouchPts, 5, kCouchSurf},
+		{8, kCCouchPts, 5, kCouchSurf},
+		{8, kDCouchPts, 5, kCouchSurf}
+	};
+	static const PrismPartDef kChairParts[4] = {
+		{8, kAChairPts, 5, kCouchSurf},
+		{8, kBChairPts, 5, kCouchSurf},
+		{8, kCChairPts2, 5, kCouchSurf},
+		{8, kDChairPts, 5, kCouchSurf}
+	};
+	static const PrismPartDef kTVParts[2] = {
+		{8, kTVBodyPts, 5, kTVBodySurf},
+		{4, kTVScreenPts, 1, kTVScreenSurf}
+	};
+	static const PrismPartDef kDrawerParts[2] = {
+		{8, kDrawerPts, 5, kDrawerSurf},
+		{4, kMirrorPts, 1, kMirrorSurf}
+	};
+
+	/* CWALL (object 49) - ported from DOS INITOBJ.C: simple corner/quarter-wall prism */
+	static const int kCWallPts[8][3] = {
+		{-128, 128, -160}, {0, 112, -160}, {112, 0, -160}, {128, -128, -160},
+		{-128, 128, 160},  {0, 112, 160},  {112, 0, 160},  {128, -128, 160}
+	};
+	static const int kCWallSurf[3][8] = {
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0}
+	};
+	static const PrismPartDef kCWallParts[1] = {{8, kCWallPts, 3, kCWallSurf}};
+
+	/* PLANT (new) - simple pot + stem + two leaf plates (approximates DOS plant) */
+	static const int kPlantPotPts[8][3] = {
+		{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0},
+		{-20, 20, 40}, {20, 20, 40}, {20, -20, 40}, {-20, -20, 40}
+	};
+	static const int kPlantPotSurf[5][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
+	};
+
+	static const int kPlantStemPts[8][3] = {
+		{-5, 5, 40}, {5, 5, 40}, {5, -5, 40}, {-5, -5, 40},
+		{-5, 5, 120}, {5, 5, 120}, {5, -5, 120}, {-5, -5, 120}
+	};
+	static const int kPlantStemSurf[4][8] = {
+		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	};
+
+	static const int kPlantLeaf1Pts[4][3] = {
+		{-40, 0, 120}, {40, 0, 120}, {40, 0, 140}, {-40, 0, 140}
+	};
+	static const int kPlantLeaf2Pts[4][3] = {
+		{0, 40, 120}, {0, -40, 120}, {0, 40, 140}, {0, -40, 140}
+	};
+	static const int kPlantLeafSurf[1][8] = {{0, 4, 0, 1, 2, 3, 0, 0}};
+
+	static const PrismPartDef kPlantParts[4] = {
+		{8, kPlantPotPts, 5, kPlantPotSurf},
+		{8, kPlantStemPts, 4, kPlantStemSurf},
+		{4, kPlantLeaf1Pts, 1, kPlantLeafSurf},
+		{4, kPlantLeaf2Pts, 1, kPlantLeafSurf}
+	};
+
+	const int clipLeft = MAX<int>((int)obj.clip.left, (int)_screenR.left);
+	const int clipTop = MAX<int>((int)obj.clip.top, (int)_screenR.top);
+	const int clipRight = MIN<int>((int)obj.clip.right, (int)_screenR.right);
+	const int clipBottom = MIN<int>((int)obj.clip.bottom, (int)_screenR.bottom);
+	if (clipLeft >= clipRight || clipTop >= clipBottom)
+		return false;
+	Common::Rect drawClip(clipLeft, clipTop, clipRight, clipBottom);
+
+	auto tint = [](uint32 base, int delta) -> uint32 {
+		return (uint32)CLIP<int>((int)base + delta, 0, 255);
+	};
+
+	ProjectedPrismPart p[10];
+	switch (obj.type) {
+	case kObjConsole:
+		if (!projectPrismPart(obj, kConsolePart, false, p[0]) || !p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kConsolePart, 0, tint(baseColor, 0), drawClip);
+		return true;
+	case kObjCChair:
+		for (int i = 0; i < 5; i++)
+			projectPrismPart(obj, kCChairParts[i], false, p[i]);
+		if (p[4].visible)
+			drawProjectedPrism(p[4], kCChairParts[4], 0, tint(baseColor, -10), drawClip);
+		if (!p[0].visible)
+			return p[4].visible;
+		drawProjectedPrism(p[0], kCChairParts[0], 0, tint(baseColor, 0), drawClip);
+		if (p[3].vsurface[0]) {
+			drawProjectedPrism(p[3], kCChairParts[3], 1, tint(baseColor, 0), drawClip);
+			if (p[1].vsurface[0]) {
+				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
+				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
+			} else {
+				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
+				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
+			}
+		} else {
+			if (p[1].vsurface[0]) {
+				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
+				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
+			} else {
+				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
+				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
+			}
+			drawProjectedPrism(p[3], kCChairParts[3], 1, tint(baseColor, 0), drawClip);
+		}
+		return true;
+	case kObjPlant: {
+		for (int i = 0; i < 4; i++)
+			projectPrismPart(obj, kPlantParts[i], false, p[i]);
+		// Pot must be visible for object to appear
+		if (!p[0].visible)
+			return false;
+		// Draw pot, stem and leaves (pot darker, foliage lighter)
+		drawProjectedPrism(p[0], kPlantParts[0], 0, tint(baseColor, -30), drawClip);
+		if (p[1].visible)
+			drawProjectedPrism(p[1], kPlantParts[1], 0, tint(baseColor, 10), drawClip);
+		if (p[2].vsurface[0])
+			drawProjectedPrism(p[2], kPlantParts[2], 0, tint(baseColor, 30), drawClip);
+		if (p[3].vsurface[0])
+			drawProjectedPrism(p[3], kPlantParts[3], 0, tint(baseColor, 30), drawClip);
+		return true;
+	}
+	case kObjCouch:
+	case kObjChair: {
+		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
+		for (int i = 0; i < 4; i++)
+			projectPrismPart(obj, parts[i], false, p[i]);
+		if (!p[0].visible)
+			return false;
+		if (p[2].vsurface[1] && p[3].vsurface[2]) {
+			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
+			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
+			if (p[0].vsurface[3]) {
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+			} else {
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+			}
+		} else if (p[3].vsurface[1]) {
+			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
+			if (p[0].vsurface[3]) {
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+			} else {
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+			}
+			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
+		} else {
+			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
+			if (p[0].vsurface[3]) {
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+			} else {
+				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
+				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
+			}
+			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
+		}
+		return true;
+	}
+	case kObjTV:
+		projectPrismPart(obj, kTVParts[0], false, p[0]);
+		projectPrismPart(obj, kTVParts[1], false, p[1]);
+		if (!p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kTVParts[0], 0, tint(baseColor, 0), drawClip);
+		if (p[1].vsurface[0])
+			drawProjectedPrism(p[1], kTVParts[1], 0, tint(baseColor, 35), drawClip);
+		return true;
+	case kObjDrawer:
+		projectPrismPart(obj, kDrawerParts[0], false, p[0]);
+		projectPrismPart(obj, kDrawerParts[1], false, p[1]);
+		if (!p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kDrawerParts[0], 0, tint(baseColor, 0), drawClip);
+		drawProjectedPrism(p[1], kDrawerParts[1], 1, tint(baseColor, 30), drawClip);
+		return true;
+case kObjFWall:
+		// Simple flat wall part (fallback handled by prism below)
+		if (!projectPrismPart(obj, kCWallParts[0], false, p[0]) || !p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kCWallParts[0], 0, tint(baseColor, -10), drawClip);
+		return true;
+case kObjCWall:
+		// Corner wall (CWALL) — mirror of DOS CWallparts
+		if (!projectPrismPart(obj, kCWallParts[0], false, p[0]) || !p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kCWallParts[0], 0, tint(baseColor, -5), drawClip);
+		return true;
+	case kObjScreen:
+		if (!projectPrismPart(obj, kScreenPart, false, p[0]) || !p[0].visible)
+			return false;
+		drawProjectedPrism(p[0], kScreenPart, 0, tint(baseColor, 0), drawClip);
+		return true;
+	case kObjTable:
+		projectPrismPart(obj, kTableParts[0], false, p[0]);
+		projectPrismPart(obj, kTableParts[1], false, p[1]);
+		if (!p[1].visible)
+			return false;
+		drawProjectedPrism(p[1], kTableParts[1], 0, tint(baseColor, -10), drawClip);
+		drawProjectedPrism(p[0], kTableParts[0], 0, tint(baseColor, 20), drawClip);
+		return true;
+	case kObjBed:
+	case kObjBBed: {
+		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
+		projectPrismPart(obj, parts[0], false, p[0]);
+		projectPrismPart(obj, parts[1], false, p[1]);
+		projectPrismPart(obj, parts[2], false, p[2]);
+		if (!p[1].visible)
+			return false;
+		if (p[0].vsurface[0]) {
+			drawProjectedPrism(p[0], parts[0], 1, tint(baseColor, 15), drawClip);
+			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, 5), drawClip);
+			drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -10), drawClip);
+		} else {
+			drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -10), drawClip);
+			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, 5), drawClip);
+			drawProjectedPrism(p[0], parts[0], 1, tint(baseColor, 15), drawClip);
+		}
+		return true;
+	}
+	case kObjDesk:
+		for (int i = 0; i < 10; i++)
+			projectPrismPart(obj, kDeskParts[i], false, p[i]);
+		if (!p[0].visible)
+			return false;
+		if (p[6].vsurface[1]) {
+			if (p[1].vsurface[3] || p[2].vsurface[3]) {
+				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
+				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
+			} else {
+				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
+				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
+			}
+			drawProjectedPrism(p[0], kDeskParts[0], 0, tint(baseColor, 15), drawClip);
+			drawProjectedPrism(p[7], kDeskParts[7], 0, tint(baseColor, 25), drawClip);
+			drawProjectedPrism(p[8], kDeskParts[8], 0, tint(baseColor, 25), drawClip);
+			if (p[9].vsurface[0])
+				drawProjectedPrism(p[9], kDeskParts[9], 0, tint(baseColor, 40), drawClip);
+			if (p[4].vsurface[0]) {
+				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
+			} else {
+				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
+			}
+		} else {
+			if (p[4].vsurface[0]) {
+				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
+			} else {
+				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
+				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
+			}
+			if (p[1].vsurface[3] || p[2].vsurface[3]) {
+				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
+				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
+			} else {
+				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
+				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
+			}
+			drawProjectedPrism(p[0], kDeskParts[0], 0, tint(baseColor, 15), drawClip);
+			drawProjectedPrism(p[7], kDeskParts[7], 0, tint(baseColor, 25), drawClip);
+			drawProjectedPrism(p[8], kDeskParts[8], 0, tint(baseColor, 25), drawClip);
+			if (p[9].vsurface[0])
+				drawProjectedPrism(p[9], kDeskParts[9], 0, tint(baseColor, 40), drawClip);
+		}
+		return true;
+	default:
+		return false;
+	}
+}
+
+void ColonyEngine::drawStaticObjectFallback(const Thing &obj, uint32 color, int depth, int sx) {
+	int scale = _rtable[depth];
+	int baseY = _height - (_centerY - scale);
+	int h = CLIP<int>(scale, 4, 96);
+	int w = CLIP<int>(h >> 1, 3, 64);
+	Common::Rect body(sx - w, baseY - h, sx + w, baseY);
+	const int bodyLeft = (int)body.left;
+	const int bodyTop = (int)body.top;
+	const int bodyRight = (int)body.right;
+	const int bodyBottom = (int)body.bottom;
+	const int clipLeft = MAX(bodyLeft, MAX((int)obj.clip.left, (int)_screenR.left));
+	const int clipTop = MAX(bodyTop, MAX((int)obj.clip.top, (int)_screenR.top));
+	const int clipRight = MIN(bodyRight, MIN((int)obj.clip.right, (int)_screenR.right));
+	const int clipBottom = MIN(bodyBottom, MIN((int)obj.clip.bottom, (int)_screenR.bottom));
+	if (clipLeft >= clipRight || clipTop >= clipBottom)
+		return;
+	Common::Rect clipped(clipLeft, clipTop, clipRight, clipBottom);
+	_gfx->drawRect(clipped, color);
+	_gfx->drawLine(clipped.left, clipped.bottom - 1, clipped.right - 1, clipped.bottom - 1, color);
+}
+
+void ColonyEngine::drawStaticObjects() {
+	struct DrawCmd {
+		int depth;
+		int index;
+		int screenX;
+	};
+
+	Common::Array<DrawCmd> drawList;
+	drawList.reserve(_objects.size());
+
+	for (uint i = 0; i < _objects.size(); i++) {
+		const Thing &obj = _objects[i];
+		if (!obj.alive || !obj.visible)
+			continue;
+		int sx, depth;
+		if (!projectWorld(obj.where.xloc, obj.where.yloc, sx, depth))
+			continue;
+		if (depth > 11000)
+			continue;
+		DrawCmd cmd;
+		cmd.depth = depth;
+		cmd.index = (int)i;
+		cmd.screenX = sx;
+		drawList.push_back(cmd);
+	}
+
+	Common::sort(drawList.begin(), drawList.end(), [](const DrawCmd &a, const DrawCmd &b) {
+		return a.depth > b.depth; // far to near
+	});
+
+	for (uint i = 0; i < drawList.size(); i++) {
+		const DrawCmd &d = drawList[i];
+		const Thing &obj = _objects[d.index];
+		const uint32 color = objectColor(obj.type);
+		if (!drawStaticObjectPrisms(obj, color))
+			drawStaticObjectFallback(obj, color, d.depth, d.screenX);
+	}
+}
+
+void ColonyEngine::setRobot(int l, int r, int num) {
+	if (num <= 0 || num > (int)_objects.size())
+		return;
+	if (l < _screenR.left)
+		l = _screenR.left;
+	if (r > _screenR.right)
+		r = _screenR.right;
+	if (l >= r)
+		return;
+	const int clipLeft = l + 1;
+	const int clipRight = r - 2;
+	if (clipLeft >= clipRight)
+		return;
+
+	Thing &obj = _objects[num - 1];
+	if (!obj.alive)
+		return;
+	obj.visible = 1;
+	obj.clip.left = clipLeft;
+	obj.clip.right = clipRight;
+	obj.clip.top = _clip.top;
+	obj.clip.bottom = _clip.bottom;
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
new file mode 100644
index 00000000000..90596722449
--- /dev/null
+++ b/engines/colony/ui.cpp
@@ -0,0 +1,607 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "colony/gfx.h"
+#include "common/system.h"
+#include "common/util.h"
+#include "common/debug.h"
+#include "graphics/palette.h"
+#include <math.h>
+
+namespace Colony {
+
+void ColonyEngine::updateViewportLayout() {
+	auto makeSafeRect = [](int left, int top, int right, int bottom) {
+		if (right < left)
+			right = left;
+		if (bottom < top)
+			bottom = top;
+		return Common::Rect(left, top, right, bottom);
+	};
+
+	int dashWidth = 0;
+	if (_showDashBoard) {
+		dashWidth = CLIP<int>(_width / 6, 72, 140);
+		if (_width - dashWidth < 160)
+			dashWidth = 0;
+	}
+
+	_screenR = makeSafeRect(dashWidth, 0, _width, _height);
+	_clip = _screenR;
+	_centerX = (_screenR.left + _screenR.right) >> 1;
+	_centerY = (_screenR.top + _screenR.bottom) >> 1;
+
+	_dashBoardRect = makeSafeRect(0, 0, dashWidth, _height);
+	if (dashWidth == 0) {
+		_compassRect = Common::Rect(0, 0, 0, 0);
+		_headsUpRect = Common::Rect(0, 0, 0, 0);
+		_powerRect = Common::Rect(0, 0, 0, 0);
+		return;
+	}
+
+	const int pad = 2;
+	const int unit = MAX(8, (dashWidth - (pad * 2)) / 4);
+	const int blockLeft = pad;
+	const int blockRight = MIN(dashWidth - pad, blockLeft + unit * 4);
+
+	const int compassBottom = _height - MAX(2, unit / 4);
+	const int compassTop = MAX(pad, compassBottom - unit * 4);
+	_compassRect = makeSafeRect(blockLeft, compassTop, blockRight, compassBottom);
+
+	const int headsUpBottom = _compassRect.top - MAX(2, unit / 4) - 4;
+	const int headsUpTop = headsUpBottom - unit * 4;
+	_headsUpRect = makeSafeRect(blockLeft, MAX(pad, headsUpTop), blockRight, MAX(pad, headsUpBottom));
+
+	_powerRect = makeSafeRect(blockLeft, pad, blockRight, _headsUpRect.top - 4);
+}
+
+void ColonyEngine::drawDashboardStep1() {
+	if (_dashBoardRect.width() <= 0 || _dashBoardRect.height() <= 0)
+		return;
+
+	const int kFWALLType = 48;
+	const uint32 panelBg = 24;
+	const uint32 frame = 190;
+	const uint32 accent = 220;
+	const uint32 mark = 255;
+	const uint32 miniMapObj = 235;
+	const uint32 miniMapActor = 255;
+
+	_gfx->fillRect(_dashBoardRect, panelBg);
+	_gfx->drawRect(_dashBoardRect, frame);
+	if (_screenR.left > 0)
+		_gfx->drawLine(_screenR.left - 1, _screenR.top, _screenR.left - 1, _screenR.bottom - 1, mark);
+
+	if (_compassRect.width() > 4 && _compassRect.height() > 4) {
+		const int cx = (_compassRect.left + _compassRect.right) >> 1;
+		const int cy = (_compassRect.top + _compassRect.bottom) >> 1;
+		const int rx = MAX(2, (_compassRect.width() - 6) >> 1);
+		const int ry = MAX(2, (_compassRect.height() - 6) >> 1);
+		const int irx = MAX(1, rx - 2);
+		const int iry = MAX(1, ry - 2);
+		auto drawEllipse = [&](int erx, int ery, uint32 color) {
+			const int segments = 48;
+			int px = cx + erx;
+			int py = cy;
+			for (int i = 1; i <= segments; i++) {
+				const double t = (6.28318530717958647692 * (double)i) / (double)segments;
+				const int x = cx + (int)((double)erx * cos(t));
+				const int y = cy + (int)((double)ery * sin(t));
+				_gfx->drawLine(px, py, x, y, color);
+				px = x;
+				py = y;
+			}
+		};
+		drawEllipse(rx, ry, frame);
+		drawEllipse(irx, iry, accent);
+
+		const int ex = cx + ((_cost[_me.look] * rx) >> 8);
+		const int ey = cy - ((_sint[_me.look] * ry) >> 8);
+		_gfx->drawLine(cx, cy, ex, ey, mark);
+		_gfx->drawLine(cx - 2, cy, cx + 2, cy, accent);
+		_gfx->drawLine(cx, cy - 2, cx, cy + 2, accent);
+	}
+
+	if (_headsUpRect.width() > 4 && _headsUpRect.height() > 4) {
+		_gfx->drawRect(_headsUpRect, frame);
+		const Common::Rect miniMapClip(_headsUpRect.left + 1, _headsUpRect.top + 1, _headsUpRect.right - 1, _headsUpRect.bottom - 1);
+		auto drawMiniMapLine = [&](int x1, int y1, int x2, int y2, uint32 color) {
+			if (clipLineToRect(x1, y1, x2, y2, miniMapClip))
+				_gfx->drawLine(x1, y1, x2, y2, color);
+		};
+
+		const int lExtBase = _dashBoardRect.width() >> 1;
+		int lExt = lExtBase + (lExtBase >> 1);
+		if (lExt & 1)
+			lExt--;
+		const int sExt = lExt >> 1;
+		const int xloc = (lExt * ((_me.xindex << 8) - _me.xloc)) >> 8;
+		const int yloc = (lExt * ((_me.yindex << 8) - _me.yloc)) >> 8;
+		const int ccenterx = (_headsUpRect.left + _headsUpRect.right) >> 1;
+		const int ccentery = (_headsUpRect.top + _headsUpRect.bottom) >> 1;
+		const int tsin = _sint[_me.look];
+		const int tcos = _cost[_me.look];
+
+		int xcorner[6];
+		int ycorner[6];
+		xcorner[0] = ccenterx + (((long)xloc * tsin - (long)yloc * tcos) >> 8);
+		ycorner[0] = ccentery - (((long)yloc * tsin + (long)xloc * tcos) >> 8);
+		xcorner[1] = ccenterx + (((long)(xloc + lExt) * tsin - (long)yloc * tcos) >> 8);
+		ycorner[1] = ccentery - (((long)yloc * tsin + (long)(xloc + lExt) * tcos) >> 8);
+		xcorner[2] = ccenterx + (((long)(xloc + lExt) * tsin - (long)(yloc + lExt) * tcos) >> 8);
+		ycorner[2] = ccentery - (((long)(yloc + lExt) * tsin + (long)(xloc + lExt) * tcos) >> 8);
+		xcorner[3] = ccenterx + (((long)xloc * tsin - (long)(yloc + lExt) * tcos) >> 8);
+		ycorner[3] = ccentery - (((long)(yloc + lExt) * tsin + (long)xloc * tcos) >> 8);
+		xcorner[4] = ccenterx + (((long)(xloc + sExt) * tsin - (long)(yloc + sExt) * tcos) >> 8);
+		ycorner[4] = ccentery - (((long)(yloc + sExt) * tsin + (long)(xloc + sExt) * tcos) >> 8);
+		xcorner[5] = ccenterx + (((long)(xloc + sExt) * tsin - (long)yloc * tcos) >> 8);
+		ycorner[5] = ccentery - (((long)yloc * tsin + (long)(xloc + sExt) * tcos) >> 8);
+
+		const int dx = xcorner[1] - xcorner[0];
+		const int dy = ycorner[0] - ycorner[1];
+		drawMiniMapLine(xcorner[0] - dx, ycorner[0] + dy, xcorner[1] + dx, ycorner[1] - dy, accent);
+		drawMiniMapLine(xcorner[1] + dy, ycorner[1] + dx, xcorner[2] - dy, ycorner[2] - dx, accent);
+		drawMiniMapLine(xcorner[2] + dx, ycorner[2] - dy, xcorner[3] - dx, ycorner[3] + dy, accent);
+		drawMiniMapLine(xcorner[3] - dy, ycorner[3] - dx, xcorner[0] + dy, ycorner[0] + dx, accent);
+
+		auto drawMarker = [&](int x, int y, int halfSize, uint32 color) {
+			const int l = MAX<int>(_headsUpRect.left + 1, x - halfSize);
+			const int t = MAX<int>(_headsUpRect.top + 1, y - halfSize);
+			const int r = MIN<int>(_headsUpRect.right - 1, x + halfSize + 1);
+			const int b = MIN<int>(_headsUpRect.bottom - 1, y + halfSize + 1);
+			if (l >= r || t >= b)
+				return;
+			_gfx->drawRect(Common::Rect(l, t, r, b), color);
+		};
+
+		auto hasRobotAt = [&](int x, int y) -> bool {
+			if (x < 0 || x >= 32 || y < 0 || y >= 32)
+				return false;
+			return _robotArray[x][y] != 0;
+		};
+		auto hasFoodAt = [&](int x, int y) -> bool {
+			if (x < 0 || x >= 32 || y < 0 || y >= 32)
+				return false;
+			const uint8 num = _foodArray[x][y];
+			if (num == 0)
+				return false;
+			if (num <= _objects.size())
+				return _objects[num - 1].type < kFWALLType;
+			return true;
+		};
+
+		if (hasFoodAt(_me.xindex, _me.yindex))
+			drawMarker(xcorner[4], ycorner[4], 1, miniMapObj);
+
+		if (_me.yindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x01)) {
+			if (hasFoodAt(_me.xindex, _me.yindex - 1))
+				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 1, miniMapObj);
+			if (hasRobotAt(_me.xindex, _me.yindex - 1))
+				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 2, miniMapActor);
+		}
+		if (_me.xindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x02)) {
+			if (hasFoodAt(_me.xindex - 1, _me.yindex))
+				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 1, miniMapObj);
+			if (hasRobotAt(_me.xindex - 1, _me.yindex))
+				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 2, miniMapActor);
+		}
+		if (_me.yindex < 30 && !(_wall[_me.xindex][_me.yindex + 1] & 0x01)) {
+			if (hasFoodAt(_me.xindex, _me.yindex + 1))
+				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 1, miniMapObj);
+			if (hasRobotAt(_me.xindex, _me.yindex + 1))
+				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 2, miniMapActor);
+		}
+		if (_me.xindex < 30 && !(_wall[_me.xindex + 1][_me.yindex] & 0x02)) {
+			if (hasFoodAt(_me.xindex + 1, _me.yindex))
+				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 1, miniMapObj);
+			if (hasRobotAt(_me.xindex + 1, _me.yindex))
+				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 2, miniMapActor);
+		}
+
+		drawMarker(ccenterx, ccentery, 2, mark);
+		drawMarker(ccenterx, ccentery, 1, accent);
+	}
+
+	if (_powerRect.width() > 4 && _powerRect.height() > 4) {
+		_gfx->drawRect(_powerRect, frame);
+		const int barY = _powerRect.bottom - MAX(3, _powerRect.width() / 8);
+		_gfx->drawLine(_powerRect.left + 1, barY, _powerRect.right - 2, barY, accent);
+	}
+}
+
+void ColonyEngine::drawCrosshair() {
+	if (!_crosshair || _screenR.width() <= 0 || _screenR.height() <= 0)
+		return;
+
+	const uint32 color = (_weapons > 0) ? 255 : 220;
+	const int cx = _centerX;
+	const int cy = _centerY;
+	const int qx = MAX(2, _screenR.width() / 32);
+	const int qy = MAX(2, _screenR.height() / 32);
+	const int fx = (qx * 3) >> 1;
+	const int fy = (qy * 3) >> 1;
+	auto drawCrossLine = [&](int x1, int y1, int x2, int y2) {
+		if (clipLineToRect(x1, y1, x2, y2, _screenR))
+			_gfx->drawLine(x1, y1, x2, y2, color);
+	};
+
+	if (_weapons > 0) {
+		const int yTop = _insight ? (cy - qy) : (cy - fy);
+		const int yBottom = _insight ? (cy + qy) : (cy + fy);
+
+		drawCrossLine(cx - qx, yTop, cx - fx, yTop);
+		drawCrossLine(cx - fx, yTop, cx - fx, yBottom);
+		drawCrossLine(cx - fx, yBottom, cx - qx, yBottom);
+		drawCrossLine(cx + qx, yTop, cx + fx, yTop);
+		drawCrossLine(cx + fx, yTop, cx + fx, yBottom);
+		drawCrossLine(cx + fx, yBottom, cx + qx, yBottom);
+		_insight = false;
+	} else {
+		drawCrossLine(cx - qx, cy, cx + qx, cy);
+		drawCrossLine(cx, cy - qy, cx, cy + qy);
+	}
+}
+
+int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
+	if (x < 0 || x >= 32 || y < 0 || y >= 32)
+		return -1;
+	const int rnum = _robotArray[x][y];
+	if (rnum <= 0)
+		return 0;
+	if (pobject == &_me && rnum <= (int)_objects.size()) {
+		Thing &obj = _objects[rnum - 1];
+		if (obj.type <= BASEOBJECT)
+			obj.where.look = obj.where.ang = _me.ang + 128;
+	}
+	return rnum;
+}
+
+int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
+	const int xind2 = xnew >> 8;
+	const int yind2 = ynew >> 8;
+	_change = true;
+
+	auto occupied = [&]() -> int {
+		return occupiedObjectAt(xind2, yind2, pobject);
+	};
+	auto moveTo = [&]() -> int {
+		const int rnum = occupied();
+		if (rnum)
+			return rnum;
+		pobject->yindex = yind2;
+		pobject->xindex = xind2;
+		pobject->dx = xnew - pobject->xloc;
+		pobject->dy = ynew - pobject->yloc;
+		pobject->xloc = xnew;
+		pobject->yloc = ynew;
+		return 0;
+	};
+
+	if (xind2 == pobject->xindex) {
+		if (yind2 == pobject->yindex) {
+			pobject->dx = xnew - pobject->xloc;
+			pobject->dy = ynew - pobject->yloc;
+			pobject->xloc = xnew;
+			pobject->yloc = ynew;
+			return 0;
+		}
+
+		if (yind2 > pobject->yindex) {
+			if (!(_wall[pobject->xindex][yind2] & 1))
+				return moveTo();
+			if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirNorth, pobject))
+				return moveTo();
+			debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
+			return -1;
+		}
+
+		if (!(_wall[pobject->xindex][pobject->yindex] & 1))
+			return moveTo();
+		if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirSouth, pobject))
+			return moveTo();
+		debug("Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
+		return -1;
+	}
+
+	if (yind2 == pobject->yindex) {
+		if (xind2 > pobject->xindex) {
+			if (!(_wall[xind2][pobject->yindex] & 2))
+				return moveTo();
+			if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirEast, pobject))
+				return moveTo();
+			debug("Collision East at x=%d y=%d", xind2, pobject->yindex);
+			return -1;
+		}
+
+		if (!(_wall[pobject->xindex][pobject->yindex] & 2))
+			return moveTo();
+		if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirWest, pobject))
+			return moveTo();
+		debug("Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
+		return -1;
+	}
+
+	// Diagonal
+	if (xind2 > pobject->xindex) {
+		if (yind2 > pobject->yindex) {
+			if ((_wall[pobject->xindex][yind2] & 1) || (_wall[xind2][pobject->yindex] & 2) || (_wall[xind2][yind2] & 3)) {
+				debug("Collision Diagonal SE");
+				return -1;
+			}
+		} else {
+			if ((_wall[pobject->xindex][pobject->yindex] & 1) || (_wall[xind2][yind2] & 2) || (_wall[xind2][pobject->yindex] & 3)) {
+				debug("Collision Diagonal NE");
+				return -1;
+			}
+		}
+	} else {
+		if (yind2 > pobject->yindex) {
+			if ((_wall[xind2][yind2] & 1) || (_wall[pobject->xindex][pobject->yindex] & 2) || (_wall[pobject->xindex][yind2] & 3)) {
+				debug("Collision Diagonal SW");
+				return -1;
+			}
+		} else {
+			if ((_wall[xind2][pobject->yindex] & 1) || (_wall[pobject->xindex][yind2] & 2) || (_wall[pobject->xindex][pobject->yindex] & 3)) {
+				debug("Collision Diagonal NW");
+				return -1;
+			}
+		}
+	}
+
+	return moveTo();
+}
+
+bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
+	if (x < 0 || x >= 31 || y < 0 || y >= 31 || direction < 0 || direction > 3)
+		return false;
+
+	const uint8 wallType = _mapData[x][y][direction][0];
+	if (wallType != kWallFeatureDoor && wallType != kWallFeatureAirlock)
+		return false;
+
+	_mapData[x][y][direction][1] = (uint8)state;
+
+	int nx = x;
+	int ny = y;
+	int opposite = -1;
+	switch (direction) {
+	case kDirNorth:
+		ny += 1;
+		opposite = kDirSouth;
+		break;
+	case kDirEast:
+		nx += 1;
+		opposite = kDirWest;
+		break;
+	case kDirWest:
+		nx -= 1;
+		opposite = kDirEast;
+		break;
+	case kDirSouth:
+		ny -= 1;
+		opposite = kDirNorth;
+		break;
+	default:
+		return true;
+	}
+
+	if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31 && opposite >= 0) {
+		if (_mapData[nx][ny][opposite][0] == wallType)
+			_mapData[nx][ny][opposite][1] = (uint8)state;
+	}
+
+	return true;
+}
+
+int ColonyEngine::openAdjacentDoors(int x, int y) {
+	int opened = 0;
+	for (int dir = 0; dir < 4; dir++) {
+		const uint8 *map = mapFeatureAt(x, y, dir);
+		if (!map)
+			continue;
+		if (map[0] == kWallFeatureDoor && map[1] != 0) {
+			if (setDoorState(x, y, dir, 0))
+				opened++;
+		}
+	}
+	return opened;
+}
+
+bool ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject) {
+	const uint8 *map = mapFeatureAt(fromX, fromY, direction);
+	if (!map || map[0] == kWallFeatureNone)
+		return false;
+
+	switch (map[0]) {
+	case kWallFeatureDoor:
+		if (map[1] == 0)
+			return true;
+		if (pobject != &_me)
+			return false;
+		if (_hasKeycard || _unlocked) {
+			setDoorState(fromX, fromY, direction, 0);
+			return true;
+		}
+		return false;
+	case kWallFeatureAirlock:
+		if (map[1] == 0)
+			return true;
+		if (pobject == &_me && _unlocked) {
+			setDoorState(fromX, fromY, direction, 0);
+			return true;
+		}
+		return false;
+	default:
+		return false;
+	}
+}
+
+void ColonyEngine::interactWithObject(int objNum) {
+	if (objNum <= 0 || objNum > (int)_objects.size())
+		return;
+
+	const Thing &obj = _objects[objNum - 1];
+	if (!obj.alive)
+		return;
+
+	const int x = CLIP<int>(obj.where.xindex, 0, 30);
+	const int y = CLIP<int>(obj.where.yindex, 0, 30);
+	const int action0 = _mapData[x][y][4][3];
+	const int action1 = _mapData[x][y][4][4];
+
+	switch (obj.type) {
+	case kObjDesk:
+		if (!_hasKeycard) {
+			_hasKeycard = true;
+			debug("CCommand: DESK granted keycard");
+		} else {
+			debug("CCommand: DESK action (%d, %d)", action0, action1);
+		}
+		break;
+	case kObjConsole:
+		switch (action0) {
+		case 1:
+			debug("CCommand: CONSOLE reactor (%d)", action1);
+			break;
+		case 2:
+		{
+			const int opened = openAdjacentDoors(_me.xindex, _me.yindex);
+			debug("CCommand: CONSOLE controls opened %d nearby doors", opened);
+			break;
+		}
+		case 3:
+			_unlocked = true;
+			debug("CCommand: CONSOLE security unlocked");
+			break;
+		default:
+			debug("CCommand: CONSOLE action=%d", action0);
+			break;
+		}
+		break;
+	case kObjProjector:
+		switch (action0) {
+		case 1:
+			debug("CCommand: PROJECTOR creatures");
+			break;
+		case 2:
+			debug("CCommand: PROJECTOR teleporters");
+			break;
+		default:
+			debug("CCommand: PROJECTOR action=%d", action0);
+			break;
+		}
+		break;
+	case kObjPowerSuit:
+		_weapons = MAX(_weapons, 1);
+		_crosshair = true;
+		debug("CCommand: POWERSUIT");
+		break;
+	case kObjTeleport:
+	{
+		const int targetLevelRaw = _mapData[x][y][4][2];
+		const int targetLevel = (targetLevelRaw == 0) ? _level : targetLevelRaw;
+		const int targetX = _mapData[x][y][4][3];
+		const int targetY = _mapData[x][y][4][4];
+		if (targetLevel >= 100 || targetX <= 0 || targetX >= 31 || targetY <= 0 || targetY >= 31) {
+			debug("CCommand: TELEPORT ignored invalid target L%d (%d,%d)", targetLevelRaw, targetX, targetY);
+			break;
+		}
+		if (targetLevel != _level)
+			loadMap(targetLevel);
+		const int oldX = _me.xindex;
+		const int oldY = _me.yindex;
+		_me.xindex = targetX;
+		_me.yindex = targetY;
+		_me.xloc = (targetX << 8) + 128;
+		_me.yloc = (targetY << 8) + 128;
+		_me.ang = _me.look;
+		if (oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32)
+			_robotArray[oldX][oldY] = 0;
+		if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
+			_robotArray[_me.xindex][_me.yindex] = MENUM;
+		_change = true;
+		debug("CCommand: TELEPORT to L%d (%d,%d)", targetLevel, targetX, targetY);
+		break;
+	}
+	case kObjDrawer:
+		if (!_hasKeycard) {
+			_hasKeycard = true;
+			debug("CCommand: DRAWER vanity=%d (picked keycard)", action0);
+		} else {
+			debug("CCommand: DRAWER vanity=%d", action0);
+		}
+		break;
+	case kObjScreen:
+		debug("CCommand: SCREEN");
+		break;
+	case kObjToilet:
+	case kObjPToilet:
+		debug("CCommand: TOILET");
+		break;
+	case kObjTub:
+		debug("CCommand: TUB");
+		break;
+	case kObjSink:
+		debug("CCommand: SINK");
+		break;
+	case kObjCryo:
+		debug("CCommand: CRYO text=%d", action0);
+		break;
+	case kObjTV:
+		debug("CCommand: TV level=%d", _level);
+		break;
+	case kObjForkLift:
+	case kObjReactor:
+	case kObjBox1:
+	case kObjBox2:
+		debug("CCommand: object type %d requires forklift/reactor flow", obj.type);
+		break;
+	case kObjPlant:
+	case kObjCChair:
+	case kObjBed:
+	case kObjTable:
+	case kObjCouch:
+	case kObjChair:
+	case kObjBBed:
+	case kObjFWall:
+	case kObjCWall:
+		// Matches DOS CCommand where these objects are non-interactive blockers.
+		break;
+	default:
+		debug("CCommand: object type %d", obj.type);
+		break;
+	}
+}
+
+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 robot = checkwall(xnew, ynew, &_me);
+	if (robot > 0 && allowInteraction)
+		interactWithObject(robot);
+
+	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
+		_robotArray[_me.xindex][_me.yindex] = MENUM;
+}
+
+} // End of namespace Colony


Commit: 7830cab35990c756d8454f8743ad34d00e9bdc1e
    https://github.com/scummvm/scummvm/commit/7830cab35990c756d8454f8743ad34d00e9bdc1e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:06+02:00

Commit Message:
COLONY: initial text

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c533b2a292b..f2682826b00 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -31,6 +31,9 @@
 #include "engines/util.h"
 #include "graphics/palette.h"
 #include "graphics/paletteman.h"
+#include "graphics/fontman.h"
+#include "graphics/font.h"
+#include "graphics/fonts/dosfont.h"
 #include <math.h>
 
 namespace Colony {
@@ -221,6 +224,8 @@ Common::Error ColonyEngine::run() {
 
 	_gfx = new Gfx(_system, _width, _height);
 	
+	scrollInfo();
+
 	loadMap(1); // Try to load the first map
 	_system->lockMouse(true);
 	_system->warpMouse(_centerX, _centerY);
@@ -292,4 +297,66 @@ Common::Error ColonyEngine::run() {
 	return Common::kNoError;
 }
 
+void ColonyEngine::scrollInfo() {
+	const char *story[] = {
+		"Mankind has left the",
+		"cradle of earth and",
+		"is beginning to eye",
+		"the galaxy. He has",
+		"begun to colonize",
+		"distant planets but has",
+		"yet to meet any alien",
+		"life forms.",
+		"****",
+		"Until now...",
+		"****",
+		"Press any key to begin",
+		"the Adventure..."
+	};
+	const int storyLength = ARRAYSIZE(story);
+
+	_gfx->clear(_gfx->black());
+	Graphics::DosFont dosFont;
+	const Graphics::Font *font = &dosFont;
+
+	int centerY = _height / 2;
+	centerY -= (storyLength * 10) / 2;
+	centerY += 5;
+
+	for (int i = 0; i < storyLength; i++) {
+		_gfx->drawString(font, story[i], 0, centerY + 10 * i, _gfx->white(), Graphics::kTextAlignCenter);
+	}
+	_gfx->copyToScreen();
+
+	// Wait for keypress
+	Common::Event event;
+	bool pressed = false;
+	while (!pressed && !shouldQuit()) {
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN) {
+				pressed = true;
+				break;
+			}
+		}
+		_system->delayMillis(10);
+	}
+
+	if (shouldQuit()) return;
+
+	// Scroll up
+	for (int i = 0; i < _height; i += 8) {
+		_gfx->scroll(0, -8, _gfx->black());
+		_gfx->copyToScreen();
+		_system->delayMillis(10);
+		
+		// Allow skipping scroll
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN) {
+				return;
+			}
+		}
+		if (shouldQuit()) return;
+	}
+}
+
 } // End of namespace Colony
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 93f82a5d773..1987e678836 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -131,6 +131,7 @@ public:
 	void rot_init(int x, int y);
 	int checkwall(int xnew, int ynew, Locate *pobject);
 	void cCommand(int xnew, int ynew, bool allowInteraction);
+	void scrollInfo();
 
 private:
 	const ADGameDescription *_gameDescription;
diff --git a/engines/colony/gfx.cpp b/engines/colony/gfx.cpp
index b4cc9a29b2b..c1f56e47b54 100644
--- a/engines/colony/gfx.cpp
+++ b/engines/colony/gfx.cpp
@@ -20,7 +20,9 @@
  */
 
 #include "common/system.h"
+#include "common/util.h"
 #include "colony/gfx.h"
+#include "graphics/font.h"
 
 namespace Colony {
 
@@ -48,6 +50,25 @@ void Gfx::fillRect(const Common::Rect &rect, uint32 color) {
 	_surface.fillRect(rect, color);
 }
 
+void Gfx::drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) {
+	font->drawString(&_surface, str, x, y, (align == Graphics::kTextAlignCenter && x == 0) ? _width : (_width - x), color, align);
+}
+
+void Gfx::scroll(int dx, int dy, uint32 background) {
+	if (abs(dx) >= _width || abs(dy) >= _height) {
+		clear(background);
+		return;
+	}
+
+	Graphics::ManagedSurface tmp;
+	tmp.create(_width, _height, _surface.format);
+	tmp.blitFrom(_surface);
+
+	clear(background);
+	_surface.blitFrom(tmp, Common::Rect(0, 0, _width, _height), Common::Rect(dx, dy, dx + _width, dy + _height));
+	tmp.free();
+}
+
 void Gfx::copyToScreen() {
 	_system->copyRectToScreen(_surface.getPixels(), _surface.pitch, 0, 0, _surface.w, _surface.h);
 	_system->updateScreen();
diff --git a/engines/colony/gfx.h b/engines/colony/gfx.h
index f7bfa3e66d3..81f7873b64b 100644
--- a/engines/colony/gfx.h
+++ b/engines/colony/gfx.h
@@ -25,6 +25,7 @@
 #include "common/scummsys.h"
 #include "common/rect.h"
 #include "graphics/managed_surface.h"
+#include "graphics/font.h"
 
 namespace Colony {
 
@@ -38,6 +39,9 @@ public:
 	void drawRect(const Common::Rect &rect, uint32 color);
 	void fillRect(const Common::Rect &rect, uint32 color);
 	
+	void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
+	void scroll(int dx, int dy, uint32 background);
+	
 	void copyToScreen();
 
 	uint32 white() { return 255; }


Commit: b60004bc460edbf71f5ffebe208bbac3c88e438c
    https://github.com/scummvm/scummvm/commit/b60004bc460edbf71f5ffebe208bbac3c88e438c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:06+02:00

Commit Message:
COLONY: add dither fill for Mac desktop pattern and improve HUD layout

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index f2682826b00..23a985ba8e1 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -213,9 +213,20 @@ Common::Error ColonyEngine::run() {
 	const Graphics::PixelFormat format = _system->getScreenFormat();
 	debug("Screen format: %d bytesPerPixel. Actual size: %dx%d", format.bytesPerPixel, _width, _height);
 
-	// Setup a grayscale palette
+	// Setup a palette with standard 16 colors followed by grayscale
 	byte pal[256 * 3];
-	for (int i = 0; i < 256; i++) {
+	static const byte ega_colors[16][3] = {
+		{0, 0, 0}, {0, 0, 170}, {0, 170, 0}, {0, 170, 170},
+		{170, 0, 0}, {170, 0, 170}, {170, 85, 0}, {170, 170, 170},
+		{85, 85, 85}, {85, 85, 255}, {85, 255, 85}, {85, 255, 255},
+		{255, 85, 85}, {255, 85, 255}, {255, 255, 85}, {255, 255, 255}
+	};
+	for (int i = 0; i < 16; i++) {
+		pal[i * 3 + 0] = ega_colors[i][0];
+		pal[i * 3 + 1] = ega_colors[i][1];
+		pal[i * 3 + 2] = ega_colors[i][2];
+	}
+	for (int i = 16; i < 256; i++) {
 		pal[i * 3 + 0] = i;
 		pal[i * 3 + 1] = i;
 		pal[i * 3 + 2] = i;
@@ -324,7 +335,7 @@ void ColonyEngine::scrollInfo() {
 	centerY += 5;
 
 	for (int i = 0; i < storyLength; i++) {
-		_gfx->drawString(font, story[i], 0, centerY + 10 * i, _gfx->white(), Graphics::kTextAlignCenter);
+		_gfx->drawString(font, story[i], 0, centerY + 10 * i, 9, Graphics::kTextAlignCenter);
 	}
 	_gfx->copyToScreen();
 
diff --git a/engines/colony/gfx.cpp b/engines/colony/gfx.cpp
index c1f56e47b54..5071238cbab 100644
--- a/engines/colony/gfx.cpp
+++ b/engines/colony/gfx.cpp
@@ -69,6 +69,25 @@ void Gfx::scroll(int dx, int dy, uint32 background) {
 	tmp.free();
 }
 
+void Gfx::drawEllipse(int x, int y, int rx, int ry, uint32 color) {
+	_surface.drawEllipse(x - rx, y - ry, x + rx, y + ry, color, false);
+}
+
+void Gfx::fillEllipse(int x, int y, int rx, int ry, uint32 color) {
+	_surface.drawEllipse(x - rx, y - ry, x + rx, y + ry, color, true);
+}
+
+void Gfx::fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) {
+	for (int y = rect.top; y < rect.bottom; y++) {
+		for (int x = rect.left; x < rect.right; x++) {
+			if ((x + y) % 2 == 0)
+				_surface.setPixel(x, y, color1);
+			else
+				_surface.setPixel(x, y, color2);
+		}
+	}
+}
+
 void Gfx::copyToScreen() {
 	_system->copyRectToScreen(_surface.getPixels(), _surface.pitch, 0, 0, _surface.w, _surface.h);
 	_system->updateScreen();
diff --git a/engines/colony/gfx.h b/engines/colony/gfx.h
index 81f7873b64b..38f10433005 100644
--- a/engines/colony/gfx.h
+++ b/engines/colony/gfx.h
@@ -41,7 +41,11 @@ public:
 	
 	void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
 	void scroll(int dx, int dy, uint32 background);
-	
+
+	void drawEllipse(int x, int y, int rx, int ry, uint32 color);
+	void fillEllipse(int x, int y, int rx, int ry, uint32 color);
+	void fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2);
+
 	void copyToScreen();
 
 	uint32 white() { return 255; }
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 90596722449..e36eced1955 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -79,50 +79,48 @@ void ColonyEngine::drawDashboardStep1() {
 		return;
 
 	const int kFWALLType = 48;
-	const uint32 panelBg = 24;
-	const uint32 frame = 190;
-	const uint32 accent = 220;
-	const uint32 mark = 255;
-	const uint32 miniMapObj = 235;
-	const uint32 miniMapActor = 255;
-
-	_gfx->fillRect(_dashBoardRect, panelBg);
-	_gfx->drawRect(_dashBoardRect, frame);
-	if (_screenR.left > 0)
-		_gfx->drawLine(_screenR.left - 1, _screenR.top, _screenR.left - 1, _screenR.bottom - 1, mark);
+
+	_gfx->fillRect(_dashBoardRect, 0); // Black background underlying
+	_gfx->fillDitherRect(_dashBoardRect, 0, 15); // Dithered 50% gray background (Black/White)
+	_gfx->drawRect(_dashBoardRect, 1); // Blue outer frame
+
+	const int shiftX = MAX(1, _dashBoardRect.width() / 8);
+	const int shiftY = MAX(1, _dashBoardRect.width() / 8);
+	const Common::Rect r = _dashBoardRect;
+
+	// Bevel lines
+	_gfx->drawLine(r.left, r.top, r.left + shiftX, r.top + shiftY, 0);
+	_gfx->drawLine(r.right - 1, r.top, r.right - 1 - shiftX, r.top + shiftY, 0);
+	_gfx->drawLine(r.left, r.bottom - 1, r.left + shiftX, r.bottom - 1 - shiftY, 0);
+	_gfx->drawLine(r.right - 1, r.bottom - 1, r.right - 1 - shiftX, r.bottom - 1 - shiftY, 0);
+
+	const Common::Rect tr(r.left + shiftX, r.top + shiftY, r.right - shiftX, r.bottom - shiftY);
+	_gfx->fillDitherRect(tr, 0, 15);
+	_gfx->drawRect(tr, 0);
 
 	if (_compassRect.width() > 4 && _compassRect.height() > 4) {
 		const int cx = (_compassRect.left + _compassRect.right) >> 1;
 		const int cy = (_compassRect.top + _compassRect.bottom) >> 1;
 		const int rx = MAX(2, (_compassRect.width() - 6) >> 1);
 		const int ry = MAX(2, (_compassRect.height() - 6) >> 1);
-		const int irx = MAX(1, rx - 2);
-		const int iry = MAX(1, ry - 2);
-		auto drawEllipse = [&](int erx, int ery, uint32 color) {
-			const int segments = 48;
-			int px = cx + erx;
-			int py = cy;
-			for (int i = 1; i <= segments; i++) {
-				const double t = (6.28318530717958647692 * (double)i) / (double)segments;
-				const int x = cx + (int)((double)erx * cos(t));
-				const int y = cy + (int)((double)ery * sin(t));
-				_gfx->drawLine(px, py, x, y, color);
-				px = x;
-				py = y;
-			}
-		};
-		drawEllipse(rx, ry, frame);
-		drawEllipse(irx, iry, accent);
+
+		_gfx->fillEllipse(cx, cy, rx, ry, 15); // White background
+		_gfx->drawEllipse(cx, cy, rx, ry, 1);  // Blue frame
 
 		const int ex = cx + ((_cost[_me.look] * rx) >> 8);
 		const int ey = cy - ((_sint[_me.look] * ry) >> 8);
-		_gfx->drawLine(cx, cy, ex, ey, mark);
-		_gfx->drawLine(cx - 2, cy, cx + 2, cy, accent);
-		_gfx->drawLine(cx, cy - 2, cx, cy + 2, accent);
+		_gfx->drawLine(cx, cy, ex, ey, 0); // Black mark
+		_gfx->drawLine(cx - 2, cy, cx + 2, cy, 0);
+		_gfx->drawLine(cx, cy - 2, cx, cy + 2, 0);
 	}
 
+	if (_screenR.left > 0)
+		_gfx->drawLine(_screenR.left - 1, _screenR.top, _screenR.left - 1, _screenR.bottom - 1, 15);
+
 	if (_headsUpRect.width() > 4 && _headsUpRect.height() > 4) {
-		_gfx->drawRect(_headsUpRect, frame);
+		_gfx->fillRect(_headsUpRect, 15); // White background
+		_gfx->drawRect(_headsUpRect, 1); // Blue frame
+
 		const Common::Rect miniMapClip(_headsUpRect.left + 1, _headsUpRect.top + 1, _headsUpRect.right - 1, _headsUpRect.bottom - 1);
 		auto drawMiniMapLine = [&](int x1, int y1, int x2, int y2, uint32 color) {
 			if (clipLineToRect(x1, y1, x2, y2, miniMapClip))
@@ -158,10 +156,10 @@ void ColonyEngine::drawDashboardStep1() {
 
 		const int dx = xcorner[1] - xcorner[0];
 		const int dy = ycorner[0] - ycorner[1];
-		drawMiniMapLine(xcorner[0] - dx, ycorner[0] + dy, xcorner[1] + dx, ycorner[1] - dy, accent);
-		drawMiniMapLine(xcorner[1] + dy, ycorner[1] + dx, xcorner[2] - dy, ycorner[2] - dx, accent);
-		drawMiniMapLine(xcorner[2] + dx, ycorner[2] - dy, xcorner[3] - dx, ycorner[3] + dy, accent);
-		drawMiniMapLine(xcorner[3] - dy, ycorner[3] - dx, xcorner[0] + dy, ycorner[0] + dx, accent);
+		drawMiniMapLine(xcorner[0] - dx, ycorner[0] + dy, xcorner[1] + dx, ycorner[1] - dy, 0); // Black for walls
+		drawMiniMapLine(xcorner[1] + dy, ycorner[1] + dx, xcorner[2] - dy, ycorner[2] - dx, 0);
+		drawMiniMapLine(xcorner[2] + dx, ycorner[2] - dy, xcorner[3] - dx, ycorner[3] + dy, 0);
+		drawMiniMapLine(xcorner[3] - dy, ycorner[3] - dx, xcorner[0] + dy, ycorner[0] + dx, 0);
 
 		auto drawMarker = [&](int x, int y, int halfSize, uint32 color) {
 			const int l = MAX<int>(_headsUpRect.left + 1, x - halfSize);
@@ -190,41 +188,45 @@ void ColonyEngine::drawDashboardStep1() {
 		};
 
 		if (hasFoodAt(_me.xindex, _me.yindex))
-			drawMarker(xcorner[4], ycorner[4], 1, miniMapObj);
+			drawMarker(xcorner[4], ycorner[4], 1, 0);
 
 		if (_me.yindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x01)) {
 			if (hasFoodAt(_me.xindex, _me.yindex - 1))
-				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 1, miniMapObj);
+				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 1, 0);
 			if (hasRobotAt(_me.xindex, _me.yindex - 1))
-				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 2, miniMapActor);
+				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 2, 0);
 		}
 		if (_me.xindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x02)) {
 			if (hasFoodAt(_me.xindex - 1, _me.yindex))
-				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 1, miniMapObj);
+				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 1, 0);
 			if (hasRobotAt(_me.xindex - 1, _me.yindex))
-				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 2, miniMapActor);
+				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 2, 0);
 		}
 		if (_me.yindex < 30 && !(_wall[_me.xindex][_me.yindex + 1] & 0x01)) {
 			if (hasFoodAt(_me.xindex, _me.yindex + 1))
-				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 1, miniMapObj);
+				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 1, 0);
 			if (hasRobotAt(_me.xindex, _me.yindex + 1))
-				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 2, miniMapActor);
+				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 2, 0);
 		}
 		if (_me.xindex < 30 && !(_wall[_me.xindex + 1][_me.yindex] & 0x02)) {
 			if (hasFoodAt(_me.xindex + 1, _me.yindex))
-				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 1, miniMapObj);
+				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 1, 0);
 			if (hasRobotAt(_me.xindex + 1, _me.yindex))
-				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 2, miniMapActor);
+				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 2, 0);
 		}
 
-		drawMarker(ccenterx, ccentery, 2, mark);
-		drawMarker(ccenterx, ccentery, 1, accent);
+		// Eye icon in the center
+		const int px = MAX(4, _dashBoardRect.width() / 10);
+		const int py = MAX(2, _dashBoardRect.width() / 24);
+		_gfx->drawEllipse(ccenterx, ccentery, px, py, 0); // Outer frame
+		_gfx->fillEllipse(ccenterx, ccentery, px / 2, py, 0); // Inner pupil
 	}
 
 	if (_powerRect.width() > 4 && _powerRect.height() > 4) {
-		_gfx->drawRect(_powerRect, frame);
+		_gfx->fillRect(_powerRect, 15); // White background
+		_gfx->drawRect(_powerRect, 1); // Blue frame
 		const int barY = _powerRect.bottom - MAX(3, _powerRect.width() / 8);
-		_gfx->drawLine(_powerRect.left + 1, barY, _powerRect.right - 2, barY, accent);
+		_gfx->drawLine(_powerRect.left + 1, barY, _powerRect.right - 2, barY, 0); // Black divider
 	}
 }
 
@@ -232,7 +234,7 @@ void ColonyEngine::drawCrosshair() {
 	if (!_crosshair || _screenR.width() <= 0 || _screenR.height() <= 0)
 		return;
 
-	const uint32 color = (_weapons > 0) ? 255 : 220;
+	const uint32 color = (_weapons > 0) ? 15 : 7;
 	const int cx = _centerX;
 	const int cy = _centerY;
 	const int qx = MAX(2, _screenR.width() / 32);


Commit: 9c0cd2c3e29712383c8f2ec6fb8f503fd5116a8b
    https://github.com/scummvm/scummvm/commit/9c0cd2c3e29712383c8f2ec6fb8f503fd5116a8b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:06+02:00

Commit Message:
COLONY: adding initial animations code

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 23a985ba8e1..daabde40e1d 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -113,10 +113,18 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_me.ang = 32;
 	_me.type = MENUM;
 
+	// Animation system init
+	_backgroundMask = nullptr;
+	_backgroundFG = nullptr;
+	_backgroundActive = false;
+	_divideBG = 0;
+
 	initTrig();
 }
 
 ColonyEngine::~ColonyEngine() {
+	deleteAnimation();
+	delete _gfx;
 }
 
 void ColonyEngine::loadMap(int mnum) {
@@ -304,7 +312,6 @@ Common::Error ColonyEngine::run() {
 		_system->delayMillis(10);
 	}
 
-	delete _gfx;
 	return Common::kNoError;
 }
 
@@ -339,35 +346,235 @@ void ColonyEngine::scrollInfo() {
 	}
 	_gfx->copyToScreen();
 
-	// Wait for keypress
-	Common::Event event;
-	bool pressed = false;
-	while (!pressed && !shouldQuit()) {
+	bool waiting = true;
+	while (waiting && !shouldQuit()) {
+		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN) {
-				pressed = true;
-				break;
-			}
+			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
+				waiting = false;
 		}
 		_system->delayMillis(10);
 	}
 
-	if (shouldQuit()) return;
+	_gfx->fillRect(_screenR, 0);
+	_gfx->copyToScreen();
+}
+
+bool ColonyEngine::loadAnimation(const Common::String &name) {
+	Common::String fileName = name + ".pic";
+	Common::File file;
+	if (!file.open(Common::Path(fileName))) {
+		warning("Could not open animation file %s", fileName.c_str());
+		return false;
+	}
+
+	deleteAnimation();
+
+	// Read background data
+	file.read(_topBG, 8);
+	file.read(_bottomBG, 8);
+	_divideBG = file.readSint16LE();
+	_backgroundActive = file.readSint16LE() != 0;
+	if (_backgroundActive) {
+		_backgroundClip = readRect(file);
+		_backgroundLocate = readRect(file);
+		_backgroundMask = loadImage(file);
+		_backgroundFG = loadImage(file);
+	}
+
+	// Read sprite data
+	int16 maxsprite = file.readSint16LE();
+	file.readSint16LE(); // locSprite
+	for (int i = 0; i < maxsprite; i++) {
+		Sprite *s = new Sprite();
+		s->fg = loadImage(file);
+		s->mask = loadImage(file);
+		s->used = file.readSint16LE() != 0;
+		s->clip = readRect(file);
+		s->locate = readRect(file);
+		_cSprites.push_back(s);
+	}
 
-	// Scroll up
-	for (int i = 0; i < _height; i += 8) {
-		_gfx->scroll(0, -8, _gfx->black());
+	// Read complex sprite data
+	int16 maxLSprite = file.readSint16LE();
+	file.readSint16LE(); // anum
+	for (int i = 0; i < maxLSprite; i++) {
+		ComplexSprite *ls = new ComplexSprite();
+		int16 size = file.readSint16LE();
+		for (int j = 0; j < size; j++) {
+			ComplexSprite::SubObject sub;
+			sub.spritenum = file.readSint16LE();
+			sub.xloc = file.readSint16LE();
+			sub.yloc = file.readSint16LE();
+			ls->objects.push_back(sub);
+		}
+		ls->bounds = readRect(file);
+		ls->visible = file.readSint16LE() != 0;
+		ls->current = file.readSint16LE();
+		ls->xloc = file.readSint16LE();
+		ls->yloc = file.readSint16LE();
+		ls->acurrent = file.readSint16LE();
+		ls->axloc = file.readSint16LE();
+		ls->ayloc = file.readSint16LE();
+		ls->type = file.readByte();
+		ls->frozen = file.readByte();
+		ls->locked = file.readByte();
+		ls->link = file.readSint16LE();
+		ls->key = file.readSint16LE();
+		ls->lock = file.readSint16LE();
+		ls->onoff = true;
+		_lSprites.push_back(ls);
+	}
+
+	return true;
+}
+
+void ColonyEngine::deleteAnimation() {
+	delete _backgroundMask; _backgroundMask = nullptr;
+	delete _backgroundFG; _backgroundFG = nullptr;
+	for (uint i = 0; i < _cSprites.size(); i++) delete _cSprites[i];
+	_cSprites.clear();
+	for (uint i = 0; i < _lSprites.size(); i++) delete _lSprites[i];
+	_lSprites.clear();
+}
+
+void ColonyEngine::playAnimation() {
+	bool running = true;
+	while (running && !shouldQuit()) {
+		drawAnimation();
 		_gfx->copyToScreen();
-		_system->delayMillis(10);
-		
-		// Allow skipping scroll
+
+		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN) {
-				return;
+			if (event.type == Common::EVENT_LBUTTONDOWN) {
+				int item = whichSprite(event.mouse);
+				if (item > 0) {
+					handleAnimationClick(item);
+				} else {
+					// Clicked background or nothing? Exit for now as a safeguard
+					running = false;
+				}
+			} else if (event.type == Common::EVENT_KEYDOWN) {
+				if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
+					running = false;
+			}
+		}
+		_system->delayMillis(20);
+	}
+	deleteAnimation();
+}
+
+void ColonyEngine::drawAnimation() {
+	_gfx->fillRect(_screenR, 0); // Black background
+
+	// Draw background if active
+	if (_backgroundActive && _backgroundFG) {
+		// Blit background using white as color 15
+		Image *img = _backgroundFG;
+		int x = _backgroundLocate.left;
+		int y = _backgroundLocate.top;
+		for (int iy = 0; iy < img->height; iy++) {
+			for (int ix = 0; ix < img->width; ix++) {
+				int byteIdx = iy * img->rowBytes + (ix / 8);
+				int bitIdx = 7 - (ix % 8);
+				if (img->data[byteIdx] & (1 << bitIdx)) {
+					_gfx->setPixel(x + ix, y + iy, 15);
+				}
+			}
+		}
+	}
+
+	// Draw complex sprites
+	for (uint i = 0; i < _lSprites.size(); i++) {
+		if (_lSprites[i]->onoff)
+			drawComplexSprite(i);
+	}
+}
+
+void ColonyEngine::drawComplexSprite(int index) {
+	ComplexSprite *ls = _lSprites[index];
+	if (!ls->onoff) return;
+
+	int cnum = ls->current;
+	if (cnum < 0 || cnum >= (int)ls->objects.size()) return;
+
+	int spriteIdx = ls->objects[cnum].spritenum;
+	if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size()) return;
+
+	Sprite *s = _cSprites[spriteIdx];
+	int x = ls->xloc + ls->objects[cnum].xloc;
+	int y = ls->yloc + ls->objects[cnum].yloc;
+
+	// Simplistic 1-bit to 8-bit blitter using White (15) as foreground
+	if (s->fg && s->fg->data) {
+		Image *img = s->fg;
+		for (int iy = 0; iy < img->height; iy++) {
+			for (int ix = 0; ix < img->width; ix++) {
+				int byteIdx = iy * img->rowBytes + (ix / 8);
+				int bitIdx = 7 - (ix % 8);
+				if (img->data[byteIdx] & (1 << bitIdx)) {
+					_gfx->setPixel(x + ix, y + iy, 15);
+				}
 			}
 		}
-		if (shouldQuit()) return;
 	}
 }
 
+Image *ColonyEngine::loadImage(Common::SeekableReadStream &file) {
+	Image *im = new Image();
+	im->width = file.readSint16LE();
+	im->height = file.readSint16LE();
+	im->align = file.readSint16LE();
+	im->rowBytes = file.readSint16LE();
+	im->bits = file.readByte();
+	im->planes = file.readByte();
+
+	int16 tf = file.readSint16LE();
+	uint32 size;
+	if (tf) {
+		/* uint32 bsize = */ file.readUint32LE();
+		size = file.readUint32LE();
+		im->data = (byte *)malloc(size);
+		unpackBytes(file, im->data, size);
+	} else {
+		size = file.readUint32LE();
+		im->data = (byte *)malloc(size);
+		file.read(im->data, size);
+	}
+	return im;
+}
+
+void ColonyEngine::unpackBytes(Common::SeekableReadStream &file, byte *dst, uint32 len) {
+	uint32 i = 0;
+	while (i < len) {
+		byte count = file.readByte();
+		byte value = file.readByte();
+		for (int j = 0; j < count && i < len; j++) {
+			dst[i++] = value;
+		}
+	}
+}
+
+Common::Rect ColonyEngine::readRect(Common::SeekableReadStream &file) {
+	int16 top = file.readSint16LE();
+	int16 left = file.readSint16LE();
+	int16 bottom = file.readSint16LE();
+	int16 right = file.readSint16LE();
+	return Common::Rect(left, top, right, bottom);
+}
+
+int ColonyEngine::whichSprite(const Common::Point &p) {
+	for (int i = _lSprites.size() - 1; i >= 0; i--) {
+		if (_lSprites[i]->onoff && _lSprites[i]->bounds.contains(p)) {
+			return i + 1;
+		}
+	}
+	return 0;
+}
+
+void ColonyEngine::handleAnimationClick(int item) {
+	// Dummy for now - real logic depends on specific animation
+	debug("Animation click on item %d", item);
+}
+
 } // End of namespace Colony
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 1987e678836..8fce1eed210 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -116,6 +116,53 @@ struct Thing {
 	// void (*think)();
 };
 
+struct Image {
+	int16 width;
+	int16 height;
+	int16 align;
+	int16 rowBytes;
+	int8 bits;
+	int8 planes;
+	byte *data;
+
+	Image() : width(0), height(0), align(0), rowBytes(0), bits(0), planes(0), data(nullptr) {}
+	~Image() { free(data); }
+};
+
+struct Sprite {
+	Image *fg;
+	Image *mask;
+	Common::Rect clip;
+	Common::Rect locate;
+	bool used;
+
+	Sprite() : fg(nullptr), mask(nullptr), used(false) {}
+	~Sprite() { delete fg; delete mask; }
+};
+
+struct ComplexSprite {
+	struct SubObject {
+		int16 spritenum;
+		int16 xloc, yloc;
+	};
+	Common::Array<SubObject> objects;
+	Common::Rect bounds;
+	bool visible;
+	int16 current;
+	int16 xloc, yloc;
+	int16 acurrent;
+	int16 axloc, ayloc;
+	uint8 type;
+	uint8 frozen;
+	uint8 locked;
+	int16 link;
+	int16 key;
+	int16 lock;
+	bool onoff;
+
+	ComplexSprite() : visible(false), current(0), xloc(0), yloc(0), acurrent(0), axloc(0), ayloc(0), type(0), frozen(0), locked(0), link(0), key(0), lock(0), onoff(true) {}
+};
+
 class ColonyEngine : public Engine {
 public:
 	ColonyEngine(OSystem *syst, const ADGameDescription *gd);
@@ -240,6 +287,29 @@ private:
 	void updateViewportLayout();
 	void drawDashboardStep1();
 	void drawCrosshair();
+
+	// Animation system
+	Common::Array<Sprite *> _cSprites;
+	Common::Array<ComplexSprite *> _lSprites;
+	Image *_backgroundMask;
+	Image *_backgroundFG;
+	Common::Rect _backgroundClip;
+	Common::Rect _backgroundLocate;
+	bool _backgroundActive;
+	byte _topBG[8];
+	byte _bottomBG[8];
+	int16 _divideBG;
+
+	bool loadAnimation(const Common::String &name);
+	void deleteAnimation();
+	void playAnimation();
+	void drawAnimation();
+	void drawComplexSprite(int index);
+	Image *loadImage(Common::SeekableReadStream &file);
+	void unpackBytes(Common::SeekableReadStream &file, byte *dst, uint32 len);
+	Common::Rect readRect(Common::SeekableReadStream &file);
+	int whichSprite(const Common::Point &p);
+	void handleAnimationClick(int item);
 };
 
 } // End of namespace Colony
diff --git a/engines/colony/gfx.cpp b/engines/colony/gfx.cpp
index 5071238cbab..86858064fe0 100644
--- a/engines/colony/gfx.cpp
+++ b/engines/colony/gfx.cpp
@@ -88,6 +88,11 @@ void Gfx::fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2)
 	}
 }
 
+void Gfx::setPixel(int x, int y, uint32 color) {
+	if (x >= 0 && x < _width && y >= 0 && y < _height)
+		_surface.setPixel(x, y, color);
+}
+
 void Gfx::copyToScreen() {
 	_system->copyRectToScreen(_surface.getPixels(), _surface.pitch, 0, 0, _surface.w, _surface.h);
 	_system->updateScreen();
diff --git a/engines/colony/gfx.h b/engines/colony/gfx.h
index 38f10433005..29393bb7be5 100644
--- a/engines/colony/gfx.h
+++ b/engines/colony/gfx.h
@@ -45,6 +45,7 @@ public:
 	void drawEllipse(int x, int y, int rx, int ry, uint32 color);
 	void fillEllipse(int x, int y, int rx, int ry, uint32 color);
 	void fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2);
+	void setPixel(int x, int y, uint32 color);
 
 	void copyToScreen();
 
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index e36eced1955..62f5c40eeb7 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -468,31 +468,25 @@ void ColonyEngine::interactWithObject(int objNum) {
 	const int x = CLIP<int>(obj.where.xindex, 0, 30);
 	const int y = CLIP<int>(obj.where.yindex, 0, 30);
 	const int action0 = _mapData[x][y][4][3];
-	const int action1 = _mapData[x][y][4][4];
 
 	switch (obj.type) {
 	case kObjDesk:
-		if (!_hasKeycard) {
-			_hasKeycard = true;
-			debug("CCommand: DESK granted keycard");
-		} else {
-			debug("CCommand: DESK action (%d, %d)", action0, action1);
-		}
+		if (loadAnimation("desk"))
+			playAnimation();
 		break;
 	case kObjConsole:
 		switch (action0) {
-		case 1:
-			debug("CCommand: CONSOLE reactor (%d)", action1);
+		case 1: // Reactor console
+			if (loadAnimation("reactor"))
+				playAnimation();
 			break;
-		case 2:
-		{
-			const int opened = openAdjacentDoors(_me.xindex, _me.yindex);
-			debug("CCommand: CONSOLE controls opened %d nearby doors", opened);
+		case 2: // Main ship controls
+			if (loadAnimation("controls"))
+				playAnimation();
 			break;
-		}
-		case 3:
-			_unlocked = true;
-			debug("CCommand: CONSOLE security unlocked");
+		case 3: // Security console
+			if (loadAnimation("security"))
+				playAnimation();
 			break;
 		default:
 			debug("CCommand: CONSOLE action=%d", action0);
@@ -545,12 +539,8 @@ void ColonyEngine::interactWithObject(int objNum) {
 		break;
 	}
 	case kObjDrawer:
-		if (!_hasKeycard) {
-			_hasKeycard = true;
-			debug("CCommand: DRAWER vanity=%d (picked keycard)", action0);
-		} else {
-			debug("CCommand: DRAWER vanity=%d", action0);
-		}
+		if (loadAnimation("vanity"))
+			playAnimation();
 		break;
 	case kObjScreen:
 		debug("CCommand: SCREEN");


Commit: 88d55733f296105e58134ead04232818b4092e2b
    https://github.com/scummvm/scummvm/commit/88d55733f296105e58134ead04232818b4092e2b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:06+02:00

Commit Message:
COLONY: refactor animation loop to use frame-based sprite rendering

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index daabde40e1d..ceff980e0da 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -465,33 +465,25 @@ void ColonyEngine::playAnimation() {
 }
 
 void ColonyEngine::drawAnimation() {
-	_gfx->fillRect(_screenR, 0); // Black background
+	_gfx->clear(0); // Ensure entire screen is black
+
+	// Center 320x200 animation on 640x480 screen
+	int ox = (640 - 320) / 2;
+	int oy = (480 - 200) / 2;
 
 	// Draw background if active
 	if (_backgroundActive && _backgroundFG) {
-		// Blit background using white as color 15
-		Image *img = _backgroundFG;
-		int x = _backgroundLocate.left;
-		int y = _backgroundLocate.top;
-		for (int iy = 0; iy < img->height; iy++) {
-			for (int ix = 0; ix < img->width; ix++) {
-				int byteIdx = iy * img->rowBytes + (ix / 8);
-				int bitIdx = 7 - (ix % 8);
-				if (img->data[byteIdx] & (1 << bitIdx)) {
-					_gfx->setPixel(x + ix, y + iy, 15);
-				}
-			}
-		}
+		drawAnimationImage(_backgroundFG, nullptr, ox + _backgroundLocate.left, oy + _backgroundLocate.top);
 	}
 
 	// Draw complex sprites
 	for (uint i = 0; i < _lSprites.size(); i++) {
 		if (_lSprites[i]->onoff)
-			drawComplexSprite(i);
+			drawComplexSprite(i, ox, oy);
 	}
 }
 
-void ColonyEngine::drawComplexSprite(int index) {
+void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
 	ComplexSprite *ls = _lSprites[index];
 	if (!ls->onoff) return;
 
@@ -502,20 +494,31 @@ void ColonyEngine::drawComplexSprite(int index) {
 	if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size()) return;
 
 	Sprite *s = _cSprites[spriteIdx];
-	int x = ls->xloc + ls->objects[cnum].xloc;
-	int y = ls->yloc + ls->objects[cnum].yloc;
-
-	// Simplistic 1-bit to 8-bit blitter using White (15) as foreground
-	if (s->fg && s->fg->data) {
-		Image *img = s->fg;
-		for (int iy = 0; iy < img->height; iy++) {
-			for (int ix = 0; ix < img->width; ix++) {
-				int byteIdx = iy * img->rowBytes + (ix / 8);
-				int bitIdx = 7 - (ix % 8);
-				if (img->data[byteIdx] & (1 << bitIdx)) {
-					_gfx->setPixel(x + ix, y + iy, 15);
-				}
+	int x = ox + ls->xloc + ls->objects[cnum].xloc;
+	int y = oy + ls->yloc + ls->objects[cnum].yloc;
+
+	drawAnimationImage(s->fg, s->mask, x, y);
+}
+
+void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y) {
+	if (!img || !img->data) return;
+
+	for (int iy = 0; iy < img->height; iy++) {
+		for (int ix = 0; ix < img->width; ix++) {
+			int byteIdx = iy * img->rowBytes + (ix / 8);
+			int bitIdx = 7 - (ix % 8);
+			
+			bool maskSet = true;
+			if (mask && mask->data) {
+				maskSet = (mask->data[byteIdx] & (1 << bitIdx)) != 0;
 			}
+
+			if (!maskSet) continue;
+
+			bool fgSet = (img->data[byteIdx] & (1 << bitIdx)) != 0;
+			uint32 color = fgSet ? 15 : 0;
+
+			_gfx->setPixel(x + ix, y + iy, color);
 		}
 	}
 }
@@ -564,8 +567,12 @@ Common::Rect ColonyEngine::readRect(Common::SeekableReadStream &file) {
 }
 
 int ColonyEngine::whichSprite(const Common::Point &p) {
+	int ox = (640 - 320) / 2;
+	int oy = (480 - 200) / 2;
+	Common::Point pt(p.x - ox, p.y - oy);
+
 	for (int i = _lSprites.size() - 1; i >= 0; i--) {
-		if (_lSprites[i]->onoff && _lSprites[i]->bounds.contains(p)) {
+		if (_lSprites[i]->onoff && _lSprites[i]->bounds.contains(pt)) {
 			return i + 1;
 		}
 	}
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 8fce1eed210..e39ca69bd8c 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -304,7 +304,8 @@ private:
 	void deleteAnimation();
 	void playAnimation();
 	void drawAnimation();
-	void drawComplexSprite(int index);
+	void drawComplexSprite(int index, int ox, int oy);
+	void drawAnimationImage(Image *img, Image *mask, int x, int y);
 	Image *loadImage(Common::SeekableReadStream &file);
 	void unpackBytes(Common::SeekableReadStream &file, byte *dst, uint32 len);
 	Common::Rect readRect(Common::SeekableReadStream &file);


Commit: d709c24db646df61bf0ceca819f0c58f42aff25c
    https://github.com/scummvm/scummvm/commit/d709c24db646df61bf0ceca819f0c58f42aff25c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:07+02:00

Commit Message:
COLONY: migration to true 3d

Changed paths:
  A engines/colony/renderer.h
  A engines/colony/renderer_opengl.cpp
  A engines/colony/renderer_software.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/gfx.cpp
    engines/colony/gfx.h
    engines/colony/module.mk
    engines/colony/render.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index ceff980e0da..a76f7e05e84 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -21,6 +21,7 @@
 
 #include "colony/colony.h"
 #include "colony/gfx.h"
+#include "common/config-manager.h"
 #include "common/file.h"
 #include "common/system.h"
 #include "common/util.h"
@@ -90,6 +91,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_hasKeycard = false;
 	_unlocked = false;
 	_weapons = 0;
+	_wireframe = true;
 	
 	memset(_wall, 0, sizeof(_wall));
 	memset(_mapData, 0, sizeof(_mapData));
@@ -212,8 +214,13 @@ void ColonyEngine::initTrig() {
 }
 
 Common::Error ColonyEngine::run() {
-	Graphics::PixelFormat format8bpp = Graphics::PixelFormat::createFormatCLUT8();
-	initGraphics(_width, _height, &format8bpp);
+	Common::String renderer = "opengl"; //ConfMan.get("renderer");
+	if (renderer == "software") {
+		Graphics::PixelFormat format8bpp = Graphics::PixelFormat::createFormatCLUT8();
+		initGraphics(_width, _height, &format8bpp);
+	} else {
+		initGraphics3d(_width, _height);
+	}
 
 	_width = _system->getWidth();
 	_height = _system->getHeight();
@@ -239,9 +246,12 @@ Common::Error ColonyEngine::run() {
 		pal[i * 3 + 1] = i;
 		pal[i * 3 + 2] = i;
 	}
-	_system->getPaletteManager()->setPalette(pal, 0, 256);
-
+	if (renderer == "software") {
+		_system->getPaletteManager()->setPalette(pal, 0, 256);
+	}
 	_gfx = new Gfx(_system, _width, _height);
+	_gfx->setPalette(pal, 0, 256);
+	_gfx->setWireframe(_wireframe);
 	
 	scrollInfo();
 
@@ -279,9 +289,8 @@ Common::Error ColonyEngine::run() {
 						_change = true;
 						break;
 					case Common::KEYCODE_F7:
-						_showDashBoard = !_showDashBoard;
-						updateViewportLayout();
-						_system->warpMouse(_centerX, _centerY);
+						_wireframe = !_wireframe;
+						_gfx->setWireframe(_wireframe);
 						_change = true;
 						break;
 					default:
@@ -295,6 +304,12 @@ Common::Error ColonyEngine::run() {
 					_me.look = (uint8)((int)_me.look - (event.relMouse.x / 2));
 					_change = true;
 				}
+				if (event.relMouse.y != 0) {
+					// Add/Subtract based on standard vertical look (pull back to look up)
+					// Mouse down is positive rel Y.
+					_me.lookY = (int8)CLIP<int>((int)_me.lookY - (event.relMouse.y / 2), -64, 64);
+					_change = true;
+				}
 			}
 		}
 		_system->warpMouse(_centerX, _centerY);
@@ -304,7 +319,9 @@ Common::Error ColonyEngine::run() {
 			_objects[i].visible = 0;
 		
 		corridor();
-		drawStaticObjects();
+		if (renderer == "software") {
+			drawStaticObjects();
+		}
 		drawDashboardStep1();
 		drawCrosshair();
 		
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index e39ca69bd8c..b168e046050 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -87,6 +87,7 @@ enum ObjectType {
 struct Locate {
 	uint8 ang;
 	uint8 look;
+	int8  lookY;
 	int lookx;
 	int delta;
 	int xloc;
@@ -211,6 +212,7 @@ private:
 	bool _hasKeycard;
 	bool _unlocked;
 	int _weapons;
+	bool _wireframe;
 
 	int _frntxWall, _frntyWall;
 	int _sidexWall, _sideyWall;
@@ -267,17 +269,27 @@ private:
 		bool vsurface[kMaxSurfaces];
 		bool visible;
 	};
+public:
 	struct PrismPartDef {
 		int pointCount;
 		const int (*points)[3];
 		int surfaceCount;
 		const int (*surfaces)[8];
 	};
+ 
+private:
 	bool projectPrismPart(const Thing &obj, const PrismPartDef &part, bool useLook, ProjectedPrismPart &out) const;
 	bool isSurfaceClockwise(const ProjectedPrismPart &part, const int surface[8]) const;
 	bool clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const;
 	void drawProjectedPrism(const ProjectedPrismPart &part, const PrismPartDef &def, int force, uint32 color, const Common::Rect &clip);
+	void draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, uint32 color);
 	bool drawStaticObjectPrisms(const Thing &obj, uint32 baseColor);
+	bool drawStaticObjectPrisms3D(const Thing &obj, uint32 baseColor);
+
+	void drawWall(int x1, int y1, int x2, int y2, uint32 color);
+	void renderCorridor3D();
+
+	// Visibility / Clipping
 	void drawStaticObjectFallback(const Thing &obj, uint32 color, int depth, int sx);
 	int occupiedObjectAt(int x, int y, const Locate *pobject);
 	void interactWithObject(int objNum);
diff --git a/engines/colony/gfx.cpp b/engines/colony/gfx.cpp
index 86858064fe0..2fb748c0ba0 100644
--- a/engines/colony/gfx.cpp
+++ b/engines/colony/gfx.cpp
@@ -20,82 +20,22 @@
  */
 
 #include "common/system.h"
-#include "common/util.h"
+#include "common/config-manager.h"
 #include "colony/gfx.h"
-#include "graphics/font.h"
 
 namespace Colony {
 
-Gfx::Gfx(OSystem *system, int width, int height) : _system(system), _width(width), _height(height) {
-	_surface.create(width, height, _system->getScreenFormat());
-}
-
-Gfx::~Gfx() {
-	_surface.free();
-}
-
-void Gfx::clear(uint32 color) {
-	_surface.clear(color);
-}
-
-void Gfx::drawLine(int x1, int y1, int x2, int y2, uint32 color) {
-	_surface.drawLine(x1, y1, x2, y2, color);
-}
-
-void Gfx::drawRect(const Common::Rect &rect, uint32 color) {
-	_surface.frameRect(rect, color);
-}
-
-void Gfx::fillRect(const Common::Rect &rect, uint32 color) {
-	_surface.fillRect(rect, color);
-}
-
-void Gfx::drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) {
-	font->drawString(&_surface, str, x, y, (align == Graphics::kTextAlignCenter && x == 0) ? _width : (_width - x), color, align);
-}
-
-void Gfx::scroll(int dx, int dy, uint32 background) {
-	if (abs(dx) >= _width || abs(dy) >= _height) {
-		clear(background);
-		return;
+Gfx::Gfx(OSystem *system, int width, int height) {
+	Common::String renderer = ConfMan.get("renderer");
+	if (renderer == "software") {
+		_renderer = createSoftwareRenderer(system, width, height);
+	} else {
+		_renderer = createOpenGLRenderer(system, width, height);
 	}
-
-	Graphics::ManagedSurface tmp;
-	tmp.create(_width, _height, _surface.format);
-	tmp.blitFrom(_surface);
-
-	clear(background);
-	_surface.blitFrom(tmp, Common::Rect(0, 0, _width, _height), Common::Rect(dx, dy, dx + _width, dy + _height));
-	tmp.free();
-}
-
-void Gfx::drawEllipse(int x, int y, int rx, int ry, uint32 color) {
-	_surface.drawEllipse(x - rx, y - ry, x + rx, y + ry, color, false);
-}
-
-void Gfx::fillEllipse(int x, int y, int rx, int ry, uint32 color) {
-	_surface.drawEllipse(x - rx, y - ry, x + rx, y + ry, color, true);
 }
 
-void Gfx::fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) {
-	for (int y = rect.top; y < rect.bottom; y++) {
-		for (int x = rect.left; x < rect.right; x++) {
-			if ((x + y) % 2 == 0)
-				_surface.setPixel(x, y, color1);
-			else
-				_surface.setPixel(x, y, color2);
-		}
-	}
-}
-
-void Gfx::setPixel(int x, int y, uint32 color) {
-	if (x >= 0 && x < _width && y >= 0 && y < _height)
-		_surface.setPixel(x, y, color);
-}
-
-void Gfx::copyToScreen() {
-	_system->copyRectToScreen(_surface.getPixels(), _surface.pitch, 0, 0, _surface.w, _surface.h);
-	_system->updateScreen();
+Gfx::~Gfx() {
+	delete _renderer;
 }
 
 } // End of namespace Colony
diff --git a/engines/colony/gfx.h b/engines/colony/gfx.h
index 29393bb7be5..50dec546425 100644
--- a/engines/colony/gfx.h
+++ b/engines/colony/gfx.h
@@ -22,10 +22,7 @@
 #ifndef COLONY_GFX_H
 #define COLONY_GFX_H
 
-#include "common/scummsys.h"
-#include "common/rect.h"
-#include "graphics/managed_surface.h"
-#include "graphics/font.h"
+#include "colony/renderer.h"
 
 namespace Colony {
 
@@ -34,29 +31,48 @@ public:
 	Gfx(OSystem *system, int width, int height);
 	~Gfx();
 
-	void clear(uint32 color);
-	void drawLine(int x1, int y1, int x2, int y2, uint32 color);
-	void drawRect(const Common::Rect &rect, uint32 color);
-	void fillRect(const Common::Rect &rect, uint32 color);
+	void clear(uint32 color) { _renderer->clear(color); }
+	void drawLine(int x1, int y1, int x2, int y2, uint32 color) { _renderer->drawLine(x1, y1, x2, y2, color); }
+	void drawRect(const Common::Rect &rect, uint32 color) { _renderer->drawRect(rect, color); }
+	void fillRect(const Common::Rect &rect, uint32 color) { _renderer->fillRect(rect, color); }
 	
-	void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
-	void scroll(int dx, int dy, uint32 background);
+	void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align = Graphics::kTextAlignLeft) {
+		_renderer->drawString(font, str, x, y, color, align);
+	}
+	void scroll(int dx, int dy, uint32 background) { _renderer->scroll(dx, dy, background); }
 
-	void drawEllipse(int x, int y, int rx, int ry, uint32 color);
-	void fillEllipse(int x, int y, int rx, int ry, uint32 color);
-	void fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2);
-	void setPixel(int x, int y, uint32 color);
+	void drawEllipse(int x, int y, int rx, int ry, uint32 color) { _renderer->drawEllipse(x, y, rx, ry, color); }
+	void fillEllipse(int x, int y, int rx, int ry, uint32 color) { _renderer->fillEllipse(x, y, rx, ry, color); }
+	void fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) { _renderer->fillDitherRect(rect, color1, color2); }
+	void setPixel(int x, int y, uint32 color) { _renderer->setPixel(x, y, color); }
+	void setPalette(const byte *palette, uint start, uint count) { _renderer->setPalette(palette, start, count); }
+ 
+	void begin3D(int camX, int camY, int camZ, int angle, int angleY, const Common::Rect &viewport) { _renderer->begin3D(camX, camY, camZ, angle, angleY, viewport); }
+	void draw3DWall(int x1, int y1, int x2, int y2, uint32 color) { _renderer->draw3DWall(x1, y1, x2, y2, color); }
+	void draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) {
+		_renderer->draw3DQuad(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, color);
+	}
+	void draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) {
+		_renderer->draw3DPolygon(x, y, z, count, color);
+	}
+	void end3D() { _renderer->end3D(); }
+	bool isAccelerated() const { return _renderer->isAccelerated(); }
 
-	void copyToScreen();
+	void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) {
+		_renderer->drawQuad(x1, y1, x2, y2, x3, y3, x4, y4, color);
+	}
+	void drawPolygon(const int *x, const int *y, int count, uint32 color) {
+		_renderer->drawPolygon(x, y, count, color);
+	}
+
+	void copyToScreen() { _renderer->copyToScreen(); }
+	void setWireframe(bool enable) { _renderer->setWireframe(enable); }
 
 	uint32 white() { return 255; }
 	uint32 black() { return 0; }
 
 private:
-	OSystem *_system;
-	Graphics::ManagedSurface _surface;
-	int _width;
-	int _height;
+	Renderer *_renderer;
 };
 
 } // End of namespace Colony
diff --git a/engines/colony/module.mk b/engines/colony/module.mk
index 16f53b98645..76b35e3ab6a 100644
--- a/engines/colony/module.mk
+++ b/engines/colony/module.mk
@@ -5,6 +5,8 @@ MODULE_OBJS := \
 	gfx.o \
 	metaengine.o \
 	render.o \
+	renderer_opengl.o \
+	renderer_software.o \
 	ui.o
 
 MODULE_DIRS += \
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 064e72a3c96..c7344c0164d 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -38,7 +38,303 @@ static const int g_indexTable[4][10] = {
 
 static const int g_dirRight[4] = {1, 3, 0, 2};
 static const int g_dirLeft[4] = {2, 0, 3, 1};
-
+ 
+ // DOS object geometry constants
+static const int kScreenPts[8][3] = {
+	{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
+	{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
+};
+static const int kScreenSurf[4][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kTableTopPts[4][3] = {
+	{-128, 128, 100}, {128, 128, 100}, {128, -128, 100}, {-128, -128, 100}
+};
+static const int kTableTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kTableBasePts[8][3] = {
+	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+	{-5, 5, 100}, {5, 5, 100}, {5, -5, 100}, {-5, -5, 100}
+};
+static const int kTableBaseSurf[4][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kBedPostPts[4][3] = {
+	{-82, 128, 100}, {82, 128, 100}, {82, 128, 0}, {-82, 128, 0}
+};
+static const int kBBedPostPts[4][3] = {
+	{-130, 128, 100}, {130, 128, 100}, {130, 128, 0}, {-130, 128, 0}
+};
+static const int kBedPostSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kBlanketSurf[4][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kSheetSurf[3][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kBedBlanketPts[8][3] = {
+	{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
+	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
+};
+static const int kBedSheetPts[8][3] = {
+	{-80, 128, 30}, {80, 128, 30}, {80, 70, 30}, {-80, 70, 30},
+	{-80, 128, 60}, {80, 128, 60}, {80, 70, 60}, {-80, 70, 60}
+};
+static const int kBBedBlanketPts[8][3] = {
+	{-128, 70, 0}, {128, 70, 0}, {128, -175, 0}, {-128, -175, 0},
+	{-128, 70, 60}, {128, 70, 60}, {128, -175, 60}, {-128, -175, 60}
+};
+static const int kBBedSheetPts[8][3] = {
+	{-128, 128, 30}, {128, 128, 30}, {128, 70, 30}, {-128, 70, 30},
+	{-128, 128, 60}, {128, 128, 60}, {128, 70, 60}, {-128, 70, 60}
+};
+static const int kDeskTopPts[4][3] = {
+	{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
+};
+static const int kDeskTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kDeskLeftPts[8][3] = {
+	{-135, 95, 0}, {-55, 95, 0}, {-55, -95, 0}, {-135, -95, 0},
+	{-135, 95, 100}, {-55, 95, 100}, {-55, -95, 100}, {-135, -95, 100}
+};
+static const int kDeskRightPts[8][3] = {
+	{55, 95, 0}, {135, 95, 0}, {135, -95, 0}, {55, -95, 0},
+	{55, 95, 100}, {135, 95, 100}, {135, -95, 100}, {55, -95, 100}
+};
+static const int kDeskCabSurf[4][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kSeatPts[4][3] = {
+	{-40, 210, 60}, {40, 210, 60}, {40, 115, 60}, {-40, 115, 60}
+};
+static const int kSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kArmLeftPts[4][3] = {
+	{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
+};
+static const int kArmRightPts[4][3] = {
+	{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
+};
+static const int kArmSurf[2][8] = {
+	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+};
+static const int kBackPts[4][3] = {
+	{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
+};
+static const int kBackSurf[2][8] = {
+	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+};
+static const int kComputerPts[8][3] = {
+	{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
+	{70, 25, 120}, {120, 25, 120}, {120, -25, 120}, {70, -25, 120}
+};
+static const int kMonitorPts[8][3] = {
+	{75, 20, 120}, {115, 20, 120}, {115, -20, 120}, {75, -20, 120},
+	{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
+};
+static const int kComputerSurf[5][8] = {
+	{0, 4, 7, 6, 5, 4, 0, 0}, {0, 4, 0, 3, 7, 4, 0, 0},
+	{0, 4, 3, 2, 6, 7, 0, 0}, {0, 4, 1, 0, 4, 5, 0, 0},
+	{0, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kDeskScreenPts[4][3] = {
+	{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
+};
+static const int kDeskScreenSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCSeatPts[4][3] = {
+	{-40, 40, 60}, {40, 40, 60}, {40, -40, 60}, {-40, -40, 60}
+};
+static const int kCSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCArmLeftPts[4][3] = {
+	{-50, 40, 90}, {-40, 40, 60}, {-40, -40, 60}, {-50, -40, 90}
+};
+static const int kCArmLeftSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCArmRightPts[4][3] = {
+	{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
+};
+static const int kCArmRightSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCBackPts[4][3] = {
+	{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
+};
+static const int kCBackSurf[2][8] = {
+	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+};
+static const int kCBasePts[8][3] = {
+	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+	{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
+};
+static const int kCBaseSurf[4][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kConsolePts[8][3] = {
+	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+	{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
+};
+static const int kConsoleSurf[5][8] = {
+	{0, 4, 4, 0, 3, 7, 0, 0}, {0, 4, 7, 3, 2, 6, 0, 0},
+	{0, 4, 5, 1, 0, 4, 0, 0}, {0, 4, 6, 2, 1, 5, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kCouchSurf[5][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kACouchPts[8][3] = {
+	{-50, 150, 0}, {50, 150, 0}, {50, -150, 0}, {-50, -150, 0},
+	{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50}
+};
+static const int kBCouchPts[8][3] = {
+	{-80, 150, 0}, {-45, 150, 0}, {-45, -150, 0}, {-80, -150, 0},
+	{-80, 150, 120}, {-55, 150, 120}, {-55, -150, 120}, {-80, -150, 120}
+};
+static const int kCCouchPts[8][3] = {
+	{-70, 170, 0}, {50, 170, 0}, {50, 150, 0}, {-70, 150, 0},
+	{-70, 170, 80}, {50, 170, 80}, {50, 150, 80}, {-70, 150, 80}
+};
+static const int kDCouchPts[8][3] = {
+	{-70, -150, 0}, {50, -150, 0}, {50, -170, 0}, {-70, -170, 0},
+	{-70, -150, 80}, {50, -150, 80}, {50, -170, 80}, {-70, -170, 80}
+};
+static const int kAChairPts[8][3] = {
+	{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
+	{-50, 50, 50}, {50, 50, 50}, {50, -50, 50}, {-50, -50, 50}
+};
+static const int kBChairPts[8][3] = {
+	{-80, 50, 0}, {-45, 50, 0}, {-45, -50, 0}, {-80, -50, 0},
+	{-80, 50, 120}, {-55, 50, 120}, {-55, -50, 120}, {-80, -50, 120}
+};
+static const int kCChairPts2[8][3] = {
+	{-70, 70, 0}, {50, 70, 0}, {50, 50, 0}, {-70, 50, 0},
+	{-70, 70, 80}, {50, 70, 80}, {50, 50, 80}, {-70, 50, 80}
+};
+static const int kDChairPts[8][3] = {
+	{-70, -50, 0}, {50, -50, 0}, {50, -70, 0}, {-70, -70, 0},
+	{-70, -50, 80}, {50, -50, 80}, {50, -70, 80}, {-70, -70, 80}
+};
+static const int kTVBodyPts[8][3] = {
+	{-30, 60, 0}, {30, 60, 0}, {30, -60, 0}, {-30, -60, 0},
+	{-30, 60, 120}, {30, 60, 120}, {30, -60, 120}, {-30, -60, 120}
+};
+static const int kTVBodySurf[5][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kTVScreenPts[4][3] = {
+	{30, 50, 10}, {30, -50, 10}, {30, 50, 110}, {30, -50, 110}
+};
+static const int kTVScreenSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
+static const int kDrawerPts[8][3] = {
+	{-80, 70, 0}, {0, 70, 0}, {0, -70, 0}, {-80, -70, 0},
+	{-80, 70, 100}, {0, 70, 100}, {0, -70, 100}, {-80, -70, 100}
+};
+static const int kDrawerSurf[5][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kMirrorPts[4][3] = {
+	{-80, 65, 100}, {-80, -65, 100}, {-80, 65, 210}, {-80, -65, 210}
+};
+static const int kMirrorSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
+static const Colony::ColonyEngine::PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
+static const Colony::ColonyEngine::PrismPartDef kTableParts[2] = {
+	{4, kTableTopPts, 1, kTableTopSurf},
+	{8, kTableBasePts, 4, kTableBaseSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kBedParts[3] = {
+	{4, kBedPostPts, 1, kBedPostSurf},
+	{8, kBedBlanketPts, 4, kBlanketSurf},
+	{8, kBedSheetPts, 3, kSheetSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kBBedParts[3] = {
+	{4, kBBedPostPts, 1, kBedPostSurf},
+	{8, kBBedBlanketPts, 4, kBlanketSurf},
+	{8, kBBedSheetPts, 3, kSheetSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kDeskParts[10] = {
+	{4, kDeskTopPts, 1, kDeskTopSurf},
+	{8, kDeskLeftPts, 4, kDeskCabSurf},
+	{8, kDeskRightPts, 4, kDeskCabSurf},
+	{4, kSeatPts, 1, kSeatSurf},
+	{4, kArmLeftPts, 2, kArmSurf},
+	{4, kArmRightPts, 2, kArmSurf},
+	{4, kBackPts, 2, kBackSurf},
+	{8, kComputerPts, 5, kComputerSurf},
+	{8, kMonitorPts, 5, kComputerSurf},
+	{4, kDeskScreenPts, 1, kDeskScreenSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kCChairParts[5] = {
+	{4, kCSeatPts, 1, kCSeatSurf},
+	{4, kCArmLeftPts, 1, kCArmLeftSurf},
+	{4, kCArmRightPts, 1, kCArmRightSurf},
+	{4, kCBackPts, 2, kCBackSurf},
+	{8, kCBasePts, 4, kCBaseSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kConsolePart = {8, kConsolePts, 5, kConsoleSurf};
+static const Colony::ColonyEngine::PrismPartDef kCouchParts[4] = {
+	{8, kACouchPts, 5, kCouchSurf},
+	{8, kBCouchPts, 5, kCouchSurf},
+	{8, kCCouchPts, 5, kCouchSurf},
+	{8, kDCouchPts, 5, kCouchSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kChairParts[4] = {
+	{8, kAChairPts, 5, kCouchSurf},
+	{8, kBChairPts, 5, kCouchSurf},
+	{8, kCChairPts2, 5, kCouchSurf},
+	{8, kDChairPts, 5, kCouchSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kTVParts[2] = {
+	{8, kTVBodyPts, 5, kTVBodySurf},
+	{4, kTVScreenPts, 1, kTVScreenSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kDrawerParts[2] = {
+	{8, kDrawerPts, 5, kDrawerSurf},
+	{4, kMirrorPts, 1, kMirrorSurf}
+};
+static const int kCWallPts[8][3] = {
+	{-128, 128, -160}, {0, 112, -160}, {112, 0, -160}, {128, -128, -160},
+	{-128, 128, 160},  {0, 112, 160},  {112, 0, 160},  {128, -128, 160}
+};
+static const int kCWallSurf[3][8] = {
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kCWallParts[1] = {{8, kCWallPts, 3, kCWallSurf}};
+static const int kPlantPotPts[8][3] = {
+	{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0},
+	{-20, 20, 40}, {20, 20, 40}, {20, -20, 40}, {-20, -20, 40}
+};
+static const int kPlantPotSurf[5][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kPlantStemPts[8][3] = {
+	{-5, 5, 40}, {5, 5, 40}, {5, -5, 40}, {-5, -5, 40},
+	{-5, 5, 120}, {5, 5, 120}, {5, -5, 120}, {-5, -5, 120}
+};
+static const int kPlantStemSurf[4][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kPlantLeaf1Pts[4][3] = {
+	{-40, 0, 120}, {40, 0, 120}, {40, 0, 140}, {-40, 0, 140}
+};
+static const int kPlantLeaf2Pts[4][3] = {
+	{0, 40, 120}, {0, -40, 120}, {0, 40, 140}, {0, -40, 140}
+};
+static const int kPlantLeafSurf[1][8] = {{0, 4, 0, 1, 2, 3, 0, 0}};
+static const Colony::ColonyEngine::PrismPartDef kPlantParts[4] = {
+	{8, kPlantPotPts, 5, kPlantPotSurf},
+	{8, kPlantStemPts, 4, kPlantStemSurf},
+	{4, kPlantLeaf1Pts, 1, kPlantLeafSurf},
+	{4, kPlantLeaf2Pts, 1, kPlantLeafSurf}
+};
+ 
+ 
 void ColonyEngine::rot_init(int x, int y) {
 	_rox = ((long)x * _tsin - (long)y * _tcos) >> 8;
 	_roy = ((long)y * _tsin + (long)x * _tcos) >> 8;
@@ -125,6 +421,11 @@ void ColonyEngine::corridor() {
 	right2 = right;
 	left2 = left;
 
+	if (_gfx->isAccelerated()) {
+		renderCorridor3D();
+		return;
+	}
+
 	xfbehind = _me.xindex + _frntxWall;
 	yfbehind = _me.yindex + _frntyWall;
 	xFrontLeft = xfbehind + _frntx;
@@ -279,12 +580,9 @@ void ColonyEngine::drawend(int xstart, int ystart, int xFrontLeft, int yFrontLef
 		}
 		_gfx->drawLine(_drX[xstart + _frntx][ystart + _frnty], _drY[xstart + _frntx][ystart + _frnty],
 		               _drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft], white);
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
-		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight],
-		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+		
+		drawWall(xFrontLeft, yFrontLeft, xFrontRight, yFrontRight, white);
+
 		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight],
 		               _drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey], white);
 		if (_drY[xstart + _sidex][ystart + _sidey] > 0) {
@@ -292,12 +590,7 @@ void ColonyEngine::drawend(int xstart, int ystart, int xFrontLeft, int yFrontLef
 			               _drX[xstart + _frntx + _sidex][ystart + _frnty + _sidey], _drY[xstart + _frntx + _sidex][ystart + _frnty + _sidey], white);
 		}
 	} else {
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft], white);
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _height - _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight], white);
-		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _height - _drY[xFrontRight][yFrontRight],
-		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
+		drawWall(xFrontLeft, yFrontLeft, xFrontRight, yFrontRight, white);
 	}
 }
 
@@ -307,6 +600,14 @@ uint8 ColonyEngine::wallAt(int x, int y) const {
 	return _wall[x][y];
 }
 
+void ColonyEngine::drawWall(int x1, int y1, int x2, int y2, uint32 color) {
+	_gfx->drawQuad(_drX[x1][y1], _drY[x1][y1],
+	               _drX[x2][y2], _drY[x2][y2],
+	               _drX[x2][y2], _height - _drY[x2][y2],
+	               _drX[x1][y1], _height - _drY[x1][y1],
+	               color);
+}
+
 void ColonyEngine::checkleft(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len) {
 	int i = 0, j;
 	int xf2, yf2;
@@ -329,9 +630,7 @@ void ColonyEngine::checkleft(int xs, int ys, int xf, int yf, int left, int right
 				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
 
 			while ((wallAt(xs, ys) & _side) && i < len && left <= right) {
-				_gfx->drawLine(_drX[xf][yf], _drY[xf][yf], _drX[xf][yf], _height - _drY[xf][yf], white);
-				_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf],
-				               _drX[xf + _frntx][yf + _frnty], _height - _drY[xf + _frntx][yf + _frnty], white);
+				drawWall(xf, yf, xf + _frntx, yf + _frnty, white);
 
 				left = MAX(_drX[xf][yf], left);
 				xf += _frntx;
@@ -347,7 +646,6 @@ void ColonyEngine::checkleft(int xs, int ys, int xf, int yf, int left, int right
 
 			if (_flip)
 				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-			_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf], _drX[xf][yf], _drY[xf][yf], white);
 			left = MAX(_drX[xf][yf], left);
 		}
 
@@ -502,9 +800,7 @@ void ColonyEngine::checkright(int xs, int ys, int xf, int yf, int left, int righ
 				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
 
 			while ((wallAt(xs, ys) & _side) && i < len && left < right) {
-				_gfx->drawLine(_drX[xf][yf], _drY[xf][yf], _drX[xf][yf], _height - _drY[xf][yf], white);
-				_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf],
-				               _drX[xf + _frntx][yf + _frnty], _height - _drY[xf + _frntx][yf + _frnty], white);
+				drawWall(xf, yf, xf + _frntx, yf + _frnty, white);
 
 				right = MIN(_drX[xf][yf], right);
 				xf += _frntx;
@@ -520,7 +816,6 @@ void ColonyEngine::checkright(int xs, int ys, int xf, int yf, int left, int righ
 
 			if (_flip)
 				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-			_gfx->drawLine(_drX[xf][yf], _height - _drY[xf][yf], _drX[xf][yf], _drY[xf][yf], white);
 			right = MIN(_drX[xf][yf], right);
 		}
 
@@ -1455,344 +1750,67 @@ void ColonyEngine::drawProjectedPrism(const ProjectedPrismPart &part, const Pris
 		const int n = def.surfaces[i][1];
 		if (n < 2)
 			continue;
-		int first = def.surfaces[i][2];
-		if (first < 0 || first >= part.pointCount)
-			continue;
-		int prev = first;
-		for (int j = 1; j < n; j++) {
+		
+		int px[ProjectedPrismPart::kMaxPoints];
+		int py[ProjectedPrismPart::kMaxPoints];
+		int count = 0;
+
+		for (int j = 0; j < n; j++) {
 			const int cur = def.surfaces[i][j + 2];
 			if (cur < 0 || cur >= part.pointCount)
 				continue;
-			int x1 = part.x[prev];
-			int y1 = part.y[prev];
-			int x2 = part.x[cur];
-			int y2 = part.y[cur];
-			if (clipLineToRect(x1, y1, x2, y2, clip))
-				_gfx->drawLine(x1, y1, x2, y2, color);
-			prev = cur;
+			px[count] = part.x[cur];
+			py[count] = part.y[cur];
+			count++;
+		}
+		
+		if (count >= 3) {
+			_gfx->drawPolygon(px, py, count, color);
+		} else if (count == 2) {
+			_gfx->drawLine(px[0], py[0], px[1], py[1], color);
 		}
-		int x1 = part.x[prev];
-		int y1 = part.y[prev];
-		int x2 = part.x[first];
-		int y2 = part.y[first];
-		if (clipLineToRect(x1, y1, x2, y2, clip))
-			_gfx->drawLine(x1, y1, x2, y2, color);
 	}
 }
 
-bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
-	// DOS object geometry from SCREEN.H / TABLE.H / BED.H / DESK.H.
-	static const int kScreenPts[8][3] = {
-		{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
-		{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
-	};
-	static const int kScreenSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
-	};
-
-	static const int kTableTopPts[4][3] = {
-		{-128, 128, 100}, {128, 128, 100}, {128, -128, 100}, {-128, -128, 100}
-	};
-	static const int kTableTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kTableBasePts[8][3] = {
-		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-		{-5, 5, 100}, {5, 5, 100}, {5, -5, 100}, {-5, -5, 100}
-	};
-	static const int kTableBaseSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
-	};
-
-	static const int kBedPostPts[4][3] = {
-		{-82, 128, 100}, {82, 128, 100}, {82, 128, 0}, {-82, 128, 0}
-	};
-	static const int kBBedPostPts[4][3] = {
-		{-130, 128, 100}, {130, 128, 100}, {130, 128, 0}, {-130, 128, 0}
-	};
-	static const int kBedPostSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kBlanketSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
-	};
-	static const int kSheetSurf[3][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-		{0, 4, 7, 6, 5, 4, 0, 0}
-	};
-	static const int kBedBlanketPts[8][3] = {
-		{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
-		{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
-	};
-	static const int kBedSheetPts[8][3] = {
-		{-80, 128, 30}, {80, 128, 30}, {80, 70, 30}, {-80, 70, 30},
-		{-80, 128, 60}, {80, 128, 60}, {80, 70, 60}, {-80, 70, 60}
-	};
-	static const int kBBedBlanketPts[8][3] = {
-		{-128, 70, 0}, {128, 70, 0}, {128, -175, 0}, {-128, -175, 0},
-		{-128, 70, 60}, {128, 70, 60}, {128, -175, 60}, {-128, -175, 60}
-	};
-	static const int kBBedSheetPts[8][3] = {
-		{-128, 128, 30}, {128, 128, 30}, {128, 70, 30}, {-128, 70, 30},
-		{-128, 128, 60}, {128, 128, 60}, {128, 70, 60}, {-128, 70, 60}
-	};
-
-	static const int kDeskTopPts[4][3] = {
-		{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
-	};
-	static const int kDeskTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kDeskLeftPts[8][3] = {
-		{-135, 95, 0}, {-55, 95, 0}, {-55, -95, 0}, {-135, -95, 0},
-		{-135, 95, 100}, {-55, 95, 100}, {-55, -95, 100}, {-135, -95, 100}
-	};
-	static const int kDeskRightPts[8][3] = {
-		{55, 95, 0}, {135, 95, 0}, {135, -95, 0}, {55, -95, 0},
-		{55, 95, 100}, {135, 95, 100}, {135, -95, 100}, {55, -95, 100}
-	};
-	static const int kDeskCabSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
-	};
-	static const int kSeatPts[4][3] = {
-		{-40, 210, 60}, {40, 210, 60}, {40, 115, 60}, {-40, 115, 60}
-	};
-	static const int kSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kArmLeftPts[4][3] = {
-		{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
-	};
-	static const int kArmRightPts[4][3] = {
-		{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
-	};
-	static const int kArmSurf[2][8] = {
-		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
-	};
-	static const int kBackPts[4][3] = {
-		{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
-	};
-	static const int kBackSurf[2][8] = {
-		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
-	};
-	static const int kComputerPts[8][3] = {
-		{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
-		{70, 25, 120}, {120, 25, 120}, {120, -25, 120}, {70, -25, 120}
-	};
-	static const int kMonitorPts[8][3] = {
-		{75, 20, 120}, {115, 20, 120}, {115, -20, 120}, {75, -20, 120},
-		{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
-	};
-	static const int kComputerSurf[5][8] = {
-		{0, 4, 7, 6, 5, 4, 0, 0}, {0, 4, 0, 3, 7, 4, 0, 0},
-		{0, 4, 3, 2, 6, 7, 0, 0}, {0, 4, 1, 0, 4, 5, 0, 0},
-		{0, 4, 2, 1, 5, 6, 0, 0}
-	};
-	static const int kDeskScreenPts[4][3] = {
-		{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
-	};
-	static const int kDeskScreenSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-
-	static const int kCSeatPts[4][3] = {
-		{-40, 40, 60}, {40, 40, 60}, {40, -40, 60}, {-40, -40, 60}
-	};
-	static const int kCSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kCArmLeftPts[4][3] = {
-		{-50, 40, 90}, {-40, 40, 60}, {-40, -40, 60}, {-50, -40, 90}
-	};
-	static const int kCArmLeftSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kCArmRightPts[4][3] = {
-		{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
-	};
-	static const int kCArmRightSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
-	static const int kCBackPts[4][3] = {
-		{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
-	};
-	static const int kCBackSurf[2][8] = {
-		{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
-	};
-	static const int kCBasePts[8][3] = {
-		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-		{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
-	};
-	static const int kCBaseSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
-	};
-
-	static const int kConsolePts[8][3] = {
-		{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-		{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
-	};
-	static const int kConsoleSurf[5][8] = {
-		{0, 4, 4, 0, 3, 7, 0, 0}, {0, 4, 7, 3, 2, 6, 0, 0},
-		{0, 4, 5, 1, 0, 4, 0, 0}, {0, 4, 6, 2, 1, 5, 0, 0},
-		{0, 4, 7, 6, 5, 4, 0, 0}
-	};
-
-	static const int kCouchSurf[5][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-		{0, 4, 7, 6, 5, 4, 0, 0}
-	};
-	static const int kACouchPts[8][3] = {
-		{-50, 150, 0}, {50, 150, 0}, {50, -150, 0}, {-50, -150, 0},
-		{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50}
-	};
-	static const int kBCouchPts[8][3] = {
-		{-80, 150, 0}, {-45, 150, 0}, {-45, -150, 0}, {-80, -150, 0},
-		{-80, 150, 120}, {-55, 150, 120}, {-55, -150, 120}, {-80, -150, 120}
-	};
-	static const int kCCouchPts[8][3] = {
-		{-70, 170, 0}, {50, 170, 0}, {50, 150, 0}, {-70, 150, 0},
-		{-70, 170, 80}, {50, 170, 80}, {50, 150, 80}, {-70, 150, 80}
-	};
-	static const int kDCouchPts[8][3] = {
-		{-70, -150, 0}, {50, -150, 0}, {50, -170, 0}, {-70, -170, 0},
-		{-70, -150, 80}, {50, -150, 80}, {50, -170, 80}, {-70, -170, 80}
-	};
-
-	static const int kAChairPts[8][3] = {
-		{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
-		{-50, 50, 50}, {50, 50, 50}, {50, -50, 50}, {-50, -50, 50}
-	};
-	static const int kBChairPts[8][3] = {
-		{-80, 50, 0}, {-45, 50, 0}, {-45, -50, 0}, {-80, -50, 0},
-		{-80, 50, 120}, {-55, 50, 120}, {-55, -50, 120}, {-80, -50, 120}
-	};
-	static const int kCChairPts2[8][3] = {
-		{-70, 70, 0}, {50, 70, 0}, {50, 50, 0}, {-70, 50, 0},
-		{-70, 70, 80}, {50, 70, 80}, {50, 50, 80}, {-70, 50, 80}
-	};
-	static const int kDChairPts[8][3] = {
-		{-70, -50, 0}, {50, -50, 0}, {50, -70, 0}, {-70, -70, 0},
-		{-70, -50, 80}, {50, -50, 80}, {50, -70, 80}, {-70, -70, 80}
-	};
-
-	static const int kTVBodyPts[8][3] = {
-		{-30, 60, 0}, {30, 60, 0}, {30, -60, 0}, {-30, -60, 0},
-		{-30, 60, 120}, {30, 60, 120}, {30, -60, 120}, {-30, -60, 120}
-	};
-	static const int kTVBodySurf[5][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-		{0, 4, 7, 6, 5, 4, 0, 0}
-	};
-	static const int kTVScreenPts[4][3] = {
-		{30, 50, 10}, {30, -50, 10}, {30, 50, 110}, {30, -50, 110}
-	};
-	static const int kTVScreenSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
-
-	static const int kDrawerPts[8][3] = {
-		{-80, 70, 0}, {0, 70, 0}, {0, -70, 0}, {-80, -70, 0},
-		{-80, 70, 100}, {0, 70, 100}, {0, -70, 100}, {-80, -70, 100}
-	};
-	static const int kDrawerSurf[5][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-		{0, 4, 7, 6, 5, 4, 0, 0}
-	};
-	static const int kMirrorPts[4][3] = {
-		{-80, 65, 100}, {-80, -65, 100}, {-80, 65, 210}, {-80, -65, 210}
-	};
-	static const int kMirrorSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
-
-	static const PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
-	static const PrismPartDef kTableParts[2] = {
-		{4, kTableTopPts, 1, kTableTopSurf},
-		{8, kTableBasePts, 4, kTableBaseSurf}
-	};
-	static const PrismPartDef kBedParts[3] = {
-		{4, kBedPostPts, 1, kBedPostSurf},
-		{8, kBedBlanketPts, 4, kBlanketSurf},
-		{8, kBedSheetPts, 3, kSheetSurf}
-	};
-	static const PrismPartDef kBBedParts[3] = {
-		{4, kBBedPostPts, 1, kBedPostSurf},
-		{8, kBBedBlanketPts, 4, kBlanketSurf},
-		{8, kBBedSheetPts, 3, kSheetSurf}
-	};
-	static const PrismPartDef kDeskParts[10] = {
-		{4, kDeskTopPts, 1, kDeskTopSurf},
-		{8, kDeskLeftPts, 4, kDeskCabSurf},
-		{8, kDeskRightPts, 4, kDeskCabSurf},
-		{4, kSeatPts, 1, kSeatSurf},
-		{4, kArmLeftPts, 2, kArmSurf},
-		{4, kArmRightPts, 2, kArmSurf},
-		{4, kBackPts, 2, kBackSurf},
-		{8, kComputerPts, 5, kComputerSurf},
-		{8, kMonitorPts, 5, kComputerSurf},
-		{4, kDeskScreenPts, 1, kDeskScreenSurf}
-	};
-	static const PrismPartDef kCChairParts[5] = {
-		{4, kCSeatPts, 1, kCSeatSurf},
-		{4, kCArmLeftPts, 1, kCArmLeftSurf},
-		{4, kCArmRightPts, 1, kCArmRightSurf},
-		{4, kCBackPts, 2, kCBackSurf},
-		{8, kCBasePts, 4, kCBaseSurf}
-	};
-	static const PrismPartDef kConsolePart = {8, kConsolePts, 5, kConsoleSurf};
-	static const PrismPartDef kCouchParts[4] = {
-		{8, kACouchPts, 5, kCouchSurf},
-		{8, kBCouchPts, 5, kCouchSurf},
-		{8, kCCouchPts, 5, kCouchSurf},
-		{8, kDCouchPts, 5, kCouchSurf}
-	};
-	static const PrismPartDef kChairParts[4] = {
-		{8, kAChairPts, 5, kCouchSurf},
-		{8, kBChairPts, 5, kCouchSurf},
-		{8, kCChairPts2, 5, kCouchSurf},
-		{8, kDChairPts, 5, kCouchSurf}
-	};
-	static const PrismPartDef kTVParts[2] = {
-		{8, kTVBodyPts, 5, kTVBodySurf},
-		{4, kTVScreenPts, 1, kTVScreenSurf}
-	};
-	static const PrismPartDef kDrawerParts[2] = {
-		{8, kDrawerPts, 5, kDrawerSurf},
-		{4, kMirrorPts, 1, kMirrorSurf}
-	};
-
-	/* CWALL (object 49) - ported from DOS INITOBJ.C: simple corner/quarter-wall prism */
-	static const int kCWallPts[8][3] = {
-		{-128, 128, -160}, {0, 112, -160}, {112, 0, -160}, {128, -128, -160},
-		{-128, 128, 160},  {0, 112, 160},  {112, 0, 160},  {128, -128, 160}
-	};
-	static const int kCWallSurf[3][8] = {
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0}
-	};
-	static const PrismPartDef kCWallParts[1] = {{8, kCWallPts, 3, kCWallSurf}};
-
-	/* PLANT (new) - simple pot + stem + two leaf plates (approximates DOS plant) */
-	static const int kPlantPotPts[8][3] = {
-		{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0},
-		{-20, 20, 40}, {20, 20, 40}, {20, -20, 40}, {-20, -20, 40}
-	};
-	static const int kPlantPotSurf[5][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
-	};
+void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, uint32 color) {
+	const uint8 ang = useLook ? obj.where.look : obj.where.ang;
+	const long rotCos = _cost[ang];
+	const long rotSin = _sint[ang];
+ 
+	for (int i = 0; i < def.surfaceCount; i++) {
+		const int n = def.surfaces[i][1];
+		if (n < 2) continue;
 
-	static const int kPlantStemPts[8][3] = {
-		{-5, 5, 40}, {5, 5, 40}, {5, -5, 40}, {-5, -5, 40},
-		{-5, 5, 120}, {5, 5, 120}, {5, -5, 120}, {-5, -5, 120}
-	};
-	static const int kPlantStemSurf[4][8] = {
-		{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-		{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
-	};
+		float px[ProjectedPrismPart::kMaxPoints];
+		float py[ProjectedPrismPart::kMaxPoints];
+		float pz[ProjectedPrismPart::kMaxPoints];
+		int count = 0;
 
-	static const int kPlantLeaf1Pts[4][3] = {
-		{-40, 0, 120}, {40, 0, 120}, {40, 0, 140}, {-40, 0, 140}
-	};
-	static const int kPlantLeaf2Pts[4][3] = {
-		{0, 40, 120}, {0, -40, 120}, {0, 40, 140}, {0, -40, 140}
-	};
-	static const int kPlantLeafSurf[1][8] = {{0, 4, 0, 1, 2, 3, 0, 0}};
+		for (int j = 0; j < n; j++) {
+			const int cur = def.surfaces[i][j + 2];
+			if (cur < 0 || cur >= def.pointCount) continue;
+
+			int ox = def.points[cur][0];
+			int oy = def.points[cur][1];
+			int oz = def.points[cur][2];
+
+			// World relative rotation
+			long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
+			long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+			
+			px[count] = (float)(rx + obj.where.xloc);
+			py[count] = (float)(ry + obj.where.yloc);
+			pz[count] = (float)oz; //(float)(oz - 160); // Floor is at -160
+			count++;
+		}
 
-	static const PrismPartDef kPlantParts[4] = {
-		{8, kPlantPotPts, 5, kPlantPotSurf},
-		{8, kPlantStemPts, 4, kPlantStemSurf},
-		{4, kPlantLeaf1Pts, 1, kPlantLeafSurf},
-		{4, kPlantLeaf2Pts, 1, kPlantLeafSurf}
-	};
+		if (count >= 3) {
+			_gfx->draw3DPolygon(px, py, pz, count, color);
+		}
+	}
+}
 
+bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
 	const int clipLeft = MAX<int>((int)obj.clip.left, (int)_screenR.left);
 	const int clipTop = MAX<int>((int)obj.clip.top, (int)_screenR.top);
 	const int clipRight = MIN<int>((int)obj.clip.right, (int)_screenR.right);
@@ -1804,6 +1822,10 @@ bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
 	auto tint = [](uint32 base, int delta) -> uint32 {
 		return (uint32)CLIP<int>((int)base + delta, 0, 255);
 	};
+ 
+	if (_gfx->isAccelerated()) {
+		return drawStaticObjectPrisms3D(obj, baseColor);
+	}
 
 	ProjectedPrismPart p[10];
 	switch (obj.type) {
@@ -2039,6 +2061,17 @@ void ColonyEngine::drawStaticObjectFallback(const Thing &obj, uint32 color, int
 }
 
 void ColonyEngine::drawStaticObjects() {
+	if (_gfx->isAccelerated()) {
+		for (uint i = 0; i < _objects.size(); i++) {
+			const Thing &obj = _objects[i];
+			if (!obj.alive)
+				continue;
+			uint32 color = objectColor(obj.type);
+			drawStaticObjectPrisms3D(obj, color);
+		}
+		return;
+	}
+
 	struct DrawCmd {
 		int depth;
 		int index;
@@ -2100,5 +2133,103 @@ void ColonyEngine::setRobot(int l, int r, int num) {
 	obj.clip.top = _clip.top;
 	obj.clip.bottom = _clip.bottom;
 }
+ 
+// DOS object geometry constants
+
+
+void ColonyEngine::renderCorridor3D() {
+	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
+ 
+	// Draw large grid/outline for floor and ceiling if needed, but for wireframe match, we can just clear to black
+	// We still draw a large black floor quad to ensure depth testing works against "empty ground"
+	_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.1f, 
+	                100000.0f, -100000.0f, -160.1f, 
+	                100000.0f, 100000.0f, -160.1f, 
+	                -100000.0f, 100000.0f, -160.1f, 0); // Black floor
+ 
+	_gfx->draw3DQuad(-100000.0f, -100000.0f, 160.1f, 
+	                100000.0f, -100000.0f, 160.1f, 
+	                100000.0f, 100000.0f, 160.1f, 
+	                -100000.0f, 100000.0f, 160.1f, 0); // Black ceiling
+ 
+	uint32 wallColor = 15; // White
+ 
+	for (int y = 0; y < 32; y++) {
+		for (int x = 0; x < 32; x++) {
+			uint8 w = _wall[x][y];
+			if (w & 0x01) {
+				_gfx->draw3DWall(x, y, x + 1, y, wallColor);
+			}
+			if (w & 0x02) {
+				_gfx->draw3DWall(x, y, x, y + 1, wallColor);
+			}
+		}
+	}
+	
+	drawStaticObjects();
+		
+	_gfx->end3D();
+}
+
+bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj, uint32 baseColor) {
+	auto tint = [](uint32 base, int delta) -> uint32 {
+		return (uint32)CLIP<int>((int)base + delta, 0, 255);
+	};
+
+	switch (obj.type) {
+	case kObjConsole:
+		draw3DPrism(obj, kConsolePart, false, tint(baseColor, 0));
+		break;
+	case kObjCChair:
+		for (int i = 0; i < 5; i++)
+			draw3DPrism(obj, kCChairParts[i], false, tint(baseColor, 0));
+		break;
+	case kObjPlant:
+		for (int i = 0; i < 4; i++)
+			draw3DPrism(obj, kPlantParts[i], false, tint(baseColor, 0));
+		break;
+	case kObjCouch:
+	case kObjChair: {
+		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
+		for (int i = 0; i < 4; i++)
+			draw3DPrism(obj, parts[i], false, tint(baseColor, 0));
+		break;
+	}
+	case kObjTV:
+		draw3DPrism(obj, kTVParts[0], false, tint(baseColor, 0));
+		draw3DPrism(obj, kTVParts[1], false, tint(baseColor, 35));
+		break;
+	case kObjDrawer:
+		draw3DPrism(obj, kDrawerParts[0], false, tint(baseColor, 0));
+		draw3DPrism(obj, kDrawerParts[1], false, tint(baseColor, 30));
+		break;
+	case kObjFWall:
+	case kObjCWall:
+		draw3DPrism(obj, kCWallParts[0], false, tint(baseColor, -5));
+		break;
+	case kObjScreen:
+		draw3DPrism(obj, kScreenPart, false, tint(baseColor, 0));
+		break;
+	case kObjTable:
+		draw3DPrism(obj, kTableParts[0], false, tint(baseColor, 20));
+		draw3DPrism(obj, kTableParts[1], false, tint(baseColor, -10));
+		break;
+	case kObjBed:
+	case kObjBBed: {
+		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
+		draw3DPrism(obj, parts[0], false, tint(baseColor, 15));
+		draw3DPrism(obj, parts[1], false, tint(baseColor, -10));
+		draw3DPrism(obj, parts[2], false, tint(baseColor, 5));
+		break;
+	}
+	case kObjDesk:
+		for (int i = 0; i < 10; i++)
+			draw3DPrism(obj, kDeskParts[i], false, tint(baseColor, 0));
+		break;
+	default:
+		return false;
+	}
+	return true;
+}
 
 } // End of namespace Colony
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
new file mode 100644
index 00000000000..dc70410315f
--- /dev/null
+++ b/engines/colony/renderer.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef COLONY_RENDERER_H
+#define COLONY_RENDERER_H
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+#include "graphics/managed_surface.h"
+#include "graphics/font.h"
+
+namespace Colony {
+
+class Renderer {
+public:
+	virtual ~Renderer() {}
+
+	virtual void clear(uint32 color) = 0;
+	virtual void drawLine(int x1, int y1, int x2, int y2, uint32 color) = 0;
+	virtual void drawRect(const Common::Rect &rect, uint32 color) = 0;
+	virtual void fillRect(const Common::Rect &rect, uint32 color) = 0;
+	
+	virtual void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) = 0;
+	virtual void scroll(int dx, int dy, uint32 background) = 0;
+	virtual bool isAccelerated() const = 0;
+	virtual void drawEllipse(int x, int y, int rx, int ry, uint32 color) = 0;
+	virtual void fillEllipse(int x, int y, int rx, int ry, uint32 color) = 0;
+	virtual void fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) = 0;
+	virtual void setPixel(int x, int y, uint32 color) = 0;
+
+	virtual void setPalette(const byte *palette, uint start, uint count) = 0;
+
+	virtual void begin3D(int camX, int camY, int camZ, int angle, int angleY, const Common::Rect &viewport) = 0;
+	virtual void draw3DWall(int x1, int y1, int x2, int y2, uint32 color) = 0;
+	virtual void draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) = 0;
+	virtual void draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) = 0;
+	virtual void end3D() = 0;
+
+	virtual void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) = 0;
+	virtual void drawPolygon(const int *x, const int *y, int count, uint32 color) = 0;
+
+	virtual void copyToScreen() = 0;
+	virtual void setWireframe(bool enable) = 0;
+};
+
+Renderer *createSoftwareRenderer(OSystem *system, int width, int height);
+Renderer *createOpenGLRenderer(OSystem *system, int width, int height);
+
+} // End of namespace Colony
+
+#endif
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
new file mode 100644
index 00000000000..3fa7a338f35
--- /dev/null
+++ b/engines/colony/renderer_opengl.cpp
@@ -0,0 +1,351 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/system.h"
+#include "graphics/surface.h"
+#include "graphics/font.h"
+#include <math.h>
+#include "colony/renderer.h"
+
+#ifdef USE_OPENGL_GAME
+#include "graphics/opengl/system_headers.h"
+#include "graphics/paletteman.h"
+
+namespace Colony {
+
+class OpenGLRenderer : public Renderer {
+public:
+	OpenGLRenderer(OSystem *system, int width, int height);
+	~OpenGLRenderer();
+
+	void clear(uint32 color) override;
+	void drawLine(int x1, int y1, int x2, int y2, uint32 color) override;
+	void drawRect(const Common::Rect &rect, uint32 color) override;
+	void fillRect(const Common::Rect &rect, uint32 color) override;
+	void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) override;
+	void scroll(int dx, int dy, uint32 background) override;
+	bool isAccelerated() const override { return true; }
+	void drawEllipse(int x, int y, int rx, int ry, uint32 color) override;
+	void fillEllipse(int x, int y, int rx, int ry, uint32 color) override;
+	void fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) override;
+	void setPixel(int x, int y, uint32 color) override;
+	void setPalette(const byte *palette, uint start, uint count) override;
+
+	void begin3D(int camX, int camY, int camZ, int angle, int angleY, const Common::Rect &viewport) override;
+	void draw3DWall(int x1, int y1, int x2, int y2, uint32 color) override;
+	void draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) override;
+	void draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) override;
+	void end3D() override;
+
+	void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) override;
+	void drawPolygon(const int *x, const int *y, int count, uint32 color) override;
+	void copyToScreen() override;
+	void setWireframe(bool enable) override { _wireframe = enable; }
+
+private:
+	void useColor(uint32 color);
+
+	OSystem *_system;
+	int _width;
+	int _height;
+	byte _palette[256 * 3];
+	bool _wireframe;
+};
+
+OpenGLRenderer::OpenGLRenderer(OSystem *system, int width, int height) : _system(system), _width(width), _height(height) {
+	_wireframe = true;
+	memset(_palette, 0, sizeof(_palette));
+	
+	// Default to white for initial colors if setPalette isn't called yet
+	for (int i = 0; i < 256 * 3; i++) _palette[i] = 255;
+
+	glDisable(GL_LIGHTING);
+	glDisable(GL_TEXTURE_2D);
+	glDisable(GL_DEPTH_TEST);
+	glDisable(GL_CULL_FACE);
+	glDisable(GL_BLEND);
+	
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(0, _width, _height, 0, -1, 1);
+	
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+
+	glViewport(0, 0, _system->getWidth(), _system->getHeight());
+	
+	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+	glClear(GL_COLOR_BUFFER_BIT);
+}
+
+OpenGLRenderer::~OpenGLRenderer() {
+}
+
+void OpenGLRenderer::setPalette(const byte *palette, uint start, uint count) {
+	if (start + count > 256) count = 256 - start;
+	memcpy(_palette + start * 3, palette, count * 3);
+}
+
+void OpenGLRenderer::useColor(uint32 color) {
+	uint32 index = color & 0xFF;
+	glColor3ub(_palette[index * 3], _palette[index * 3 + 1], _palette[index * 3 + 2]);
+}
+
+void OpenGLRenderer::clear(uint32 color) {
+	uint32 index = color & 0xFF;
+	glClearColor(_palette[index * 3] / 255.0f, _palette[index * 3 + 1] / 255.0f, _palette[index * 3 + 2] / 255.0f, 1.0f);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+void OpenGLRenderer::drawLine(int x1, int y1, int x2, int y2, uint32 color) {
+	useColor(color);
+	glBegin(GL_LINES);
+	glVertex2i(x1, y1);
+	glVertex2i(x2, y2);
+	glEnd();
+}
+
+void OpenGLRenderer::drawRect(const Common::Rect &rect, uint32 color) {
+	useColor(color);
+	glBegin(GL_LINE_LOOP);
+	glVertex2i(rect.left, rect.top);
+	glVertex2i(rect.right, rect.top);
+	glVertex2i(rect.right, rect.bottom);
+	glVertex2i(rect.left, rect.bottom);
+	glEnd();
+}
+
+void OpenGLRenderer::fillRect(const Common::Rect &rect, uint32 color) {
+	useColor(color);
+	glBegin(GL_QUADS);
+	glVertex2i(rect.left, rect.top);
+	glVertex2i(rect.right, rect.top);
+	glVertex2i(rect.right, rect.bottom);
+	glVertex2i(rect.left, rect.bottom);
+	glEnd();
+}
+
+void OpenGLRenderer::drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) {
+	if (!font) return;
+	int width = font->getStringWidth(str);
+	int height = font->getFontHeight();
+	if (align == Graphics::kTextAlignCenter) x -= width / 2;
+	else if (align == Graphics::kTextAlignRight) x -= width;
+	
+	Graphics::Surface surface;
+	surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+	memset(surface.getPixels(), 0, width * height);
+	font->drawString(&surface, str, 0, 0, width, 1, Graphics::kTextAlignLeft);
+	
+	for (int py = 0; py < height; py++) {
+		for (int px = 0; px < width; px++) {
+			if (*((byte *)surface.getBasePtr(px, py)) == 1) {
+				setPixel(x + px, y + py, color);
+			}
+		}
+	}
+	surface.free();
+}
+ 
+void OpenGLRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY, const Common::Rect &viewport) {
+	glEnable(GL_DEPTH_TEST);
+	glClear(GL_DEPTH_BUFFER_BIT);
+ 
+	glPolygonMode(GL_FRONT_AND_BACK, _wireframe ? GL_LINE : GL_FILL);
+ 
+	// Scale viewport coordinates to system pixels
+	float scaleX = (float)_system->getWidth() / _width;
+	float scaleY = (float)_system->getHeight() / _height;
+	int sysH = _system->getHeight();
+ 
+	int vpX = (int)(viewport.left * scaleX);
+	int vpY = sysH - (int)(viewport.bottom * scaleY);
+	int vpW = (int)(viewport.width() * scaleX);
+	int vpH = (int)(viewport.height() * scaleY);
+ 
+	glViewport(vpX, vpY, vpW, vpH);
+	glScissor(vpX, vpY, vpW, vpH);
+	glEnable(GL_SCISSOR_TEST);
+ 
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	
+	// Optical center is at viewport.width()/2 and viewport.height()/2
+	// xmax = (width/2) / 256.0
+	float xmax = (viewport.width() / 2.0f) / 256.0f;
+	float ymax = (viewport.height() / 2.0f) / 256.0f;
+	glFrustum(-xmax, xmax, -ymax, ymax, 1.0, 20000.0);
+ 
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+  
+	// 1. Vertical look (Pitch) - around camera X axis
+	glRotatef((float)angleY * 360.0f / 256.0f, 1.0f, 0.0f, 0.0f);
+ 
+	// 2. Map world to camera orientation (-90 deg X)
+	glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
+ 
+	// 3. Horizontal rotation (Yaw) around World Z axis (Up)
+	glRotatef(-(float)angle * 360.0f / 256.0f, 0.0f, 0.0f, 1.0f);
+	
+	// 4. Translate camera
+	glTranslatef(-(float)camX, -(float)camY, -(float)camZ);
+}
+ 
+void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
+	float fx1 = x1 * 256.0f;
+	float fy1 = y1 * 256.0f;
+	float fx2 = x2 * 256.0f;
+	float fy2 = y2 * 256.0f;
+ 
+	useColor(color);
+	glBegin(GL_QUADS);
+	glVertex3f(fx1, fy1, -160.0f);
+	glVertex3f(fx2, fy2, -160.0f);
+	glVertex3f(fx2, fy2, 160.0f);
+	glVertex3f(fx1, fy1, 160.0f);
+	glEnd();
+}
+ 
+void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) {
+	useColor(color);
+	glBegin(GL_QUADS);
+	glVertex3f(x1, y1, z1);
+	glVertex3f(x2, y2, z2);
+	glVertex3f(x3, y3, z3);
+	glVertex3f(x4, y4, z4);
+	glEnd();
+}
+ 
+void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) {
+	if (count < 3) return;
+ 
+	useColor(color);
+	glBegin(GL_POLYGON);
+	for (int i = 0; i < count; i++) {
+		glVertex3f(x[i], y[i], z[i]);
+	}
+	glEnd();
+}
+ 
+void OpenGLRenderer::end3D() {
+	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+	glDisable(GL_DEPTH_TEST);
+	glDisable(GL_SCISSOR_TEST);
+	
+	glViewport(0, 0, _system->getWidth(), _system->getHeight());
+	glScissor(0, 0, _system->getWidth(), _system->getHeight());
+ 
+	// Reset to 2D
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(0, _width, _height, 0, -1, 1);
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+}
+
+void OpenGLRenderer::scroll(int dx, int dy, uint32 background) {
+}
+
+void OpenGLRenderer::drawEllipse(int x, int y, int rx, int ry, uint32 color) {
+	useColor(color);
+	glBegin(GL_LINE_LOOP);
+	for (int i = 0; i < 360; i += 10) {
+		float rad = i * M_PI / 180.0f;
+		glVertex2f(x + cos(rad) * (float)rx, y + sin(rad) * (float)ry);
+	}
+	glEnd();
+}
+
+void OpenGLRenderer::fillEllipse(int x, int y, int rx, int ry, uint32 color) {
+	useColor(color);
+	glBegin(GL_POLYGON);
+	for (int i = 0; i < 360; i += 10) {
+		float rad = i * M_PI / 180.0f;
+		glVertex2f(x + cos(rad) * (float)rx, y + sin(rad) * (float)ry);
+	}
+	glEnd();
+}
+
+void OpenGLRenderer::fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) {
+	fillRect(rect, color1);
+}
+
+void OpenGLRenderer::setPixel(int x, int y, uint32 color) {
+	useColor(color);
+	glBegin(GL_POINTS);
+	glVertex2i(x, y);
+	glEnd();
+}
+
+void OpenGLRenderer::drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) {
+	useColor(color);
+	glBegin(GL_QUADS);
+	glVertex2i(x1, y1);
+	glVertex2i(x2, y2);
+	glVertex2i(x3, y3);
+	glVertex2i(x4, y4);
+	glEnd();
+	
+	glColor3ub(255, 255, 255);
+	glBegin(GL_LINE_LOOP);
+	glVertex2i(x1, y1);
+	glVertex2i(x2, y2);
+	glVertex2i(x3, y3);
+	glVertex2i(x4, y4);
+	glEnd();
+}
+
+void OpenGLRenderer::drawPolygon(const int *x, const int *y, int count, uint32 color) {
+	if (count < 3) return;
+	useColor(color);
+	glBegin(GL_POLYGON);
+	for (int i = 0; i < count; i++) {
+		glVertex2i(x[i], y[i]);
+	}
+	glEnd();
+	
+	glColor3ub(255, 255, 255);
+	glBegin(GL_LINE_LOOP);
+	for (int i = 0; i < count; i++) {
+		glVertex2i(x[i], y[i]);
+	}
+	glEnd();
+}
+
+void OpenGLRenderer::copyToScreen() {
+	glFlush();
+	_system->updateScreen();
+}
+
+Renderer *createOpenGLRenderer(OSystem *system, int width, int height) {
+	return new OpenGLRenderer(system, width, height);
+}
+
+} // End of namespace Colony
+
+#else
+
+namespace Colony {
+Renderer *createOpenGLRenderer(OSystem *system, int width, int height) { return nullptr; }
+}
+
+#endif
diff --git a/engines/colony/renderer_software.cpp b/engines/colony/renderer_software.cpp
new file mode 100644
index 00000000000..cd529d153bb
--- /dev/null
+++ b/engines/colony/renderer_software.cpp
@@ -0,0 +1,160 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/system.h"
+#include "graphics/managed_surface.h"
+#include "colony/renderer.h"
+
+namespace Colony {
+
+class SoftwareRenderer : public Renderer {
+public:
+	SoftwareRenderer(OSystem *system, int width, int height);
+	~SoftwareRenderer();
+
+	void clear(uint32 color) override;
+	void drawLine(int x1, int y1, int x2, int y2, uint32 color) override;
+	void drawRect(const Common::Rect &rect, uint32 color) override;
+	void fillRect(const Common::Rect &rect, uint32 color) override;
+	void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) override;
+	void scroll(int dx, int dy, uint32 background) override;
+	bool isAccelerated() const override { return false; }
+	void drawEllipse(int x, int y, int rx, int ry, uint32 color) override;
+	void fillEllipse(int x, int y, int rx, int ry, uint32 color) override;
+	void fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) override;
+	void setPixel(int x, int y, uint32 color) override;
+	void setPalette(const byte *palette, uint start, uint count) override;
+
+	void begin3D(int camX, int camY, int camZ, int angle, int angleY, const Common::Rect &viewport) override {}
+	void draw3DWall(int x1, int y1, int x2, int y2, uint32 color) override {}
+	void draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) override {}
+	void draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) override {}
+	void end3D() override {}
+
+	void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) override;
+	void drawPolygon(const int *x, const int *y, int count, uint32 color) override;
+	void copyToScreen() override;
+	void setWireframe(bool enable) override {}
+
+private:
+	OSystem *_system;
+	Graphics::ManagedSurface _surface;
+	int _width;
+	int _height;
+};
+
+SoftwareRenderer::SoftwareRenderer(OSystem *system, int width, int height) : _system(system), _width(width), _height(height) {
+	_surface.create(width, height, _system->getScreenFormat());
+}
+
+SoftwareRenderer::~SoftwareRenderer() {
+	_surface.free();
+}
+
+void SoftwareRenderer::clear(uint32 color) {
+	_surface.clear(color);
+}
+
+void SoftwareRenderer::drawLine(int x1, int y1, int x2, int y2, uint32 color) {
+	_surface.drawLine(x1, y1, x2, y2, color);
+}
+
+void SoftwareRenderer::drawRect(const Common::Rect &rect, uint32 color) {
+	_surface.frameRect(rect, color);
+}
+
+void SoftwareRenderer::fillRect(const Common::Rect &rect, uint32 color) {
+	_surface.fillRect(rect, color);
+}
+
+void SoftwareRenderer::drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) {
+	if (!font) return;
+	font->drawString(&_surface, str, x, y, (align == Graphics::kTextAlignCenter && x == 0) ? _width : (_width - x), color, align);
+}
+
+void SoftwareRenderer::scroll(int dx, int dy, uint32 background) {
+	if (abs(dx) >= _width || abs(dy) >= _height) {
+		clear(background);
+		return;
+	}
+
+	Graphics::ManagedSurface tmp;
+	tmp.create(_width, _height, _surface.format);
+	tmp.blitFrom(_surface);
+
+	clear(background);
+	_surface.blitFrom(tmp, Common::Rect(0, 0, _width, _height), Common::Rect(dx, dy, dx + _width, dy + _height));
+	tmp.free();
+}
+
+void SoftwareRenderer::drawEllipse(int x, int y, int rx, int ry, uint32 color) {
+	_surface.drawEllipse(x - rx, y - ry, x + rx, y + ry, color, false);
+}
+
+void SoftwareRenderer::fillEllipse(int x, int y, int rx, int ry, uint32 color) {
+	_surface.drawEllipse(x - rx, y - ry, x + rx, y + ry, color, true);
+}
+
+void SoftwareRenderer::fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) {
+	for (int y = rect.top; y < rect.bottom; y++) {
+		for (int x = rect.left; x < rect.right; x++) {
+			if ((x + y) % 2 == 0)
+				_surface.setPixel(x, y, color1);
+			else
+				_surface.setPixel(x, y, color2);
+		}
+	}
+}
+
+void SoftwareRenderer::setPixel(int x, int y, uint32 color) {
+	if (x >= 0 && x < _width && y >= 0 && y < _height)
+		_surface.setPixel(x, y, color);
+}
+
+void SoftwareRenderer::setPalette(const byte *palette, uint start, uint count) {
+	// Software renderer doesn't need to cache palette, ManagedSurface uses it from system if 8bpp
+}
+
+void SoftwareRenderer::drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) {
+	_surface.drawLine(x1, y1, x2, y2, color);
+	_surface.drawLine(x2, y2, x3, y3, color);
+	_surface.drawLine(x3, y3, x4, y4, color);
+	_surface.drawLine(x4, y4, x1, y1, color);
+}
+
+void SoftwareRenderer::drawPolygon(const int *x, const int *y, int count, uint32 color) {
+	if (count < 2) return;
+	for (int i = 0; i < count; i++) {
+		int next = (i + 1) % count;
+		_surface.drawLine(x[i], y[i], x[next], y[next], color);
+	}
+}
+
+void SoftwareRenderer::copyToScreen() {
+	_system->copyRectToScreen(_surface.getPixels(), _surface.pitch, 0, 0, _surface.w, _surface.h);
+	_system->updateScreen();
+}
+
+Renderer *createSoftwareRenderer(OSystem *system, int width, int height) {
+	return new SoftwareRenderer(system, width, height);
+}
+
+} // End of namespace Colony


Commit: 071d5cc88c01866b256e0eefd1d7bab39bba66e3
    https://github.com/scummvm/scummvm/commit/071d5cc88c01866b256e0eefd1d7bab39bba66e3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:07+02:00

Commit Message:
COLONY: removed most of the old 2d code

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/gfx.cpp
    engines/colony/gfx.h
    engines/colony/module.mk
    engines/colony/render.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index a76f7e05e84..3dca1d436a4 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -82,9 +82,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_height = 480;
 	_centerX = _width / 2;
 	_centerY = _height / 2;
-	_flip = false;
 	_mouseSensitivity = 1;
-	_change = true;
 	_showDashBoard = true;
 	_crosshair = true;
 	_insight = false;
@@ -206,21 +204,13 @@ void ColonyEngine::initTrig() {
 		_sint[i] = g_sintTable[i];
 		_cost[i] = g_sintTable[(i + 64) & 0xFF];
 	}
-
-	_rtable[0] = 32000;
-	for (int i = 1; i < 11585; i++) {
-		_rtable[i] = (160 * 128) / i;
-	}
 }
 
 Common::Error ColonyEngine::run() {
-	Common::String renderer = "opengl"; //ConfMan.get("renderer");
-	if (renderer == "software") {
-		Graphics::PixelFormat format8bpp = Graphics::PixelFormat::createFormatCLUT8();
-		initGraphics(_width, _height, &format8bpp);
-	} else {
-		initGraphics3d(_width, _height);
-	}
+	// Create the renderer (follows Freescape pattern: always uses OpenGL)
+	_gfx = createRenderer(_system, _width, _height);
+	if (!_gfx)
+		return Common::kUserCanceled;
 
 	_width = _system->getWidth();
 	_height = _system->getHeight();
@@ -246,10 +236,6 @@ Common::Error ColonyEngine::run() {
 		pal[i * 3 + 1] = i;
 		pal[i * 3 + 2] = i;
 	}
-	if (renderer == "software") {
-		_system->getPaletteManager()->setPalette(pal, 0, 256);
-	}
-	_gfx = new Gfx(_system, _width, _height);
 	_gfx->setPalette(pal, 0, 256);
 	_gfx->setWireframe(_wireframe);
 	
@@ -282,16 +268,13 @@ Common::Error ColonyEngine::run() {
 					}
 				case Common::KEYCODE_LEFT:
 					_me.look = (uint8)((int)_me.look + 8);
-					_change = true;
 					break;
 					case Common::KEYCODE_RIGHT:
 						_me.look = (uint8)((int)_me.look - 8);
-						_change = true;
 						break;
 					case Common::KEYCODE_F7:
 						_wireframe = !_wireframe;
 						_gfx->setWireframe(_wireframe);
-						_change = true;
 						break;
 					default:
 						break;
@@ -299,29 +282,18 @@ Common::Error ColonyEngine::run() {
 				debug("Me: x=%d y=%d look=%d", _me.xloc, _me.yloc, _me.look);
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
 				if (event.relMouse.x != 0) {
-					// Subtract because increasing look turns left, but mouse right is positive rel X
-					// Reduced sensitivity by half
 					_me.look = (uint8)((int)_me.look - (event.relMouse.x / 2));
-					_change = true;
 				}
 				if (event.relMouse.y != 0) {
-					// Add/Subtract based on standard vertical look (pull back to look up)
-					// Mouse down is positive rel Y.
 					_me.lookY = (int8)CLIP<int>((int)_me.lookY - (event.relMouse.y / 2), -64, 64);
-					_change = true;
 				}
 			}
 		}
 		_system->warpMouse(_centerX, _centerY);
 
 		_gfx->clear(_gfx->black());
-		for (uint i = 0; i < _objects.size(); i++)
-			_objects[i].visible = 0;
 		
 		corridor();
-		if (renderer == "software") {
-			drawStaticObjects();
-		}
 		drawDashboardStep1();
 		drawCrosshair();
 		
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index b168e046050..48ad2ee4210 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -175,8 +175,6 @@ public:
 	void loadMap(int mnum);
 	void corridor();
 	void quadrant();
-	void perspective(int pnt[2], int rox, int roy);
-	void rot_init(int x, int y);
 	int checkwall(int xnew, int ynew, Locate *pobject);
 	void cCommand(int xnew, int ynew, bool allowInteraction);
 	void scrollInfo();
@@ -194,18 +192,14 @@ private:
 	int _level;
 	int _robotNum;
 
-	Gfx *_gfx;
-	
-	int _rox, _roy;
+	Renderer *_gfx;
+
 	int _tsin, _tcos;
 	int _sint[256];
 	int _cost[256];
-	int _rtable[11585];
 	int _centerX, _centerY;
 	int _width, _height;
-	bool _flip;
 	int _mouseSensitivity;
-	bool _change;
 	bool _showDashBoard;
 	bool _crosshair;
 	bool _insight;
@@ -221,9 +215,6 @@ private:
 	int _front, _side;
 	int _direction;
 
-	int _drX[34][34];
-	int _drY[34][34];
-
 	Common::Rect _clip;
 	Common::Rect _screenR;
 	Common::Rect _dashBoardRect;
@@ -231,44 +222,11 @@ private:
 	Common::Rect _headsUpRect;
 	Common::Rect _powerRect;
 
-	void drawend(int xstart, int ystart, int xFrontLeft, int yFrontLeft);
-	void checkleft(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len);
-	void checkright(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len);
 	uint8 wallAt(int x, int y) const;
 	const uint8 *mapFeatureAt(int x, int y, int direction) const;
-	void frontfeature(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry);
-	void features(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry);
-	void dowall(int cellx, int celly, int direction, int left[4], int right[4]);
-	void drawWindow(int left[4], int right[4]);
-	void drawClosedDoor(int left[4], int right[4]);
-	void drawOpenDoor(int left[4], int right[4], int left2[2], int right2[2]);
-	void drawTunnel(int left[4], int right[4], int left2[2], int right2[2]);
-	void drawGlyphs(int left[4], int right[4]);
-	void drawBooks(int left[4], int right[4], int left2[2], int right2[2]);
-	void drawUpStairs(int left[4], int right[4], int left2[2], int right2[2]);
-	void drawDnStairs(int left[4], int right[4], int left2[2], int right2[2]);
-	void drawALOpen(int left[4], int right[4]);
-	void drawALClosed(int left[4], int right[4]);
-	void drawOpenSSDoor(int left[4], int right[4]);
-	void drawClosedSSDoor(int left[4], int right[4]);
-	void drawElevator(int left[4], int right[4]);
-	void drawColor(const uint8 *map, int left[4], int right[4]);
-	void split7(int arr[7], int x1, int x2) const;
-	void split7x7(int left[4], int right[4], int lr[7], int ud[7][7]) const;
 	void drawStaticObjects();
-	bool projectWorld(int worldX, int worldY, int &screenX, int &depth) const;
 	uint32 objectColor(int type) const;
-	void setRobot(int l, int r, int num);
-	struct ProjectedPrismPart {
-		static const int kMaxPoints = 8;
-		static const int kMaxSurfaces = 8;
-		int pointCount;
-		int x[kMaxPoints];
-		int y[kMaxPoints];
-		int depth[kMaxPoints];
-		bool vsurface[kMaxSurfaces];
-		bool visible;
-	};
+
 public:
 	struct PrismPartDef {
 		int pointCount;
@@ -276,21 +234,12 @@ public:
 		int surfaceCount;
 		const int (*surfaces)[8];
 	};
- 
+
 private:
-	bool projectPrismPart(const Thing &obj, const PrismPartDef &part, bool useLook, ProjectedPrismPart &out) const;
-	bool isSurfaceClockwise(const ProjectedPrismPart &part, const int surface[8]) const;
-	bool clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const;
-	void drawProjectedPrism(const ProjectedPrismPart &part, const PrismPartDef &def, int force, uint32 color, const Common::Rect &clip);
 	void draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, uint32 color);
-	bool drawStaticObjectPrisms(const Thing &obj, uint32 baseColor);
 	bool drawStaticObjectPrisms3D(const Thing &obj, uint32 baseColor);
-
-	void drawWall(int x1, int y1, int x2, int y2, uint32 color);
 	void renderCorridor3D();
 
-	// Visibility / Clipping
-	void drawStaticObjectFallback(const Thing &obj, uint32 color, int depth, int sx);
 	int occupiedObjectAt(int x, int y, const Locate *pobject);
 	void interactWithObject(int objNum);
 	bool setDoorState(int x, int y, int direction, int state);
@@ -299,6 +248,7 @@ private:
 	void updateViewportLayout();
 	void drawDashboardStep1();
 	void drawCrosshair();
+	bool clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const;
 
 	// Animation system
 	Common::Array<Sprite *> _cSprites;
diff --git a/engines/colony/gfx.cpp b/engines/colony/gfx.cpp
index 2fb748c0ba0..0f0e524b255 100644
--- a/engines/colony/gfx.cpp
+++ b/engines/colony/gfx.cpp
@@ -21,21 +21,18 @@
 
 #include "common/system.h"
 #include "common/config-manager.h"
-#include "colony/gfx.h"
+#include "engines/util.h"
+#include "colony/renderer.h"
 
 namespace Colony {
 
-Gfx::Gfx(OSystem *system, int width, int height) {
-	Common::String renderer = ConfMan.get("renderer");
-	if (renderer == "software") {
-		_renderer = createSoftwareRenderer(system, width, height);
-	} else {
-		_renderer = createOpenGLRenderer(system, width, height);
-	}
-}
+// Forward declaration for the OpenGL renderer factory
+Renderer *createOpenGLRenderer(OSystem *system, int width, int height);
 
-Gfx::~Gfx() {
-	delete _renderer;
+Renderer *createRenderer(OSystem *system, int width, int height) {
+	// Always use OpenGL (following Freescape's pattern for accelerated renderers)
+	initGraphics3d(width, height);
+	return createOpenGLRenderer(system, width, height);
 }
 
 } // End of namespace Colony
diff --git a/engines/colony/gfx.h b/engines/colony/gfx.h
index 50dec546425..d1291cf5018 100644
--- a/engines/colony/gfx.h
+++ b/engines/colony/gfx.h
@@ -22,59 +22,8 @@
 #ifndef COLONY_GFX_H
 #define COLONY_GFX_H
 
+// The Colony engine uses Renderer directly (like Freescape).
+// This header is kept for source compatibility with files that #include "colony/gfx.h".
 #include "colony/renderer.h"
 
-namespace Colony {
-
-class Gfx {
-public:
-	Gfx(OSystem *system, int width, int height);
-	~Gfx();
-
-	void clear(uint32 color) { _renderer->clear(color); }
-	void drawLine(int x1, int y1, int x2, int y2, uint32 color) { _renderer->drawLine(x1, y1, x2, y2, color); }
-	void drawRect(const Common::Rect &rect, uint32 color) { _renderer->drawRect(rect, color); }
-	void fillRect(const Common::Rect &rect, uint32 color) { _renderer->fillRect(rect, color); }
-	
-	void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align = Graphics::kTextAlignLeft) {
-		_renderer->drawString(font, str, x, y, color, align);
-	}
-	void scroll(int dx, int dy, uint32 background) { _renderer->scroll(dx, dy, background); }
-
-	void drawEllipse(int x, int y, int rx, int ry, uint32 color) { _renderer->drawEllipse(x, y, rx, ry, color); }
-	void fillEllipse(int x, int y, int rx, int ry, uint32 color) { _renderer->fillEllipse(x, y, rx, ry, color); }
-	void fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) { _renderer->fillDitherRect(rect, color1, color2); }
-	void setPixel(int x, int y, uint32 color) { _renderer->setPixel(x, y, color); }
-	void setPalette(const byte *palette, uint start, uint count) { _renderer->setPalette(palette, start, count); }
- 
-	void begin3D(int camX, int camY, int camZ, int angle, int angleY, const Common::Rect &viewport) { _renderer->begin3D(camX, camY, camZ, angle, angleY, viewport); }
-	void draw3DWall(int x1, int y1, int x2, int y2, uint32 color) { _renderer->draw3DWall(x1, y1, x2, y2, color); }
-	void draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) {
-		_renderer->draw3DQuad(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, color);
-	}
-	void draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) {
-		_renderer->draw3DPolygon(x, y, z, count, color);
-	}
-	void end3D() { _renderer->end3D(); }
-	bool isAccelerated() const { return _renderer->isAccelerated(); }
-
-	void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) {
-		_renderer->drawQuad(x1, y1, x2, y2, x3, y3, x4, y4, color);
-	}
-	void drawPolygon(const int *x, const int *y, int count, uint32 color) {
-		_renderer->drawPolygon(x, y, count, color);
-	}
-
-	void copyToScreen() { _renderer->copyToScreen(); }
-	void setWireframe(bool enable) { _renderer->setWireframe(enable); }
-
-	uint32 white() { return 255; }
-	uint32 black() { return 0; }
-
-private:
-	Renderer *_renderer;
-};
-
-} // End of namespace Colony
-
 #endif
diff --git a/engines/colony/module.mk b/engines/colony/module.mk
index 76b35e3ab6a..cc50ad9ec34 100644
--- a/engines/colony/module.mk
+++ b/engines/colony/module.mk
@@ -6,7 +6,6 @@ MODULE_OBJS := \
 	metaengine.o \
 	render.o \
 	renderer_opengl.o \
-	renderer_software.o \
 	ui.o
 
 MODULE_DIRS += \
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index c7344c0164d..cbdba71fe38 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -36,72 +36,69 @@ static const int g_indexTable[4][10] = {
 	{0, 0,  0, 1,  1,  0,  0, -1,  2, 1}
 };
 
-static const int g_dirRight[4] = {1, 3, 0, 2};
-static const int g_dirLeft[4] = {2, 0, 3, 1};
- 
- // DOS object geometry constants
+// DOS object geometry constants
 static const int kScreenPts[8][3] = {
 	{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
 	{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
 };
 static const int kScreenSurf[4][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	{0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kTableTopPts[4][3] = {
-	{-128, 128, 100}, {128, 128, 100}, {128, -128, 100}, {-128, -128, 100}
+	{-64, 64, 100}, {64, 64, 100}, {64, -64, 100}, {-64, -64, 100}
 };
 static const int kTableTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
 static const int kTableBasePts[8][3] = {
-	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-	{-5, 5, 100}, {5, 5, 100}, {5, -5, 100}, {-5, -5, 100}
+	{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0},
+	{-20, 20, 100}, {20, 20, 100}, {20, -20, 100}, {-20, -20, 100}
 };
 static const int kTableBaseSurf[4][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
 	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kBedPostPts[4][3] = {
-	{-82, 128, 100}, {82, 128, 100}, {82, 128, 0}, {-82, 128, 0}
-};
-static const int kBBedPostPts[4][3] = {
-	{-130, 128, 100}, {130, 128, 100}, {130, 128, 0}, {-130, 128, 0}
+	{-80, 180, 0}, {80, 180, 0}, {80, 180, 100}, {-80, 180, 100}
 };
 static const int kBedPostSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kBedBlanketPts[8][3] = {
+	{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
+	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
+};
 static const int kBlanketSurf[4][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
 	{0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
 };
+static const int kBedSheetPts[8][3] = {
+	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60},
+	{-80, 70, 80}, {80, 70, 80}, {80, -175, 80}, {-80, -175, 80}
+};
 static const int kSheetSurf[3][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
 	{0, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kBedBlanketPts[8][3] = {
-	{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
-	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
-};
-static const int kBedSheetPts[8][3] = {
-	{-80, 128, 30}, {80, 128, 30}, {80, 70, 30}, {-80, 70, 30},
-	{-80, 128, 60}, {80, 128, 60}, {80, 70, 60}, {-80, 70, 60}
-};
 static const int kBBedBlanketPts[8][3] = {
-	{-128, 70, 0}, {128, 70, 0}, {128, -175, 0}, {-128, -175, 0},
-	{-128, 70, 60}, {128, 70, 60}, {128, -175, 60}, {-128, -175, 60}
+	{-120, 96, 0}, {120, 96, 0}, {120, -96, 0}, {-120, -96, 0},
+	{-120, 96, 60}, {120, 96, 60}, {120, -96, 60}, {-120, -96, 60}
 };
 static const int kBBedSheetPts[8][3] = {
-	{-128, 128, 30}, {128, 128, 30}, {128, 70, 30}, {-128, 70, 30},
-	{-128, 128, 60}, {128, 128, 60}, {128, 70, 60}, {-128, 70, 60}
+	{-120, 96, 60}, {120, 96, 60}, {120, -96, 60}, {-120, -96, 60},
+	{-120, 96, 80}, {120, 96, 80}, {120, -96, 80}, {-120, -96, 80}
+};
+static const int kBBedPostPts[4][3] = {
+	{-120, 96, 0}, {120, 96, 0}, {120, 96, 100}, {-120, 96, 100}
 };
 static const int kDeskTopPts[4][3] = {
-	{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
+	{-128, 80, 100}, {128, 80, 100}, {128, -80, 100}, {-128, -80, 100}
 };
 static const int kDeskTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
 static const int kDeskLeftPts[8][3] = {
-	{-135, 95, 0}, {-55, 95, 0}, {-55, -95, 0}, {-135, -95, 0},
-	{-135, 95, 100}, {-55, 95, 100}, {-55, -95, 100}, {-135, -95, 100}
+	{-128, 80, 0}, {-60, 80, 0}, {-60, -80, 0}, {-128, -80, 0},
+	{-128, 80, 100}, {-60, 80, 100}, {-60, -80, 100}, {-128, -80, 100}
 };
 static const int kDeskRightPts[8][3] = {
-	{55, 95, 0}, {135, 95, 0}, {135, -95, 0}, {55, -95, 0},
-	{55, 95, 100}, {135, 95, 100}, {135, -95, 100}, {55, -95, 100}
+	{60, 80, 0}, {128, 80, 0}, {128, -80, 0}, {60, -80, 0},
+	{60, 80, 100}, {128, 80, 100}, {128, -80, 100}, {60, -80, 100}
 };
 static const int kDeskCabSurf[4][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
@@ -115,32 +112,32 @@ static const int kArmLeftPts[4][3] = {
 	{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
 };
 static const int kArmRightPts[4][3] = {
-	{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
+	{40, 115, 90}, {40, 115, 0}, {40, 210, 0}, {40, 210, 90}
 };
 static const int kArmSurf[2][8] = {
-	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 3, 3, 0, 2, 0, 0, 0}
 };
 static const int kBackPts[4][3] = {
-	{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
+	{-40, 115, 130}, {40, 115, 130}, {40, 115, 0}, {-40, 115, 0}
 };
 static const int kBackSurf[2][8] = {
-	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 3, 3, 0, 2, 0, 0, 0}
 };
 static const int kComputerPts[8][3] = {
-	{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
-	{70, 25, 120}, {120, 25, 120}, {120, -25, 120}, {70, -25, 120}
-};
-static const int kMonitorPts[8][3] = {
-	{75, 20, 120}, {115, 20, 120}, {115, -20, 120}, {75, -20, 120},
-	{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
+	{-50, 60, 100}, {50, 60, 100}, {50, -60, 100}, {-50, -60, 100},
+	{-50, 60, 130}, {50, 60, 130}, {50, -60, 130}, {-50, -60, 130}
 };
 static const int kComputerSurf[5][8] = {
-	{0, 4, 7, 6, 5, 4, 0, 0}, {0, 4, 0, 3, 7, 4, 0, 0},
-	{0, 4, 3, 2, 6, 7, 0, 0}, {0, 4, 1, 0, 4, 5, 0, 0},
-	{0, 4, 2, 1, 5, 6, 0, 0}
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kMonitorPts[8][3] = {
+	{-40, 60, 130}, {40, 60, 130}, {40, -60, 130}, {-40, -60, 130},
+	{-40, 60, 200}, {40, 60, 200}, {40, -60, 200}, {-40, -60, 200}
 };
 static const int kDeskScreenPts[4][3] = {
-	{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
+	{-30, -60, 135}, {30, -60, 135}, {30, -60, 195}, {-30, -60, 195}
 };
 static const int kDeskScreenSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCSeatPts[4][3] = {
@@ -152,33 +149,28 @@ static const int kCArmLeftPts[4][3] = {
 };
 static const int kCArmLeftSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCArmRightPts[4][3] = {
-	{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
+	{40, -40, 60}, {40, 40, 60}, {50, 40, 90}, {50, -40, 90}
 };
 static const int kCArmRightSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCBackPts[4][3] = {
-	{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
+	{-50, -40, 120}, {50, -40, 120}, {50, -40, 60}, {-50, -40, 60}
 };
 static const int kCBackSurf[2][8] = {
-	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 3, 3, 0, 2, 0, 0, 0}
 };
 static const int kCBasePts[8][3] = {
-	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-	{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
+	{-8, 8, 0}, {8, 8, 0}, {8, -8, 0}, {-8, -8, 0},
+	{-8, 8, 60}, {8, 8, 60}, {8, -8, 60}, {-8, -8, 60}
 };
 static const int kCBaseSurf[4][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
 	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kConsolePts[8][3] = {
-	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-	{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
+	{-70, 110, 0}, {70, 110, 0}, {70, -110, 0}, {-70, -110, 0},
+	{-70, 110, 180}, {70, 110, 180}, {70, -110, 180}, {-70, -110, 180}
 };
 static const int kConsoleSurf[5][8] = {
-	{0, 4, 4, 0, 3, 7, 0, 0}, {0, 4, 7, 3, 2, 6, 0, 0},
-	{0, 4, 5, 1, 0, 4, 0, 0}, {0, 4, 6, 2, 1, 5, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kCouchSurf[5][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
 	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
 	{0, 4, 7, 6, 5, 4, 0, 0}
@@ -188,24 +180,29 @@ static const int kACouchPts[8][3] = {
 	{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50}
 };
 static const int kBCouchPts[8][3] = {
-	{-80, 150, 0}, {-45, 150, 0}, {-45, -150, 0}, {-80, -150, 0},
-	{-80, 150, 120}, {-55, 150, 120}, {-55, -150, 120}, {-80, -150, 120}
+	{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50},
+	{-50, 150, 100}, {50, 150, 100}, {50, -150, 100}, {-50, -150, 100}
 };
 static const int kCCouchPts[8][3] = {
-	{-70, 170, 0}, {50, 170, 0}, {50, 150, 0}, {-70, 150, 0},
-	{-70, 170, 80}, {50, 170, 80}, {50, 150, 80}, {-70, 150, 80}
+	{-70, 150, 0}, {-50, 150, 0}, {-50, -150, 0}, {-70, -150, 0},
+	{-70, 150, 100}, {-50, 150, 100}, {-50, -150, 100}, {-70, -150, 100}
 };
 static const int kDCouchPts[8][3] = {
-	{-70, -150, 0}, {50, -150, 0}, {50, -170, 0}, {-70, -170, 0},
-	{-70, -150, 80}, {50, -150, 80}, {50, -170, 80}, {-70, -170, 80}
+	{50, 150, 0}, {70, 150, 0}, {70, -150, 0}, {50, -150, 0},
+	{50, 150, 100}, {70, 150, 100}, {70, -150, 100}, {50, -150, 100}
+};
+static const int kCouchSurf[5][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 5, 1, 0, 4, 0, 0}, {0, 4, 6, 2, 1, 5, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kAChairPts[8][3] = {
-	{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
-	{-50, 50, 50}, {50, 50, 50}, {50, -50, 50}, {-50, -50, 50}
+	{-70, 70, 0}, {50, 70, 0}, {50, -70, 0}, {-70, -70, 0},
+	{-70, 70, 40}, {50, 70, 40}, {50, -70, 40}, {-70, -70, 40}
 };
 static const int kBChairPts[8][3] = {
-	{-80, 50, 0}, {-45, 50, 0}, {-45, -50, 0}, {-80, -50, 0},
-	{-80, 50, 120}, {-55, 50, 120}, {-55, -50, 120}, {-80, -50, 120}
+	{-70, 70, 40}, {50, 70, 40}, {50, -70, 40}, {-70, -70, 40},
+	{-70, 70, 80}, {50, 70, 80}, {50, -70, 80}, {-70, -70, 80}
 };
 static const int kCChairPts2[8][3] = {
 	{-70, 70, 0}, {50, 70, 0}, {50, 50, 0}, {-70, 50, 0},
@@ -333,56 +330,28 @@ static const Colony::ColonyEngine::PrismPartDef kPlantParts[4] = {
 	{4, kPlantLeaf1Pts, 1, kPlantLeafSurf},
 	{4, kPlantLeaf2Pts, 1, kPlantLeafSurf}
 };
- 
- 
-void ColonyEngine::rot_init(int x, int y) {
-	_rox = ((long)x * _tsin - (long)y * _tcos) >> 8;
-	_roy = ((long)y * _tsin + (long)x * _tcos) >> 8;
-}
-
-void ColonyEngine::perspective(int pnt[2], int rox, int roy) {
-	long p;
-
-	if (roy <= 0)
-		roy = 1;
-	p = _centerX + ((long)rox * 256) / roy;
 
-	if (p < -32000)
-		p = -32000;
-	else if (p > 32000)
-		p = 32000;
-	pnt[0] = (int)p;
-
-	if (_flip)
-		pnt[1] = _centerY + _rtable[roy];
-	else
-		pnt[1] = _centerY - _rtable[roy];
-}
 
 void ColonyEngine::quadrant() {
 	int remain;
 	int quad;
 
 	quad = _me.look >> 6;				/*divide by 64		*/
-	remain = _me.look - (quad << 6);			/*multiply by 64	*/
+	remain = _me.look - (quad << 6);		/*multiply by 64	*/
 	_tsin = _sint[remain];
 	_tcos = _cost[remain];
 
 	switch (quad) {
 	case 0:
-		rot_init((_me.xindex << 8) - _me.xloc, (_me.yindex << 8) - _me.yloc);
 		_direction = 0; // NORTH
 		break;
 	case 1:
-		rot_init((_me.yindex << 8) - _me.yloc, _me.xloc - ((_me.xindex + 1) << 8));
 		_direction = 2; // WEST
 		break;
 	case 2:
-		rot_init(_me.xloc - ((_me.xindex + 1) << 8), _me.yloc - ((_me.yindex + 1) << 8));
 		_direction = 3; // SOUTH
 		break;
 	case 3:
-		rot_init(_me.yloc - ((_me.yindex + 1) << 8), (_me.xindex << 8) - _me.xloc);
 		_direction = 1; // EAST
 		break;
 	}
@@ -400,198 +369,8 @@ void ColonyEngine::quadrant() {
 }
 
 void ColonyEngine::corridor() {
-	int length = 1;
-	int xFrontLeft, yFrontLeft;
-	int xFrontRight, yFrontRight;
-	int xsstart, ysstart;
-	int xfbehind, yfbehind;
-	int roxsave, roysave;
-	int left, right;
-	int left2, right2;
-	int cellx, celly;
-	int cellxsave, cellysave;
-	int dr[2];
-	const int screenLeft = (int)_screenR.left;
-	const int screenRight = (int)_screenR.right;
-
 	quadrant();
-
-	right = screenRight;
-	left = screenLeft;
-	right2 = right;
-	left2 = left;
-
-	if (_gfx->isAccelerated()) {
-		renderCorridor3D();
-		return;
-	}
-
-	xfbehind = _me.xindex + _frntxWall;
-	yfbehind = _me.yindex + _frntyWall;
-	xFrontLeft = xfbehind + _frntx;
-	yFrontLeft = yfbehind + _frnty;
-	xFrontRight = xFrontLeft + _sidex;
-	yFrontRight = yFrontLeft + _sidey;
-	xsstart = _me.xindex + _sidexWall;
-	ysstart = _me.yindex + _sideyWall;
-	cellxsave = cellx = _me.xindex;
-	cellysave = celly = _me.yindex;
-
-	int rox = _rox;
-	int roy = _roy;
-
-	if (_change) {
-		perspective(dr, rox, roy);
-		if (xfbehind >= 0 && xfbehind < 34 && yfbehind >= 0 && yfbehind < 34) {
-			_drY[xfbehind][yfbehind] = dr[1];
-			if (dr[0] > _screenR.left)
-				_drX[xfbehind][yfbehind] = -32000;
-			else
-				_drX[xfbehind][yfbehind] = dr[0];
-		}
-
-		perspective(dr, rox + _tsin, roy + _tcos);
-		if (xfbehind + _sidex >= 0 && xfbehind + _sidex < 34 && yfbehind + _sidey >= 0 && yfbehind + _sidey < 34) {
-			_drY[xfbehind + _sidex][yfbehind + _sidey] = dr[1];
-			if (dr[0] < _screenR.right)
-				_drX[xfbehind + _sidex][yfbehind + _sidey] = 32000;
-			else
-				_drX[xfbehind + _sidex][yfbehind + _sidey] = dr[0];
-		}
-	}
-
-	roxsave = rox;
-	roysave = roy;
-
-	// Move to the first wall in front of the observer.
-	rox -= _tcos;
-	roy += _tsin;
-
-	if (_change) {
-		perspective(dr, rox, roy);
-		if (xFrontLeft >= 0 && xFrontLeft < 34 && yFrontLeft >= 0 && yFrontLeft < 34) {
-			_drX[xFrontLeft][yFrontLeft] = dr[0];
-			_drY[xFrontLeft][yFrontLeft] = dr[1];
-		}
-		perspective(dr, rox + _tsin, roy + _tcos);
-		if (xFrontRight >= 0 && xFrontRight < 34 && yFrontRight >= 0 && yFrontRight < 34) {
-			_drX[xFrontRight][yFrontRight] = dr[0];
-			_drY[xFrontRight][yFrontRight] = dr[1];
-		}
-	}
-
-	if (wallAt(cellx + _sidexWall, celly + _sideyWall) & _side)
-		left2 = MAX(_drX[xFrontLeft][yFrontLeft], screenLeft);
-	else
-		left2 = MAX(left, left2);
-	left2 = MAX(left2, screenLeft);
-
-	if (wallAt(cellx + _sidexWall + _sidex, celly + _sideyWall + _sidey) & _side)
-		right2 = _drX[xFrontRight][yFrontRight];
-	else
-		right2 = MIN(right, right2);
-
-	uint32 white = _gfx->white();
-	_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
-	               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
-	if (wallAt(cellx, celly) & ~0x03)
-		frontfeature(cellx, celly, xFrontLeft, yFrontLeft, left2, right2, rox, roy);
-
-	while (!(wallAt(xFrontLeft, yFrontLeft) & _front)) {
-		rox -= _tcos;
-		roy += _tsin;
-		xFrontLeft += _frntx;
-		yFrontLeft += _frnty;
-		xFrontRight += _frntx;
-		yFrontRight += _frnty;
-		if (_change) {
-			perspective(dr, rox, roy);
-			if (xFrontLeft >= 0 && xFrontLeft < 34 && yFrontLeft >= 0 && yFrontLeft < 34) {
-				_drX[xFrontLeft][yFrontLeft] = dr[0];
-				_drY[xFrontLeft][yFrontLeft] = dr[1];
-			}
-			perspective(dr, rox + _tsin, roy + _tcos);
-			if (xFrontRight >= 0 && xFrontRight < 34 && yFrontRight >= 0 && yFrontRight < 34) {
-				_drX[xFrontRight][yFrontRight] = dr[0];
-				_drY[xFrontRight][yFrontRight] = dr[1];
-			}
-		}
-
-		cellx += _frntx;
-		celly += _frnty;
-		if (wallAt(cellx + _sidexWall, celly + _sideyWall) & _side)
-			left2 = MAX(screenLeft, _drX[xFrontLeft][yFrontLeft]);
-		else
-			left2 = MAX(left, left2);
-		left2 = MAX(left2, screenLeft);
-		if (wallAt(cellx + _sidexWall + _sidex, celly + _sideyWall + _sidey) & _side)
-			right2 = _drX[xFrontRight][yFrontRight];
-		else
-			right2 = MIN(right, right2);
-		if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
-			if (_robotArray[cellx][celly])
-				setRobot(left2, right2, _robotArray[cellx][celly]);
-			if (_foodArray[cellx][celly])
-				setRobot(left2, right2, _foodArray[cellx][celly]);
-		}
-		if (wallAt(cellx, celly) & ~0x03)
-			features(cellx, celly, xFrontLeft, yFrontLeft, left2, right2, rox, roy);
-
-		_gfx->drawLine(_drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft],
-		               _drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight], white);
-
-		length++;
-		if (length > 30)
-			break; // Safety break
-	}
-	drawend(xfbehind, yfbehind, xFrontLeft, yFrontLeft);
-
-	left = screenLeft;
-	right = MIN(right, _drX[xFrontLeft][yFrontLeft]);
-	if (left < right)
-		checkleft(xsstart, ysstart, xfbehind, yfbehind, left, right, roxsave, roysave, cellxsave, cellysave, length);
-
-	left = MAX(left, _drX[xFrontRight][yFrontRight]);
-	if (left < screenLeft)
-		left = screenLeft;
-	right = screenRight;
-	xsstart += _sidex;
-	ysstart += _sidey;
-	xfbehind += _sidex;
-	yfbehind += _sidey;
-	if (left < right)
-		checkright(xsstart, ysstart, xfbehind, yfbehind, left, right, roxsave + _tsin, roysave + _tcos, cellxsave, cellysave, length);
-
-	_change = false;
-}
-
-void ColonyEngine::drawend(int xstart, int ystart, int xFrontLeft, int yFrontLeft) {
-	int xFrontRight, yFrontRight;
-
-	xFrontRight = xFrontLeft + _sidex;
-	yFrontRight = yFrontLeft + _sidey;
-
-	uint32 white = _gfx->white();
-
-	if ((xstart != xFrontLeft) || (ystart != yFrontLeft)) {
-		if (_drY[xstart + _frntx][ystart + _frnty] > 0) {
-			_gfx->drawLine(_drX[xstart][ystart], _drY[xstart][ystart],
-			               _drX[xstart + _frntx][ystart + _frnty], _drY[xstart + _frntx][ystart + _frnty], white);
-		}
-		_gfx->drawLine(_drX[xstart + _frntx][ystart + _frnty], _drY[xstart + _frntx][ystart + _frnty],
-		               _drX[xFrontLeft][yFrontLeft], _drY[xFrontLeft][yFrontLeft], white);
-		
-		drawWall(xFrontLeft, yFrontLeft, xFrontRight, yFrontRight, white);
-
-		_gfx->drawLine(_drX[xFrontRight][yFrontRight], _drY[xFrontRight][yFrontRight],
-		               _drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey], white);
-		if (_drY[xstart + _sidex][ystart + _sidey] > 0) {
-			_gfx->drawLine(_drX[xstart + _sidex][ystart + _sidey], _drY[xstart + _sidex][ystart + _sidey],
-			               _drX[xstart + _frntx + _sidex][ystart + _frnty + _sidey], _drY[xstart + _frntx + _sidex][ystart + _frnty + _sidey], white);
-		}
-	} else {
-		drawWall(xFrontLeft, yFrontLeft, xFrontRight, yFrontRight, white);
-	}
+	renderCorridor3D();
 }
 
 uint8 ColonyEngine::wallAt(int x, int y) const {
@@ -600,948 +379,12 @@ uint8 ColonyEngine::wallAt(int x, int y) const {
 	return _wall[x][y];
 }
 
-void ColonyEngine::drawWall(int x1, int y1, int x2, int y2, uint32 color) {
-	_gfx->drawQuad(_drX[x1][y1], _drY[x1][y1],
-	               _drX[x2][y2], _drY[x2][y2],
-	               _drX[x2][y2], _height - _drY[x2][y2],
-	               _drX[x1][y1], _height - _drY[x1][y1],
-	               color);
-}
-
-void ColonyEngine::checkleft(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len) {
-	int i = 0, j;
-	int xf2, yf2;
-	int rox, roy;
-	int xsstart, ysstart;
-	int xfstart, yfstart;
-	int xestart, yestart;
-	int cellxsave, cellysave;
-	int dr[2];
-	uint32 white = _gfx->white();
-
-	cellx -= _sidex;
-	celly -= _sidey;
-	rx = rx - _tsin;
-	ry = ry - _tcos;
-
-	while (i < len && left <= right) {
-		if (wallAt(xs, ys) & _side) {
-			if (_flip)
-				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-
-			while ((wallAt(xs, ys) & _side) && i < len && left <= right) {
-				drawWall(xf, yf, xf + _frntx, yf + _frnty, white);
-
-				left = MAX(_drX[xf][yf], left);
-				xf += _frntx;
-				yf += _frnty;
-				xs += _frntx;
-				ys += _frnty;
-				cellx += _frntx;
-				celly += _frnty;
-				rx -= _tcos;
-				ry += _tsin;
-				i++;
-			}
-
-			if (_flip)
-				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-			left = MAX(_drX[xf][yf], left);
-		}
-
-		if (i < len && left <= right) {
-			j = 0;
-			xf2 = xf - _sidex;
-			yf2 = yf - _sidey;
-			xfstart = xf2;
-			yfstart = yf2;
-			xsstart = xs - _sidex;
-			ysstart = ys - _sidey;
-			cellxsave = cellx;
-			cellysave = celly;
-
-			rox = rx;
-			roy = ry;
-			if (_change) {
-				perspective(dr, rx, ry);
-				_drX[xf2][yf2] = dr[0];
-				_drY[xf2][yf2] = dr[1];
-			}
-
-			while (!(wallAt(xs, ys) & _side) && i < len) {
-				rx -= _tcos;
-				ry += _tsin;
-				if (_change) {
-					perspective(dr, rx, ry);
-					_drX[xf2 + _frntx][yf2 + _frnty] = dr[0];
-					_drY[xf2 + _frntx][yf2 + _frnty] = dr[1];
-				}
-
-				if (_drX[xf + _frntx][yf + _frnty] > left) {
-					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2],
-					               _drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty], white);
-					_gfx->drawLine(_drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty],
-					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
-					if (wallAt(cellx, celly) & ~0x03)
-						features(cellx, celly, xf2 + _frntx, yf2 + _frnty, left, right, rx, ry);
-					if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
-						if (_robotArray[cellx][celly])
-							setRobot(left, right, _robotArray[cellx][celly]);
-						if (_foodArray[cellx][celly])
-							setRobot(left, right, _foodArray[cellx][celly]);
-					}
-				} else {
-					j = 0;
-					xfstart = xf2;
-					yfstart = yf2;
-					xsstart = xs - _sidex;
-					ysstart = ys - _sidey;
-					rox = rx + _tcos;
-					roy = ry - _tsin;
-					cellxsave = cellx;
-					cellysave = celly;
-				}
-
-				xf2 += _frntx;
-				yf2 += _frnty;
-				xf += _frntx;
-				yf += _frnty;
-				xs += _frntx;
-				ys += _frnty;
-				cellx += _frntx;
-				celly += _frnty;
-				i++;
-				j++;
-			}
-
-			if (wallAt(xf - _sidex, yf - _sidey) & _front) {
-				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2], _drX[xf][yf], _height - _drY[xf][yf], white);
-
-				if (MIN(_drX[xf2][yf2], right) >= left) {
-					checkleft(xsstart, ysstart, xfstart, yfstart, left, MIN(right, _drX[xf2][yf2]),
-					          rox, roy, cellxsave, cellysave, j);
-				}
-			} else {
-				if (_flip)
-					_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-				xestart = xf2;
-				yestart = yf2;
-
-				while (!(wallAt(xf2, yf2) & _front)) {
-					rx -= _tcos;
-					ry += _tsin;
-					cellx += _frntx;
-					celly += _frnty;
-					xf2 += _frntx;
-					yf2 += _frnty;
-					xf += _frntx;
-					yf += _frnty;
-					xs += _frntx;
-					ys += _frnty;
-					if (_change) {
-						perspective(dr, rx, ry);
-						_drX[xf2][yf2] = dr[0];
-						_drY[xf2][yf2] = dr[1];
-					}
-					if (_change) {
-						perspective(dr, rx + _tsin, ry + _tcos);
-						_drX[xf][yf] = dr[0];
-						_drY[xf][yf] = dr[1];
-					}
-					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
-					if (wallAt(cellx - _frntx, celly - _frnty) & ~0x03)
-						features(cellx - _frntx, celly - _frnty, xf2, yf2, left, right, rx, ry);
-					const int objxL = cellx - _frntx;
-					const int objyL = celly - _frnty;
-					if (objxL >= 0 && objxL < 32 && objyL >= 0 && objyL < 32) {
-						if (_robotArray[objxL][objyL])
-							setRobot(left, right, _robotArray[objxL][objyL]);
-						if (_foodArray[objxL][objyL])
-							setRobot(left, right, _foodArray[objxL][objyL]);
-					}
-					i++;
-					j++;
-				}
-
-				_gfx->drawLine(_drX[xestart][yestart], _drY[xestart][yestart], _drX[xf2][yf2], _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2],
-				               _drX[xf2 + _sidex][yf2 + _sidey], _height - _drY[xf2 + _sidex][yf2 + _sidey], white);
-
-				if (MIN(_drX[xf2][yf2], right) >= left) {
-					checkleft(xsstart, ysstart, xfstart, yfstart, left, MIN(_drX[xf2][yf2], right),
-					          rox, roy, cellxsave, cellysave, j);
-				}
-			}
-		}
-	}
-}
-
-void ColonyEngine::checkright(int xs, int ys, int xf, int yf, int left, int right, int rx, int ry, int cellx, int celly, int len) {
-	int i = 0, j;
-	int xf2, yf2;
-	int rox, roy;
-	int xsstart, ysstart;
-	int xfstart, yfstart;
-	int xestart, yestart;
-	int cellxsave, cellysave;
-	int dr[2];
-	uint32 white = _gfx->white();
-
-	cellx += _sidex;
-	celly += _sidey;
-	rx = rx + _tsin;
-	ry = ry + _tcos;
-
-	while (i < len && left < right) {
-		if (wallAt(xs, ys) & _side) {
-			if (_flip)
-				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-
-			while ((wallAt(xs, ys) & _side) && i < len && left < right) {
-				drawWall(xf, yf, xf + _frntx, yf + _frnty, white);
-
-				right = MIN(_drX[xf][yf], right);
-				xf += _frntx;
-				yf += _frnty;
-				xs += _frntx;
-				ys += _frnty;
-				cellx += _frntx;
-				celly += _frnty;
-				rx -= _tcos;
-				ry += _tsin;
-				i++;
-			}
-
-			if (_flip)
-				_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-			right = MIN(_drX[xf][yf], right);
-		}
-
-		if (i < len && left < right) {
-			j = 0;
-			xf2 = xf + _sidex;
-			yf2 = yf + _sidey;
-			xfstart = xf2;
-			yfstart = yf2;
-			xsstart = xs + _sidex;
-			ysstart = ys + _sidey;
-			cellxsave = cellx;
-			cellysave = celly;
-
-			rox = rx;
-			roy = ry;
-			if (_change) {
-				perspective(dr, rx, ry);
-				_drX[xf2][yf2] = dr[0];
-				_drY[xf2][yf2] = dr[1];
-			}
-
-			while (!(wallAt(xs, ys) & _side) && i < len) {
-				rx -= _tcos;
-				ry += _tsin;
-				if (_change) {
-					perspective(dr, rx, ry);
-					_drX[xf2 + _frntx][yf2 + _frnty] = dr[0];
-					_drY[xf2 + _frntx][yf2 + _frnty] = dr[1];
-				}
-
-				if (_drX[xf + _frntx][yf + _frnty] < right) {
-					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2],
-					               _drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty], white);
-					_gfx->drawLine(_drX[xf2 + _frntx][yf2 + _frnty], _drY[xf2 + _frntx][yf2 + _frnty],
-					               _drX[xf + _frntx][yf + _frnty], _drY[xf + _frntx][yf + _frnty], white);
-					if (wallAt(cellx, celly) & ~0x03)
-						features(cellx, celly, xf + _frntx, yf + _frnty, left, right, rx - _tsin, ry - _tcos);
-					if (cellx >= 0 && cellx < 32 && celly >= 0 && celly < 32) {
-						if (_robotArray[cellx][celly])
-							setRobot(left, right, _robotArray[cellx][celly]);
-						if (_foodArray[cellx][celly])
-							setRobot(left, right, _foodArray[cellx][celly]);
-					}
-				} else {
-					j = 0;
-					xfstart = xf2;
-					yfstart = yf2;
-					xsstart = xs + _sidex;
-					ysstart = ys + _sidey;
-					rox = rx + _tcos;
-					roy = ry - _tsin;
-					cellxsave = cellx;
-					cellysave = celly;
-				}
-
-				xf2 += _frntx;
-				yf2 += _frnty;
-				xf += _frntx;
-				yf += _frnty;
-				xs += _frntx;
-				ys += _frnty;
-				cellx += _frntx;
-				celly += _frnty;
-				i++;
-				j++;
-			}
-
-			if (wallAt(xf, yf) & _front) {
-				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2], _drX[xf][yf], _height - _drY[xf][yf], white);
-
-				if (MAX(_drX[xf2][yf2], left) < right) {
-					checkright(xsstart, ysstart, xfstart, yfstart, MAX(left, _drX[xf2][yf2]), right,
-					           rox, roy, cellxsave, cellysave, j);
-				}
-			} else {
-				if (_flip)
-					_gfx->drawLine(_drX[xf][yf], 0, _drX[xf][yf], _height - _drY[xf][yf], white);
-				xestart = xf2;
-				yestart = yf2;
-
-				while (!(wallAt(xf, yf) & _front)) {
-					rx -= _tcos;
-					ry += _tsin;
-					cellx += _frntx;
-					celly += _frnty;
-					xf2 += _frntx;
-					yf2 += _frnty;
-					xf += _frntx;
-					yf += _frnty;
-					xs += _frntx;
-					ys += _frnty;
-					if (_change) {
-						perspective(dr, rx, ry);
-						_drX[xf2][yf2] = dr[0];
-						_drY[xf2][yf2] = dr[1];
-					}
-					if (_change) {
-						perspective(dr, rx - _tsin, ry - _tcos);
-						_drX[xf][yf] = dr[0];
-						_drY[xf][yf] = dr[1];
-					}
-					_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf][yf], _drY[xf][yf], white);
-					if (wallAt(cellx - _frntx, celly - _frnty) & ~0x03)
-						features(cellx - _frntx, celly - _frnty, xf, yf, left, right, rx - _tsin, ry - _tcos);
-					const int objxR = cellx - _frntx;
-					const int objyR = celly - _frnty;
-					if (objxR >= 0 && objxR < 32 && objyR >= 0 && objyR < 32) {
-						if (_robotArray[objxR][objyR])
-							setRobot(left, right, _robotArray[objxR][objyR]);
-						if (_foodArray[objxR][objyR])
-							setRobot(left, right, _foodArray[objxR][objyR]);
-					}
-					i++;
-					j++;
-				}
-
-				_gfx->drawLine(_drX[xestart][yestart], _drY[xestart][yestart], _drX[xf2][yf2], _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _drY[xf2][yf2], _drX[xf2][yf2], _height - _drY[xf2][yf2], white);
-				_gfx->drawLine(_drX[xf2][yf2], _height - _drY[xf2][yf2],
-				               _drX[xf2 - _sidex][yf2 - _sidey], _height - _drY[xf2 - _sidex][yf2 - _sidey], white);
-
-				if (MAX(_drX[xf2][yf2], left) < right) {
-					checkright(xsstart, ysstart, xfstart, yfstart, MAX(_drX[xf2][yf2], left), right,
-					           rox, roy, cellxsave, cellysave, j);
-				}
-			}
-		}
-	}
-}
-
 const uint8 *ColonyEngine::mapFeatureAt(int x, int y, int direction) const {
 	if (x < 0 || x >= 31 || y < 0 || y >= 31 || direction < 0 || direction >= 5)
 		return nullptr;
 	return _mapData[x][y][direction];
 }
 
-void ColonyEngine::frontfeature(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry) {
-	int l[4], r[4];
-
-	l[0] = _drX[xFront][yFront];
-	l[2] = rx - _tcos;
-	l[3] = ry + _tsin;
-	r[0] = _drX[xFront + _sidex][yFront + _sidey];
-	r[2] = rx + _tsin - _tcos;
-	r[3] = ry + _tsin + _tcos;
-	if (_flip) {
-		l[1] = _height - _drY[xFront][yFront];
-		r[1] = _height - _drY[xFront + _sidex][yFront + _sidey];
-	} else {
-		l[1] = _drY[xFront][yFront];
-		r[1] = _drY[xFront + _sidex][yFront + _sidey];
-	}
-
-	if (MAX(left, l[0]) < MIN(right, r[0])) {
-		const uint8 *map = mapFeatureAt(cellx, celly, _direction);
-		if (map && map[0])
-			dowall(cellx, celly, _direction, l, r);
-	}
-}
-
-void ColonyEngine::features(int cellx, int celly, int xFront, int yFront, int left, int right, int rx, int ry) {
-	int l[4], r[4], ll[4], rr[4];
-
-	l[0] = _drX[xFront][yFront];
-	l[2] = rx - _tcos;
-	l[3] = ry + _tsin;
-	r[0] = _drX[xFront + _sidex][yFront + _sidey];
-	r[2] = rx + _tsin - _tcos;
-	r[3] = ry + _tsin + _tcos;
-	if (_flip) {
-		l[1] = _height - _drY[xFront][yFront];
-		r[1] = _height - _drY[xFront + _sidex][yFront + _sidey];
-	} else {
-		l[1] = _drY[xFront][yFront];
-		r[1] = _drY[xFront + _sidex][yFront + _sidey];
-	}
-
-	if (MAX(left, l[0]) + 1 < MIN(right, r[0]) - 1) {
-		const uint8 *map = mapFeatureAt(cellx, celly, _direction);
-		if (map && map[0])
-			dowall(cellx, celly, _direction, l, r);
-	}
-
-	ll[0] = r[0];
-	ll[1] = r[1];
-	ll[2] = rx + _tsin + _tsin;
-	ll[3] = ry + _tcos + _tcos;
-	rr[0] = _drX[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
-	if (_flip)
-		rr[1] = _height - _drY[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
-	else
-		rr[1] = _drY[xFront + _sidex - _frntx][yFront + _sidey - _frnty];
-	rr[2] = rx + _tsin + _tsin + _tcos;
-	rr[3] = ry + _tsin + _tsin - _tcos; // wait, let me check colony.cpp line 1291. ry + _tcos + _tcos - _tsin; 
-	// Ah, I see: rr[3] = ry + _tsin + _tsin - _tcos; wait, I copied it wrong.
-	// colony.cpp:1291: 	rr[3] = ry + _tcos + _tcos - _tsin;
-	// Let me re-copy accurately.
-	
-	rr[3] = ry + _tcos + _tcos - _tsin;
-	if (MAX(left, ll[0]) + 1 < MIN(right, rr[0]) - 1) {
-		const uint8 *map = mapFeatureAt(cellx, celly, g_dirRight[_direction]);
-		if (map && map[0])
-			dowall(cellx, celly, g_dirRight[_direction], ll, rr);
-	}
-
-	ll[0] = _drX[xFront - _frntx][yFront - _frnty];
-	if (_flip)
-		ll[1] = _height - _drY[xFront - _frntx][yFront - _frnty];
-	else
-		ll[1] = _drY[xFront - _frntx][yFront - _frnty];
-	ll[2] = rx + _tcos - _tsin;
-	ll[3] = (ry - _tcos) - _tsin;
-	rr[0] = l[0];
-	rr[1] = l[1];
-	rr[2] = rx - _tsin;
-	rr[3] = ry - _tcos;
-	if (MAX(left, ll[0]) + 1 < MIN(right, rr[0]) - 1) {
-		const uint8 *map = mapFeatureAt(cellx, celly, g_dirLeft[_direction]);
-		if (map && map[0])
-			dowall(cellx, celly, g_dirLeft[_direction], ll, rr);
-	}
-}
-
-void ColonyEngine::dowall(int cellx, int celly, int direction, int left[4], int right[4]) {
-	const uint8 *map = mapFeatureAt(cellx, celly, direction);
-	int left2[2], right2[2];
-	if (!map)
-		return;
-
-	switch (map[0]) {
-	case kWallFeatureDoor:
-		if (_level == 1 || _level == 5 || _level == 6) {
-			if (map[1] == 0)
-				drawOpenSSDoor(left, right);
-			else
-				drawClosedSSDoor(left, right);
-		} else {
-			if (map[1] == 0) {
-				perspective(left2, left[2], left[3]);
-				perspective(right2, right[2], right[3]);
-				if (_flip) {
-					left2[1] = _height - left2[1];
-					right2[1] = _height - right2[1];
-				}
-				drawOpenDoor(left, right, left2, right2);
-			} else {
-				drawClosedDoor(left, right);
-			}
-		}
-		break;
-	case kWallFeatureWindow:
-		drawWindow(left, right);
-		break;
-	case kWallFeatureShelves:
-		perspective(left2, left[2], left[3]);
-		perspective(right2, right[2], right[3]);
-		if (_flip) {
-			left2[1] = _height - left2[1];
-			right2[1] = _height - right2[1];
-		}
-		drawBooks(left, right, left2, right2);
-		break;
-	case kWallFeatureUpStairs:
-		perspective(left2, left[2], left[3]);
-		perspective(right2, right[2], right[3]);
-		if (_flip) {
-			left2[1] = _height - left2[1];
-			right2[1] = _height - right2[1];
-		}
-		drawUpStairs(left, right, left2, right2);
-		break;
-	case kWallFeatureDnStairs:
-		perspective(left2, left[2], left[3]);
-		perspective(right2, right[2], right[3]);
-		if (_flip) {
-			left2[1] = _height - left2[1];
-			right2[1] = _height - right2[1];
-		}
-		drawDnStairs(left, right, left2, right2);
-		break;
-	case kWallFeatureGlyph:
-		drawGlyphs(left, right);
-		break;
-	case kWallFeatureElevator:
-		drawElevator(left, right);
-		break;
-	case kWallFeatureTunnel:
-		perspective(left2, left[2], left[3]);
-		perspective(right2, right[2], right[3]);
-		if (_flip) {
-			left2[1] = _height - left2[1];
-			right2[1] = _height - right2[1];
-		}
-		drawTunnel(left, right, left2, right2);
-		break;
-	case kWallFeatureAirlock:
-		if (map[1] == 0)
-			drawALOpen(left, right);
-		else
-			drawALClosed(left, right);
-		break;
-	case kWallFeatureColor:
-		drawColor(map, left, right);
-		break;
-	default:
-		break;
-	}
-}
-
-void ColonyEngine::drawWindow(int left[4], int right[4]) {
-	const uint32 dark = 160;
-	int x1 = left[0];
-	int x2 = right[0];
-	int y1 = left[1];
-	int y2 = right[1];
-	int y3 = _height - right[1];
-	int y4 = _height - left[1];
-	int xc = (x1 + x2) >> 1;
-	int xx1 = (xc + x1) >> 1;
-	int xx2 = (xc + x2) >> 1;
-	if (xx2 < _screenR.left || xx1 > _screenR.right)
-		return;
-	int yl = (y1 + y4) >> 1;
-	int yr = (y2 + y3) >> 1;
-	int yy1 = (yl + y1) >> 1;
-	int yy2 = (yr + y2) >> 1;
-	int yy3 = (yl + y3) >> 1;
-	int yy4 = (yr + y4) >> 1;
-	int yy[4];
-	yy[0] = _height - ((((yy1 + yy2) >> 1) + yy1) >> 1);
-	yy[1] = _height - ((((yy1 + yy2) >> 1) + yy2) >> 1);
-	yy[2] = _height - ((((yy3 + yy4) >> 1) + yy3) >> 1);
-	yy[3] = _height - ((((yy3 + yy4) >> 1) + yy4) >> 1);
-	_gfx->drawLine(xx1, yy[0], xx2, yy[1], dark);
-	_gfx->drawLine(xx2, yy[1], xx2, yy[2], dark);
-	_gfx->drawLine(xx2, yy[2], xx1, yy[3], dark);
-	_gfx->drawLine(xx1, yy[3], xx1, yy[0], dark);
-	_gfx->drawLine(xc, (yy[0] + yy[1]) >> 1, xc, (yy[2] + yy[3]) >> 1, dark);
-	_gfx->drawLine(xx1, yl, xx2, yr, dark);
-}
-
-void ColonyEngine::drawClosedDoor(int left[4], int right[4]) {
-	const uint32 dark = 160;
-	int x1 = left[0];
-	int x2 = right[0];
-	int y1 = left[1];
-	int y2 = right[1];
-	int y3 = _height - right[1];
-	int y4 = _height - left[1];
-	int xc = (x1 + x2) >> 1;
-	int xx1 = (xc + x1) >> 1;
-	int xx2 = (xc + x2) >> 1;
-	if (xx2 < _screenR.left || xx1 > _screenR.right)
-		return;
-
-	int yc = (y1 + y2) >> 1;
-	int ytl = (yc + y1) >> 1;
-	int ytr = (yc + y2) >> 1;
-	yc = (y4 + y3) >> 1;
-	int ybl = (yc + y4) >> 1;
-	int ybr = (yc + y3) >> 1;
-	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
-	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
-
-	_gfx->drawLine(xx1, ybl, xx1, ytl, dark);
-	_gfx->drawLine(xx1, ytl, xx2, ytr, dark);
-	_gfx->drawLine(xx2, ytr, xx2, ybr, dark);
-	_gfx->drawLine(xx2, ybr, xx1, ybl, dark);
-
-	ybl = (ybl + ytl) >> 1;
-	ybr = (ybr + ytr) >> 1;
-	yc = (ybl + ybr) >> 1;
-	ybl = (((yc + ybl) >> 1) + ybl) >> 1;
-	ybr = (((yc + ybr) >> 1) + ybr) >> 1;
-	xx1 = (((xx1 + xc) >> 1) + xx1) >> 1;
-	xx2 = (((xx2 + xc) >> 1) + xx2) >> 1;
-	_gfx->drawLine(xx1, ybl, xx2, ybr, dark);
-}
-
-void ColonyEngine::drawOpenDoor(int left[4], int right[4], int left2[2], int right2[2]) {
-	const uint32 dark = 160;
-	const uint32 light = 210;
-	int x1 = left[0];
-	int x2 = right[0];
-	int y1 = left[1];
-	int y2 = right[1];
-	int y3 = _height - right[1];
-	int y4 = _height - left[1];
-	int xc = (x1 + x2) >> 1;
-	int xl = (xc + x1) >> 1;
-	int xr = (xc + x2) >> 1;
-	int yc = (y1 + y2) >> 1;
-	int ytl = (yc + y1) >> 1;
-	int ytr = (yc + y2) >> 1;
-	yc = (y4 + y3) >> 1;
-	int ybl = (yc + y4) >> 1;
-	int ybr = (yc + y3) >> 1;
-	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
-	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
-	if (xr < _screenR.left || xl > _screenR.right)
-		return;
-
-	_gfx->drawLine(xl, ybl, xl, ytl, dark);
-	_gfx->drawLine(xl, ytl, xr, ytr, dark);
-	_gfx->drawLine(xr, ytr, xr, ybr, dark);
-	_gfx->drawLine(xr, ybr, xl, ybl, dark);
-
-	x1 = left2[0];
-	x2 = right2[0];
-	y1 = _height - left2[1];
-	y2 = _height - right2[1];
-	xc = (x1 + x2) >> 1;
-	int xfl = (xc + x1) >> 1;
-	int xfr = (xc + x2) >> 1;
-	yc = (y1 + y2) >> 1;
-	int yfl = (yc + y1) >> 1;
-	int yfr = (yc + y2) >> 1;
-
-	_gfx->drawLine(xl, ybl, xfl, yfl, light);
-	_gfx->drawLine(xfl, yfl, xfr, yfr, light);
-	_gfx->drawLine(xfr, yfr, xr, ybr, light);
-	_gfx->drawLine(xr, ybr, xl, ybl, light);
-}
-
-void ColonyEngine::drawTunnel(int left[4], int right[4], int left2[2], int right2[2]) {
-	const uint32 dark = 120;
-	int baseX[7], baseY[7], tunnelY[7][7];
-	int xl = left[0];
-	int xr = right[0];
-	int ytl = left[1];
-	int ytr = right[1];
-	int ybr = _height - right[1];
-	int ybl = _height - left[1];
-	int hl = ybl - ytl;
-	int hr = ybr - ytr;
-	(void)left2;
-	(void)right2;
-	(void)MAX(hl, hr);
-	split7(baseX, xl, xr);
-	if (baseX[0] > _screenR.right || baseX[6] < _screenR.left)
-		return;
-	split7(baseY, ybl, ybr);
-	for (int i = 0; i < 7; i++)
-		split7(tunnelY[i], baseY[i], _height - baseY[i]);
-
-	int x[6] = {baseX[0], baseX[0], baseX[1], baseX[5], baseX[6], baseX[6]};
-	int y[6] = {baseY[0], tunnelY[0][5], tunnelY[1][6], tunnelY[5][6], tunnelY[6][5], baseY[6]};
-	for (int i = 0; i < 6; i++) {
-		int n = (i + 1) % 6;
-		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
-	}
-}
-
-void ColonyEngine::drawGlyphs(int left[4], int right[4]) {
-	const uint32 dark = 170;
-	int xl = left[0];
-	int xr = right[0];
-	int y1 = left[1];
-	int y2 = right[1];
-	int y3 = _height - right[1];
-	int y4 = _height - left[1];
-	int xc = (xl + xr) >> 1;
-	xl = (((xc + xl) >> 1) + xl) >> 1;
-	xr = (((xc + xr) >> 1) + xr) >> 1;
-	int ytc = (y1 + y2) >> 1;
-	int ybc = (y3 + y4) >> 1;
-	int ytl = (((y1 + ytc) >> 1) + y1) >> 1;
-	int ytr = (((y2 + ytc) >> 1) + y2) >> 1;
-	int ybl = (((y4 + ybc) >> 1) + y4) >> 1;
-	int ybr = (((y3 + ybc) >> 1) + y3) >> 1;
-	int yl1 = (ytl + ybl) >> 1;
-	int yr1 = (ytr + ybr) >> 1;
-	int yl2 = (yl1 + ytl) >> 1;
-	int yr2 = (yr1 + ytr) >> 1;
-	int yl3 = (yl2 + yl1) >> 1;
-	int yr3 = (yr2 + yr1) >> 1;
-	int yl4 = (yl1 + ybl) >> 1;
-	int yr4 = (yr1 + ybr) >> 1;
-	int yr5 = (yr4 + yr1) >> 1;
-	int yl5 = (yl4 + yl1) >> 1;
-
-	_gfx->drawLine(xl, yl1, xr, yr1, dark);
-	_gfx->drawLine(xl, yl2, xr, yr2, dark);
-	_gfx->drawLine(xl, yl3, xr, yr3, dark);
-	_gfx->drawLine(xl, yl4, xr, yr4, dark);
-	_gfx->drawLine(xl, yl5, xr, yr5, dark);
-	_gfx->drawLine(xl, (yl2 + yl3) >> 1, xr, (yr2 + yr3) >> 1, dark);
-	_gfx->drawLine(xl, (yl3 + yl1) >> 1, xr, (yr3 + yr1) >> 1, dark);
-	_gfx->drawLine(xl, (yl1 + yl5) >> 1, xr, (yr1 + yr5) >> 1, dark);
-	_gfx->drawLine(xl, (yl4 + yl5) >> 1, xr, (yr4 + yr5) >> 1, dark);
-}
-
-void ColonyEngine::drawBooks(int left[4], int right[4], int left2[2], int right2[2]) {
-	const uint32 dark = 170;
-	int l2[2] = {left2[0], left2[1]};
-	int r2[2] = {right2[0], right2[1]};
-	for (int i = 0; i < 2; i++) {
-		l2[0] = (l2[0] + left[0]) >> 1;
-		l2[1] = (l2[1] + left[1]) >> 1;
-		r2[0] = (r2[0] + right[0]) >> 1;
-		r2[1] = (r2[1] + right[1]) >> 1;
-	}
-	_gfx->drawLine(l2[0], l2[1], l2[0], _height - l2[1], dark);
-	_gfx->drawLine(l2[0], _height - l2[1], r2[0], _height - r2[1], dark);
-	_gfx->drawLine(r2[0], _height - r2[1], r2[0], r2[1], dark);
-	_gfx->drawLine(r2[0], r2[1], l2[0], l2[1], dark);
-	_gfx->drawLine(left[0], left[1], l2[0], l2[1], dark);
-	_gfx->drawLine(left[0], _height - left[1], l2[0], _height - l2[1], dark);
-	_gfx->drawLine(right[0], right[1], r2[0], r2[1], dark);
-	_gfx->drawLine(right[0], _height - right[1], r2[0], _height - r2[1], dark);
-
-	int lf[7], rf[7], lb[7], rb[7];
-	split7(lf, left[1], _height - left[1]);
-	split7(rf, right[1], _height - right[1]);
-	split7(lb, l2[1], _height - l2[1]);
-	split7(rb, r2[1], _height - r2[1]);
-	for (int i = 0; i < 7; i++) {
-		_gfx->drawLine(left[0], lf[i], right[0], rf[i], dark);
-		_gfx->drawLine(right[0], rf[i], r2[0], rb[i], dark);
-		_gfx->drawLine(r2[0], rb[i], l2[0], lb[i], dark);
-		_gfx->drawLine(l2[0], lb[i], left[0], lf[i], dark);
-	}
-}
-
-void ColonyEngine::drawUpStairs(int left[4], int right[4], int left2[2], int right2[2]) {
-	const uint32 dark = 170;
-	int xl[7], xr[7], yl[7], yr[7];
-	split7(xl, left[0], left2[0]);
-	split7(xr, right[0], right2[0]);
-	split7(yl, _height - left[1], left2[1]);
-	split7(yr, _height - right[1], right2[1]);
-	for (int i = 0; i < 6; i++) {
-		_gfx->drawLine(xl[i], yl[i], xl[i + 1], yl[i + 1], dark);
-		_gfx->drawLine(xr[i], yr[i], xr[i + 1], yr[i + 1], dark);
-		_gfx->drawLine(xl[i], yl[i], xr[i], yr[i], dark);
-	}
-}
-
-void ColonyEngine::drawDnStairs(int left[4], int right[4], int left2[2], int right2[2]) {
-	const uint32 dark = 170;
-	int xl[7], xr[7], yl[7], yr[7];
-	split7(xl, left[0], left2[0]);
-	split7(xr, right[0], right2[0]);
-	split7(yl, left[1], left2[1]);
-	split7(yr, right[1], right2[1]);
-	for (int i = 0; i < 6; i++) {
-		_gfx->drawLine(xl[i], yl[i], xl[i + 1], yl[i + 1], dark);
-		_gfx->drawLine(xr[i], yr[i], xr[i + 1], yr[i + 1], dark);
-		_gfx->drawLine(xl[i], _height - yl[i], xr[i], _height - yr[i], dark);
-	}
-}
-
-void ColonyEngine::drawALOpen(int left[4], int right[4]) {
-	const uint32 dark = 150;
-	int lr[7], ud[7][7];
-	split7x7(left, right, lr, ud);
-	int x[8] = {lr[0], lr[1], lr[3], lr[5], lr[6], lr[5], lr[3], lr[1]};
-	int y[8] = {ud[3][0], ud[5][1], ud[6][3], ud[5][5], ud[3][6], ud[1][5], ud[0][3], ud[1][1]};
-	for (int i = 0; i < 8; i++) {
-		int n = (i + 1) % 8;
-		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
-	}
-}
-
-void ColonyEngine::drawALClosed(int left[4], int right[4]) {
-	const uint32 dark = 170;
-	int lr[7], ud[7][7];
-	split7x7(left, right, lr, ud);
-	int x[8] = {lr[0], lr[1], lr[3], lr[5], lr[6], lr[5], lr[3], lr[1]};
-	int y[8] = {ud[3][0], ud[5][1], ud[6][3], ud[5][5], ud[3][6], ud[1][5], ud[0][3], ud[1][1]};
-	for (int i = 0; i < 8; i++) {
-		int n = (i + 1) % 8;
-		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
-	}
-	_gfx->drawLine(lr[0], ud[3][0], lr[3], ud[3][3], dark);
-	_gfx->drawLine(lr[3], ud[6][3], lr[3], ud[3][3], dark);
-	_gfx->drawLine(lr[6], ud[3][6], lr[3], ud[3][3], dark);
-	_gfx->drawLine(lr[3], ud[0][3], lr[3], ud[3][3], dark);
-}
-
-void ColonyEngine::drawOpenSSDoor(int left[4], int right[4]) {
-	const uint32 dark = 140;
-	int lr[7], ud[7][7];
-	split7x7(left, right, lr, ud);
-	int x[8] = {lr[2], lr[1], lr[1], lr[2], lr[4], lr[5], lr[5], lr[4]};
-	int y[8] = {ud[0][2], ud[1][1], ud[5][1], ud[6][2], ud[6][4], ud[5][5], ud[1][5], ud[0][4]};
-	for (int i = 0; i < 8; i++) {
-		int n = (i + 1) % 8;
-		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
-	}
-}
-
-void ColonyEngine::drawClosedSSDoor(int left[4], int right[4]) {
-	const uint32 dark = 170;
-	int lr[7], ud[7][7];
-	split7x7(left, right, lr, ud);
-	int x[8] = {lr[2], lr[1], lr[1], lr[2], lr[4], lr[5], lr[5], lr[4]};
-	int y[8] = {ud[0][2], ud[1][1], ud[5][1], ud[6][2], ud[6][4], ud[5][5], ud[1][5], ud[0][4]};
-	for (int i = 0; i < 8; i++) {
-		int n = (i + 1) % 8;
-		_gfx->drawLine(x[i], y[i], x[n], y[n], dark);
-	}
-	_gfx->drawLine(lr[2], ud[1][2], lr[2], ud[5][2], dark);
-	_gfx->drawLine(lr[2], ud[5][2], lr[4], ud[5][4], dark);
-	_gfx->drawLine(lr[4], ud[5][4], lr[4], ud[1][4], dark);
-	_gfx->drawLine(lr[4], ud[1][4], lr[2], ud[1][2], dark);
-}
-
-void ColonyEngine::drawElevator(int left[4], int right[4]) {
-	const uint32 dark = 170;
-	int x1 = left[0];
-	int x2 = right[0];
-	int y1 = left[1];
-	int y2 = right[1];
-	int y3 = _height - right[1];
-	int y4 = _height - left[1];
-	int xc = (x1 + x2) >> 1;
-	int xx1 = (xc + x1) >> 1;
-	xx1 = (x1 + xx1) >> 1;
-	int xx2 = (xc + x2) >> 1;
-	xx2 = (x2 + xx2) >> 1;
-	if (xx2 < _screenR.left || xx1 > _screenR.right)
-		return;
-	int ytc = (y1 + y2) >> 1;
-	int ytl = (ytc + y1) >> 1;
-	ytl = (ytl + y1) >> 1;
-	int ytr = (ytc + y2) >> 1;
-	ytr = (ytr + y2) >> 1;
-	int ybc = (y4 + y3) >> 1;
-	int ybl = (ybc + y4) >> 1;
-	ybl = (ybl + y4) >> 1;
-	int ybr = (ybc + y3) >> 1;
-	ybr = (ybr + y3) >> 1;
-	ytl = (((((ybl + ytl) >> 1) + ytl) >> 1) + ytl) >> 1;
-	ytr = (((((ybr + ytr) >> 1) + ytr) >> 1) + ytr) >> 1;
-	_gfx->drawLine(xx1, ybl, xx1, ytl, dark);
-	_gfx->drawLine(xx1, ytl, xx2, ytr, dark);
-	_gfx->drawLine(xx2, ytr, xx2, ybr, dark);
-	_gfx->drawLine(xx2, ybr, xx1, ybl, dark);
-	_gfx->drawLine(xc, ybc, xc, (ytl + ytr) >> 1, dark);
-}
-
-void ColonyEngine::drawColor(const uint8 *map, int left[4], int right[4]) {
-	int xl = left[0];
-	int xr = right[0];
-	int yl[5], yr[5];
-	yl[0] = left[1];
-	yr[0] = right[1];
-	yl[4] = _height - yl[0];
-	yr[4] = _height - yr[0];
-	yl[2] = (yl[0] + yl[4]) >> 1;
-	yr[2] = (yr[0] + yr[4]) >> 1;
-	yl[1] = (yl[0] + yl[2]) >> 1;
-	yl[3] = (yl[2] + yl[4]) >> 1;
-	yr[1] = (yr[0] + yr[2]) >> 1;
-	yr[3] = (yr[2] + yr[4]) >> 1;
-
-	if (map[1] || map[2] || map[3] || map[4]) {
-		for (int i = 1; i <= 3; i++) {
-			uint32 c = 120 + map[i] * 20;
-			_gfx->drawLine(xl, yl[i], xr, yr[i], c);
-		}
-	} else {
-		uint32 c = 100 + (_level * 15);
-		_gfx->drawLine(xl, yl[1], xr, yr[1], c);
-		_gfx->drawLine(xl, yl[2], xr, yr[2], c);
-		_gfx->drawLine(xl, yl[3], xr, yr[3], c);
-	}
-}
-
-void ColonyEngine::split7(int arr[7], int x1, int x2) const {
-	arr[3] = (x1 + x2) >> 1;
-	arr[1] = (x1 + arr[3]) >> 1;
-	arr[0] = (x1 + arr[1]) >> 1;
-	arr[2] = (arr[1] + arr[3]) >> 1;
-	arr[5] = (arr[3] + x2) >> 1;
-	arr[6] = (arr[5] + x2) >> 1;
-	arr[4] = (arr[3] + arr[5]) >> 1;
-}
-
-void ColonyEngine::split7x7(int left[4], int right[4], int lr[7], int ud[7][7]) const {
-	int leftX, rightX, leftY, rightY;
-	int lud[7], rud[7];
-	if (right[0] < left[0]) {
-		rightX = left[0];
-		leftX = right[0];
-		rightY = left[1];
-		leftY = right[1];
-	} else {
-		leftX = left[0];
-		rightX = right[0];
-		leftY = left[1];
-		rightY = right[1];
-	}
-	split7(lr, leftX, rightX);
-	if (_flip) {
-		split7(lud, leftY, _height - leftY);
-		split7(rud, rightY, _height - rightY);
-	} else {
-		split7(lud, _height - leftY, leftY);
-		split7(rud, _height - rightY, rightY);
-	}
-	for (int i = 0; i < 7; i++)
-		split7(ud[i], lud[i], rud[i]);
-}
-
-bool ColonyEngine::projectWorld(int worldX, int worldY, int &screenX, int &depth) const {
-	long x = worldX - _me.xloc;
-	long y = worldY - _me.yloc;
-	long tsin = _cost[_me.look];
-	long tcos = _sint[_me.look];
-	long xx = (x * tcos - y * tsin) >> 7;
-	long yy = (x * tsin + y * tcos) >> 7;
-
-	if (yy <= 16)
-		return false;
-	if (yy >= 11585)
-		yy = 11584;
-
-	screenX = _centerX + (int)(((int64)xx * 256) / yy);
-	depth = (int)yy;
-	return true;
-}
-
 uint32 ColonyEngine::objectColor(int type) const {
 	switch (type) {
 	case 21: // DESK
@@ -1564,214 +407,6 @@ uint32 ColonyEngine::objectColor(int type) const {
 	}
 }
 
-bool ColonyEngine::isSurfaceClockwise(const ProjectedPrismPart &part, const int surface[8]) const {
-	const int n = surface[1];
-	for (int i = 2; i < n; i++) {
-		const int ia = surface[i];
-		const int ib = surface[i + 1];
-		const int ic = surface[i + 2];
-		if (ia < 0 || ia >= part.pointCount || ib < 0 || ib >= part.pointCount || ic < 0 || ic >= part.pointCount)
-			continue;
-		const long dx = part.x[ia] - part.x[ib];
-		const long dy = part.y[ia] - part.y[ib];
-		const long dxp = part.x[ic] - part.x[ib];
-		const long dyp = part.y[ic] - part.y[ib];
-		if (dx < 0) {
-			if (dy == 0) {
-				if (dyp > 0)
-					return false;
-				if (dyp < 0)
-					return true;
-			} else {
-				const long b = dy * dxp - dx * dyp;
-				if (b > 0)
-					return false;
-				if (b < 0)
-					return true;
-			}
-		} else if (dx > 0) {
-			if (dy == 0) {
-				if (dyp < 0)
-					return false;
-				if (dyp > 0)
-					return true;
-			} else {
-				const long b = dx * dyp - dy * dxp;
-				if (b < 0)
-					return false;
-				if (b > 0)
-					return true;
-			}
-		} else {
-			if (dy < 0) {
-				if (dxp > 0)
-					return true;
-				if (dxp < 0)
-					return false;
-			}
-			if (dy > 0) {
-				if (dxp < 0)
-					return true;
-				if (dxp > 0)
-					return false;
-			}
-		}
-	}
-	return false;
-}
-
-bool ColonyEngine::projectPrismPart(const Thing &obj, const PrismPartDef &part, bool useLook, ProjectedPrismPart &out) const {
-	out.pointCount = CLIP<int>(part.pointCount, 0, ProjectedPrismPart::kMaxPoints);
-	for (int i = 0; i < ProjectedPrismPart::kMaxSurfaces; i++)
-		out.vsurface[i] = false;
-	out.visible = false;
-	if (out.pointCount <= 0 || !part.points || !part.surfaces)
-		return false;
-
-	const uint8 ang = useLook ? obj.where.look : obj.where.ang;
-	const long rotCos = _cost[ang];
-	const long rotSin = _sint[ang];
-	const long viewSin = _cost[_me.look];
-	const long viewCos = _sint[_me.look];
-
-	int minX = 32000;
-	int maxX = -32000;
-	int minY = 32000;
-	int maxY = -32000;
-	// DOS InitObj() applies: Robot[i][j].pnt[k][2] -= Floor, with Floor == 160.
-	// We keep source geometry unmodified and apply the same offset at projection time.
-	static const int kFloorShift = 160;
-	for (int i = 0; i < out.pointCount; i++) {
-		const int px = part.points[i][0];
-		const int py = part.points[i][1];
-		const int pz = part.points[i][2];
-		const long rx = ((long)px * rotCos - (long)py * rotSin) >> 7;
-		const long ry = ((long)px * rotSin + (long)py * rotCos) >> 7;
-		const long worldX = rx + obj.where.xloc;
-		const long worldY = ry + obj.where.yloc;
-
-		const long tx = worldX - _me.xloc;
-		const long ty = worldY - _me.yloc;
-		const long xx = (tx * viewCos - ty * viewSin) >> 7;
-		long yy = (tx * viewSin + ty * viewCos) >> 7;
-		if (yy <= 16)
-			yy = 16;
-
-		out.x[i] = _centerX + (int)(((int64)xx * 256) / yy);
-		out.depth[i] = (int)yy;
-		const long zrel = (long)pz - kFloorShift;
-		out.y[i] = _centerY - (int)(((int64)zrel * 256) / yy);
-		minX = MIN(minX, out.x[i]);
-		maxX = MAX(maxX, out.x[i]);
-		minY = MIN(minY, out.y[i]);
-		maxY = MAX(maxY, out.y[i]);
-	}
-
-	out.visible = !(maxX < _screenR.left || minX >= _screenR.right || maxY < _screenR.top || minY >= _screenR.bottom);
-	if (!out.visible)
-		return false;
-
-	const int surfCount = CLIP<int>(part.surfaceCount, 0, ProjectedPrismPart::kMaxSurfaces);
-	for (int i = 0; i < surfCount; i++)
-		out.vsurface[i] = isSurfaceClockwise(out, part.surfaces[i]);
-	return true;
-}
-
-bool ColonyEngine::clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const {
-	if (clip.left >= clip.right || clip.top >= clip.bottom)
-		return false;
-	const int l = clip.left;
-	const int r = clip.right - 1;
-	const int t = clip.top;
-	const int b = clip.bottom - 1;
-	auto outCode = [&](int x, int y) {
-		int code = 0;
-		if (x < l)
-			code |= 1;
-		else if (x > r)
-			code |= 2;
-		if (y < t)
-			code |= 4;
-		else if (y > b)
-			code |= 8;
-		return code;
-	};
-
-	int c1 = outCode(x1, y1);
-	int c2 = outCode(x2, y2);
-	while (true) {
-		if ((c1 | c2) == 0)
-			return true;
-		if (c1 & c2)
-			return false;
-
-		const int cOut = c1 ? c1 : c2;
-		int x = 0;
-		int y = 0;
-		if (cOut & 8) {
-			if (y2 == y1)
-				return false;
-			x = x1 + (x2 - x1) * (b - y1) / (y2 - y1);
-			y = b;
-		} else if (cOut & 4) {
-			if (y2 == y1)
-				return false;
-			x = x1 + (x2 - x1) * (t - y1) / (y2 - y1);
-			y = t;
-		} else if (cOut & 2) {
-			if (x2 == x1)
-				return false;
-			y = y1 + (y2 - y1) * (r - x1) / (x2 - x1);
-			x = r;
-		} else {
-			if (x2 == x1)
-				return false;
-			y = y1 + (y2 - y1) * (l - x1) / (x2 - x1);
-			x = l;
-		}
-
-		if (cOut == c1) {
-			x1 = x;
-			y1 = y;
-			c1 = outCode(x1, y1);
-		} else {
-			x2 = x;
-			y2 = y;
-			c2 = outCode(x2, y2);
-		}
-	}
-}
-
-void ColonyEngine::drawProjectedPrism(const ProjectedPrismPart &part, const PrismPartDef &def, int force, uint32 color, const Common::Rect &clip) {
-	const int surfCount = CLIP<int>(def.surfaceCount, 0, ProjectedPrismPart::kMaxSurfaces);
-	for (int i = 0; i < surfCount; i++) {
-		if (!(part.vsurface[i] || force))
-			continue;
-		const int n = def.surfaces[i][1];
-		if (n < 2)
-			continue;
-		
-		int px[ProjectedPrismPart::kMaxPoints];
-		int py[ProjectedPrismPart::kMaxPoints];
-		int count = 0;
-
-		for (int j = 0; j < n; j++) {
-			const int cur = def.surfaces[i][j + 2];
-			if (cur < 0 || cur >= part.pointCount)
-				continue;
-			px[count] = part.x[cur];
-			py[count] = part.y[cur];
-			count++;
-		}
-		
-		if (count >= 3) {
-			_gfx->drawPolygon(px, py, count, color);
-		} else if (count == 2) {
-			_gfx->drawLine(px[0], py[0], px[1], py[1], color);
-		}
-	}
-}
-
 void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, uint32 color) {
 	const uint8 ang = useLook ? obj.where.look : obj.where.ang;
 	const long rotCos = _cost[ang];
@@ -1781,9 +416,9 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 		const int n = def.surfaces[i][1];
 		if (n < 2) continue;
 
-		float px[ProjectedPrismPart::kMaxPoints];
-		float py[ProjectedPrismPart::kMaxPoints];
-		float pz[ProjectedPrismPart::kMaxPoints];
+		float px[8];
+		float py[8];
+		float pz[8];
 		int count = 0;
 
 		for (int j = 0; j < n; j++) {
@@ -1800,7 +435,7 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 			
 			px[count] = (float)(rx + obj.where.xloc);
 			py[count] = (float)(ry + obj.where.yloc);
-			pz[count] = (float)oz; //(float)(oz - 160); // Floor is at -160
+			pz[count] = (float)oz;
 			count++;
 		}
 
@@ -1810,338 +445,20 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 	}
 }
 
-bool ColonyEngine::drawStaticObjectPrisms(const Thing &obj, uint32 baseColor) {
-	const int clipLeft = MAX<int>((int)obj.clip.left, (int)_screenR.left);
-	const int clipTop = MAX<int>((int)obj.clip.top, (int)_screenR.top);
-	const int clipRight = MIN<int>((int)obj.clip.right, (int)_screenR.right);
-	const int clipBottom = MIN<int>((int)obj.clip.bottom, (int)_screenR.bottom);
-	if (clipLeft >= clipRight || clipTop >= clipBottom)
-		return false;
-	Common::Rect drawClip(clipLeft, clipTop, clipRight, clipBottom);
-
-	auto tint = [](uint32 base, int delta) -> uint32 {
-		return (uint32)CLIP<int>((int)base + delta, 0, 255);
-	};
- 
-	if (_gfx->isAccelerated()) {
-		return drawStaticObjectPrisms3D(obj, baseColor);
-	}
-
-	ProjectedPrismPart p[10];
-	switch (obj.type) {
-	case kObjConsole:
-		if (!projectPrismPart(obj, kConsolePart, false, p[0]) || !p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kConsolePart, 0, tint(baseColor, 0), drawClip);
-		return true;
-	case kObjCChair:
-		for (int i = 0; i < 5; i++)
-			projectPrismPart(obj, kCChairParts[i], false, p[i]);
-		if (p[4].visible)
-			drawProjectedPrism(p[4], kCChairParts[4], 0, tint(baseColor, -10), drawClip);
-		if (!p[0].visible)
-			return p[4].visible;
-		drawProjectedPrism(p[0], kCChairParts[0], 0, tint(baseColor, 0), drawClip);
-		if (p[3].vsurface[0]) {
-			drawProjectedPrism(p[3], kCChairParts[3], 1, tint(baseColor, 0), drawClip);
-			if (p[1].vsurface[0]) {
-				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
-				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
-			} else {
-				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
-				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
-			}
-		} else {
-			if (p[1].vsurface[0]) {
-				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
-				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
-			} else {
-				drawProjectedPrism(p[2], kCChairParts[2], 1, tint(baseColor, 0), drawClip);
-				drawProjectedPrism(p[1], kCChairParts[1], 1, tint(baseColor, 0), drawClip);
-			}
-			drawProjectedPrism(p[3], kCChairParts[3], 1, tint(baseColor, 0), drawClip);
-		}
-		return true;
-	case kObjPlant: {
-		for (int i = 0; i < 4; i++)
-			projectPrismPart(obj, kPlantParts[i], false, p[i]);
-		// Pot must be visible for object to appear
-		if (!p[0].visible)
-			return false;
-		// Draw pot, stem and leaves (pot darker, foliage lighter)
-		drawProjectedPrism(p[0], kPlantParts[0], 0, tint(baseColor, -30), drawClip);
-		if (p[1].visible)
-			drawProjectedPrism(p[1], kPlantParts[1], 0, tint(baseColor, 10), drawClip);
-		if (p[2].vsurface[0])
-			drawProjectedPrism(p[2], kPlantParts[2], 0, tint(baseColor, 30), drawClip);
-		if (p[3].vsurface[0])
-			drawProjectedPrism(p[3], kPlantParts[3], 0, tint(baseColor, 30), drawClip);
-		return true;
-	}
-	case kObjCouch:
-	case kObjChair: {
-		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
-		for (int i = 0; i < 4; i++)
-			projectPrismPart(obj, parts[i], false, p[i]);
-		if (!p[0].visible)
-			return false;
-		if (p[2].vsurface[1] && p[3].vsurface[2]) {
-			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
-			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
-			if (p[0].vsurface[3]) {
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-			} else {
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-			}
-		} else if (p[3].vsurface[1]) {
-			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
-			if (p[0].vsurface[3]) {
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-			} else {
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-			}
-			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
-		} else {
-			drawProjectedPrism(p[3], parts[3], 0, tint(baseColor, -5), drawClip);
-			if (p[0].vsurface[3]) {
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-			} else {
-				drawProjectedPrism(p[0], parts[0], 0, tint(baseColor, 5), drawClip);
-				drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -5), drawClip);
-			}
-			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, -5), drawClip);
-		}
-		return true;
-	}
-	case kObjTV:
-		projectPrismPart(obj, kTVParts[0], false, p[0]);
-		projectPrismPart(obj, kTVParts[1], false, p[1]);
-		if (!p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kTVParts[0], 0, tint(baseColor, 0), drawClip);
-		if (p[1].vsurface[0])
-			drawProjectedPrism(p[1], kTVParts[1], 0, tint(baseColor, 35), drawClip);
-		return true;
-	case kObjDrawer:
-		projectPrismPart(obj, kDrawerParts[0], false, p[0]);
-		projectPrismPart(obj, kDrawerParts[1], false, p[1]);
-		if (!p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kDrawerParts[0], 0, tint(baseColor, 0), drawClip);
-		drawProjectedPrism(p[1], kDrawerParts[1], 1, tint(baseColor, 30), drawClip);
-		return true;
-case kObjFWall:
-		// Simple flat wall part (fallback handled by prism below)
-		if (!projectPrismPart(obj, kCWallParts[0], false, p[0]) || !p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kCWallParts[0], 0, tint(baseColor, -10), drawClip);
-		return true;
-case kObjCWall:
-		// Corner wall (CWALL) — mirror of DOS CWallparts
-		if (!projectPrismPart(obj, kCWallParts[0], false, p[0]) || !p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kCWallParts[0], 0, tint(baseColor, -5), drawClip);
-		return true;
-	case kObjScreen:
-		if (!projectPrismPart(obj, kScreenPart, false, p[0]) || !p[0].visible)
-			return false;
-		drawProjectedPrism(p[0], kScreenPart, 0, tint(baseColor, 0), drawClip);
-		return true;
-	case kObjTable:
-		projectPrismPart(obj, kTableParts[0], false, p[0]);
-		projectPrismPart(obj, kTableParts[1], false, p[1]);
-		if (!p[1].visible)
-			return false;
-		drawProjectedPrism(p[1], kTableParts[1], 0, tint(baseColor, -10), drawClip);
-		drawProjectedPrism(p[0], kTableParts[0], 0, tint(baseColor, 20), drawClip);
-		return true;
-	case kObjBed:
-	case kObjBBed: {
-		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
-		projectPrismPart(obj, parts[0], false, p[0]);
-		projectPrismPart(obj, parts[1], false, p[1]);
-		projectPrismPart(obj, parts[2], false, p[2]);
-		if (!p[1].visible)
-			return false;
-		if (p[0].vsurface[0]) {
-			drawProjectedPrism(p[0], parts[0], 1, tint(baseColor, 15), drawClip);
-			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, 5), drawClip);
-			drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -10), drawClip);
-		} else {
-			drawProjectedPrism(p[1], parts[1], 0, tint(baseColor, -10), drawClip);
-			drawProjectedPrism(p[2], parts[2], 0, tint(baseColor, 5), drawClip);
-			drawProjectedPrism(p[0], parts[0], 1, tint(baseColor, 15), drawClip);
-		}
-		return true;
-	}
-	case kObjDesk:
-		for (int i = 0; i < 10; i++)
-			projectPrismPart(obj, kDeskParts[i], false, p[i]);
-		if (!p[0].visible)
-			return false;
-		if (p[6].vsurface[1]) {
-			if (p[1].vsurface[3] || p[2].vsurface[3]) {
-				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
-				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
-			} else {
-				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
-				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
-			}
-			drawProjectedPrism(p[0], kDeskParts[0], 0, tint(baseColor, 15), drawClip);
-			drawProjectedPrism(p[7], kDeskParts[7], 0, tint(baseColor, 25), drawClip);
-			drawProjectedPrism(p[8], kDeskParts[8], 0, tint(baseColor, 25), drawClip);
-			if (p[9].vsurface[0])
-				drawProjectedPrism(p[9], kDeskParts[9], 0, tint(baseColor, 40), drawClip);
-			if (p[4].vsurface[0]) {
-				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
-			} else {
-				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
-			}
-		} else {
-			if (p[4].vsurface[0]) {
-				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
-			} else {
-				drawProjectedPrism(p[5], kDeskParts[5], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[3], kDeskParts[3], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[6], kDeskParts[6], 0, tint(baseColor, -5), drawClip);
-				drawProjectedPrism(p[4], kDeskParts[4], 0, tint(baseColor, -5), drawClip);
-			}
-			if (p[1].vsurface[3] || p[2].vsurface[3]) {
-				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
-				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
-			} else {
-				drawProjectedPrism(p[2], kDeskParts[2], 0, tint(baseColor, -15), drawClip);
-				drawProjectedPrism(p[1], kDeskParts[1], 0, tint(baseColor, -15), drawClip);
-			}
-			drawProjectedPrism(p[0], kDeskParts[0], 0, tint(baseColor, 15), drawClip);
-			drawProjectedPrism(p[7], kDeskParts[7], 0, tint(baseColor, 25), drawClip);
-			drawProjectedPrism(p[8], kDeskParts[8], 0, tint(baseColor, 25), drawClip);
-			if (p[9].vsurface[0])
-				drawProjectedPrism(p[9], kDeskParts[9], 0, tint(baseColor, 40), drawClip);
-		}
-		return true;
-	default:
-		return false;
-	}
-}
-
-void ColonyEngine::drawStaticObjectFallback(const Thing &obj, uint32 color, int depth, int sx) {
-	int scale = _rtable[depth];
-	int baseY = _height - (_centerY - scale);
-	int h = CLIP<int>(scale, 4, 96);
-	int w = CLIP<int>(h >> 1, 3, 64);
-	Common::Rect body(sx - w, baseY - h, sx + w, baseY);
-	const int bodyLeft = (int)body.left;
-	const int bodyTop = (int)body.top;
-	const int bodyRight = (int)body.right;
-	const int bodyBottom = (int)body.bottom;
-	const int clipLeft = MAX(bodyLeft, MAX((int)obj.clip.left, (int)_screenR.left));
-	const int clipTop = MAX(bodyTop, MAX((int)obj.clip.top, (int)_screenR.top));
-	const int clipRight = MIN(bodyRight, MIN((int)obj.clip.right, (int)_screenR.right));
-	const int clipBottom = MIN(bodyBottom, MIN((int)obj.clip.bottom, (int)_screenR.bottom));
-	if (clipLeft >= clipRight || clipTop >= clipBottom)
-		return;
-	Common::Rect clipped(clipLeft, clipTop, clipRight, clipBottom);
-	_gfx->drawRect(clipped, color);
-	_gfx->drawLine(clipped.left, clipped.bottom - 1, clipped.right - 1, clipped.bottom - 1, color);
-}
-
 void ColonyEngine::drawStaticObjects() {
-	if (_gfx->isAccelerated()) {
-		for (uint i = 0; i < _objects.size(); i++) {
-			const Thing &obj = _objects[i];
-			if (!obj.alive)
-				continue;
-			uint32 color = objectColor(obj.type);
-			drawStaticObjectPrisms3D(obj, color);
-		}
-		return;
-	}
-
-	struct DrawCmd {
-		int depth;
-		int index;
-		int screenX;
-	};
-
-	Common::Array<DrawCmd> drawList;
-	drawList.reserve(_objects.size());
-
 	for (uint i = 0; i < _objects.size(); i++) {
 		const Thing &obj = _objects[i];
-		if (!obj.alive || !obj.visible)
+		if (!obj.alive)
 			continue;
-		int sx, depth;
-		if (!projectWorld(obj.where.xloc, obj.where.yloc, sx, depth))
-			continue;
-		if (depth > 11000)
-			continue;
-		DrawCmd cmd;
-		cmd.depth = depth;
-		cmd.index = (int)i;
-		cmd.screenX = sx;
-		drawList.push_back(cmd);
-	}
-
-	Common::sort(drawList.begin(), drawList.end(), [](const DrawCmd &a, const DrawCmd &b) {
-		return a.depth > b.depth; // far to near
-	});
-
-	for (uint i = 0; i < drawList.size(); i++) {
-		const DrawCmd &d = drawList[i];
-		const Thing &obj = _objects[d.index];
-		const uint32 color = objectColor(obj.type);
-		if (!drawStaticObjectPrisms(obj, color))
-			drawStaticObjectFallback(obj, color, d.depth, d.screenX);
+		uint32 color = objectColor(obj.type);
+		drawStaticObjectPrisms3D(obj, color);
 	}
 }
 
-void ColonyEngine::setRobot(int l, int r, int num) {
-	if (num <= 0 || num > (int)_objects.size())
-		return;
-	if (l < _screenR.left)
-		l = _screenR.left;
-	if (r > _screenR.right)
-		r = _screenR.right;
-	if (l >= r)
-		return;
-	const int clipLeft = l + 1;
-	const int clipRight = r - 2;
-	if (clipLeft >= clipRight)
-		return;
-
-	Thing &obj = _objects[num - 1];
-	if (!obj.alive)
-		return;
-	obj.visible = 1;
-	obj.clip.left = clipLeft;
-	obj.clip.right = clipRight;
-	obj.clip.top = _clip.top;
-	obj.clip.bottom = _clip.bottom;
-}
- 
-// DOS object geometry constants
-
-
 void ColonyEngine::renderCorridor3D() {
 	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
  
-	// Draw large grid/outline for floor and ceiling if needed, but for wireframe match, we can just clear to black
-	// We still draw a large black floor quad to ensure depth testing works against "empty ground"
+	// Draw large black floor and ceiling quads
 	_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.1f, 
 	                100000.0f, -100000.0f, -160.1f, 
 	                100000.0f, 100000.0f, -160.1f, 
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index dc70410315f..a58e5e6b414 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -33,36 +33,40 @@ class Renderer {
 public:
 	virtual ~Renderer() {}
 
+	// 2D drawing primitives (used for UI overlay, dashboard, etc.)
 	virtual void clear(uint32 color) = 0;
 	virtual void drawLine(int x1, int y1, int x2, int y2, uint32 color) = 0;
 	virtual void drawRect(const Common::Rect &rect, uint32 color) = 0;
 	virtual void fillRect(const Common::Rect &rect, uint32 color) = 0;
-	
-	virtual void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) = 0;
+	virtual void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align = Graphics::kTextAlignLeft) = 0;
 	virtual void scroll(int dx, int dy, uint32 background) = 0;
-	virtual bool isAccelerated() const = 0;
 	virtual void drawEllipse(int x, int y, int rx, int ry, uint32 color) = 0;
 	virtual void fillEllipse(int x, int y, int rx, int ry, uint32 color) = 0;
 	virtual void fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) = 0;
 	virtual void setPixel(int x, int y, uint32 color) = 0;
+	virtual void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) = 0;
+	virtual void drawPolygon(const int *x, const int *y, int count, uint32 color) = 0;
 
 	virtual void setPalette(const byte *palette, uint start, uint count) = 0;
 
+	// 3D scene rendering
 	virtual void begin3D(int camX, int camY, int camZ, int angle, int angleY, const Common::Rect &viewport) = 0;
 	virtual void draw3DWall(int x1, int y1, int x2, int y2, uint32 color) = 0;
 	virtual void draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) = 0;
 	virtual void draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) = 0;
 	virtual void end3D() = 0;
 
-	virtual void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) = 0;
-	virtual void drawPolygon(const int *x, const int *y, int count, uint32 color) = 0;
-
+	// Buffer management
 	virtual void copyToScreen() = 0;
 	virtual void setWireframe(bool enable) = 0;
+
+	// Convenience color accessors
+	uint32 white() const { return 255; }
+	uint32 black() const { return 0; }
 };
 
-Renderer *createSoftwareRenderer(OSystem *system, int width, int height);
-Renderer *createOpenGLRenderer(OSystem *system, int width, int height);
+// Factory function (follows Freescape pattern: picks best available renderer)
+Renderer *createRenderer(OSystem *system, int width, int height);
 
 } // End of namespace Colony
 
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 3fa7a338f35..5cee3064b61 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -42,7 +42,6 @@ public:
 	void fillRect(const Common::Rect &rect, uint32 color) override;
 	void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) override;
 	void scroll(int dx, int dy, uint32 background) override;
-	bool isAccelerated() const override { return true; }
 	void drawEllipse(int x, int y, int rx, int ry, uint32 color) override;
 	void fillEllipse(int x, int y, int rx, int ry, uint32 color) override;
 	void fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) override;
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 62f5c40eeb7..62a6313879b 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -280,7 +280,6 @@ int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
 int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 	const int xind2 = xnew >> 8;
 	const int yind2 = ynew >> 8;
-	_change = true;
 
 	auto occupied = [&]() -> int {
 		return occupiedObjectAt(xind2, yind2, pobject);
@@ -534,8 +533,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 			_robotArray[oldX][oldY] = 0;
 		if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 			_robotArray[_me.xindex][_me.yindex] = MENUM;
-		_change = true;
-		debug("CCommand: TELEPORT to L%d (%d,%d)", targetLevel, targetX, targetY);
+			debug("CCommand: TELEPORT to L%d (%d,%d)", targetLevel, targetX, targetY);
 		break;
 	}
 	case kObjDrawer:
@@ -596,4 +594,65 @@ void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
 		_robotArray[_me.xindex][_me.yindex] = MENUM;
 }
 
+bool ColonyEngine::clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const {
+	if (clip.left >= clip.right || clip.top >= clip.bottom)
+		return false;
+	const int l = clip.left;
+	const int r = clip.right - 1;
+	const int t = clip.top;
+	const int b = clip.bottom - 1;
+	auto outCode = [&](int x, int y) {
+		int code = 0;
+		if (x < l)
+			code |= 1;
+		else if (x > r)
+			code |= 2;
+		if (y < t)
+			code |= 4;
+		else if (y > b)
+			code |= 8;
+		return code;
+	};
+
+	int c1 = outCode(x1, y1);
+	int c2 = outCode(x2, y2);
+	while (true) {
+		if ((c1 | c2) == 0)
+			return true;
+		if (c1 & c2)
+			return false;
+
+		const int cOut = c1 ? c1 : c2;
+		int x = 0;
+		int y = 0;
+		if (cOut & 8) {
+			if (y2 == y1) return false;
+			x = x1 + (x2 - x1) * (b - y1) / (y2 - y1);
+			y = b;
+		} else if (cOut & 4) {
+			if (y2 == y1) return false;
+			x = x1 + (x2 - x1) * (t - y1) / (y2 - y1);
+			y = t;
+		} else if (cOut & 2) {
+			if (x2 == x1) return false;
+			y = y1 + (y2 - y1) * (r - x1) / (x2 - x1);
+			x = r;
+		} else {
+			if (x2 == x1) return false;
+			y = y1 + (y2 - y1) * (l - x1) / (x2 - x1);
+			x = l;
+		}
+
+		if (cOut == c1) {
+			x1 = x;
+			y1 = y;
+			c1 = outCode(x1, y1);
+		} else {
+			x2 = x;
+			y2 = y;
+			c2 = outCode(x2, y2);
+		}
+	}
+}
+
 } // End of namespace Colony


Commit: 8212ca241fd9cdf074bc4209aea90b52c4d32c66
    https://github.com/scummvm/scummvm/commit/8212ca241fd9cdf074bc4209aea90b52c4d32c66
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:07+02:00

Commit Message:
COLONY: fixed z-position of objects

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index cbdba71fe38..f3ee020ec6d 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -294,8 +294,8 @@ static const Colony::ColonyEngine::PrismPartDef kDrawerParts[2] = {
 	{4, kMirrorPts, 1, kMirrorSurf}
 };
 static const int kCWallPts[8][3] = {
-	{-128, 128, -160}, {0, 112, -160}, {112, 0, -160}, {128, -128, -160},
-	{-128, 128, 160},  {0, 112, 160},  {112, 0, 160},  {128, -128, 160}
+	{-128, 128, 0}, {0, 112, 0}, {112, 0, 0}, {128, -128, 0},
+	{-128, 128, 320},  {0, 112, 320},  {112, 0, 320},  {128, -128, 320}
 };
 static const int kCWallSurf[3][8] = {
 	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0}
@@ -435,7 +435,7 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 			
 			px[count] = (float)(rx + obj.where.xloc);
 			py[count] = (float)(ry + obj.where.yloc);
-			pz[count] = (float)oz;
+			pz[count] = (float)(oz - 160); // Shift from floor-relative (z=0) to world (z=-160)
 			count++;
 		}
 


Commit: fca94c9d8353b705b0f990716f31fc88d1552a04
    https://github.com/scummvm/scummvm/commit/fca94c9d8353b705b0f990716f31fc88d1552a04
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:08+02:00

Commit Message:
COLONY: removed old sin tables

Changed paths:
    engines/colony/colony.cpp
    engines/colony/render.cpp
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 3dca1d436a4..49716d12a9d 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -39,40 +39,7 @@
 
 namespace Colony {
 
-static const int16 g_sintTable[256] = {
-	90,  93,  95,  97,  99, 101, 103, 105,
-	106, 108, 110, 111, 113, 114, 116, 117,
-	118, 119, 121, 122, 122, 123, 124, 125,
-	126, 126, 127, 127, 127, 128, 128, 128,
-	128, 128, 128, 128, 127, 127, 127, 126,
-	126, 125, 124, 123, 122, 122, 121, 119,
-	118, 117, 116, 114, 113, 111, 110, 108,
-	106, 105, 103, 101,  99,  97,  95,  93,
-	90,  88,  86,  84,  81,  79,  76,  74,
-	71,  68,  66,  63,  60,  56,  54,  52,
-	49,  46,  43,  40,  37,  34,  31,  28,
-	25,  23,  19,  16,  13,   9,   6,   3,
-	0,  -3,  -6,  -9, -13, -16, -19, -23,
-	-25, -28, -31, -34, -37, -40, -43, -46,
-	-49, -52, -54, -56, -60, -63, -66, -68,
-	-71, -74, -76, -79, -81, -84, -86, -88,
-	-88, -90, -93, -95, -97, -99,-101,-103,
-	-105,-106,-108,-110,-111,-113,-114,-116,
-	-117,-118,-119,-121,-122,-122,-123,-124,
-	-125,-126,-126,-127,-127,-127,-128,-128,
-	-128,-128,-128,-128,-127,-127,-127,-126,
-	-126,-125,-124,-123,-122,-122,-121,-119,
-	-118,-117,-116,-114,-113,-111,-110,-108,
-	-106,-105,-103,-101, -99, -97, -95, -93,
-	-90, -88, -86, -84, -81, -79, -76, -74,
-	-71, -68, -66, -63, -60, -56, -54, -52,
-	-49, -46, -43, -40, -37, -34, -31, -28,
-	-25, -23, -19, -16, -13,  -9,  -6,  -3,
-	0,   3,   6,   9,  13,  16,  19,  23,
-	25,  28,  31,  34,  37,  40,  43,  46,
-	49,  52,  54,  56,  60,  63,  66,  68,
-	71,  74,  76,  79,  81,  84,  86,  88
-};
+
 
 ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd) {
 	_level = 0;
@@ -200,9 +167,11 @@ void ColonyEngine::loadMap(int mnum) {
 }
 
 void ColonyEngine::initTrig() {
+	// Compute standard sin/cos lookup tables (256 steps = full circle, scaled by 128)
 	for (int i = 0; i < 256; i++) {
-		_sint[i] = g_sintTable[i];
-		_cost[i] = g_sintTable[(i + 64) & 0xFF];
+		float rad = (float)i * 2.0f * M_PI / 256.0f;
+		_sint[i] = (int)roundf(128.0f * sinf(rad));
+		_cost[i] = (int)roundf(128.0f * cosf(rad));
 	}
 }
 
@@ -287,9 +256,12 @@ Common::Error ColonyEngine::run() {
 				if (event.relMouse.y != 0) {
 					_me.lookY = (int8)CLIP<int>((int)_me.lookY - (event.relMouse.y / 2), -64, 64);
 				}
+				// Warp back to center and purge remaining mouse events
+				// to prevent the warp from generating phantom deltas (Freescape pattern)
+				_system->warpMouse(_centerX, _centerY);
+				_system->getEventManager()->purgeMouseEvents();
 			}
 		}
-		_system->warpMouse(_centerX, _centerY);
 
 		_gfx->clear(_gfx->black());
 		
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index f3ee020ec6d..2f32e342f55 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -408,7 +408,9 @@ uint32 ColonyEngine::objectColor(int type) const {
 }
 
 void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, uint32 color) {
-	const uint8 ang = useLook ? obj.where.look : obj.where.ang;
+	// +32 compensates for the original sine table's 45° phase offset.
+	// Object angles from game data were stored assuming that offset.
+	const uint8 ang = (useLook ? obj.where.look : obj.where.ang) + 32;
 	const long rotCos = _cost[ang];
 	const long rotSin = _sint[ang];
  
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 5cee3064b61..52ac22aa1b6 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -187,11 +187,16 @@ void OpenGLRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY
 	glMatrixMode(GL_PROJECTION);
 	glLoadIdentity();
 	
-	// Optical center is at viewport.width()/2 and viewport.height()/2
-	// xmax = (width/2) / 256.0
-	float xmax = (viewport.width() / 2.0f) / 256.0f;
-	float ymax = (viewport.height() / 2.0f) / 256.0f;
-	glFrustum(-xmax, xmax, -ymax, ymax, 1.0, 20000.0);
+	// Use a proper FOV-based perspective projection (like Freescape).
+	// A fixed vertical FOV ensures the forward direction stays centered
+	// regardless of viewport dimensions (e.g. when dashboard narrows it).
+	float aspectRatio = (float)viewport.width() / (float)viewport.height();
+	float fov = 75.0f; // vertical FOV in degrees
+	float nearClip = 1.0f;
+	float farClip = 20000.0f;
+	float ymax = nearClip * tanf(fov * M_PI / 360.0f);
+	float xmax = ymax * aspectRatio;
+	glFrustum(-xmax, xmax, -ymax, ymax, nearClip, farClip);
  
 	glMatrixMode(GL_MODELVIEW);
 	glLoadIdentity();


Commit: 0ad4a21d8e246d3eecef166937770f2910ac5259
    https://github.com/scummvm/scummvm/commit/0ad4a21d8e246d3eecef166937770f2910ac5259
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:08+02:00

Commit Message:
COLONY: fixed movement

Changed paths:
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 52ac22aa1b6..a1aac5dd117 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -208,7 +208,9 @@ void OpenGLRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY
 	glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
  
 	// 3. Horizontal rotation (Yaw) around World Z axis (Up)
-	glRotatef(-(float)angle * 360.0f / 256.0f, 0.0f, 0.0f, 1.0f);
+	// +90° compensates for the -90° X rotation above: without it the camera
+	// looks along +Y at angle=0, but movement cos/sin uses +X as angle=0.
+	glRotatef(-(float)angle * 360.0f / 256.0f + 90.0f, 0.0f, 0.0f, 1.0f);
 	
 	// 4. Translate camera
 	glTranslatef(-(float)camX, -(float)camY, -(float)camZ);


Commit: 09442d12d0279e654e1f2cdb4ce52098cf285c73
    https://github.com/scummvm/scummvm/commit/09442d12d0279e654e1f2cdb4ce52098cf285c73
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:08+02:00

Commit Message:
COLONY: wireframe solid mode

Changed paths:
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index a1aac5dd117..90fd2138c87 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -168,7 +168,8 @@ void OpenGLRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY
 	glEnable(GL_DEPTH_TEST);
 	glClear(GL_DEPTH_BUFFER_BIT);
  
-	glPolygonMode(GL_FRONT_AND_BACK, _wireframe ? GL_LINE : GL_FILL);
+	// Always use filled polygons; wireframe mode draws black fill + colored edges
+	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  
 	// Scale viewport coordinates to system pixels
 	float scaleX = (float)_system->getWidth() / _width;
@@ -192,7 +193,7 @@ void OpenGLRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY
 	// regardless of viewport dimensions (e.g. when dashboard narrows it).
 	float aspectRatio = (float)viewport.width() / (float)viewport.height();
 	float fov = 75.0f; // vertical FOV in degrees
-	float nearClip = 1.0f;
+	float nearClip = 10.0f;
 	float farClip = 20000.0f;
 	float ymax = nearClip * tanf(fov * M_PI / 360.0f);
 	float xmax = ymax * aspectRatio;
@@ -221,7 +222,22 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 	float fy1 = y1 * 256.0f;
 	float fx2 = x2 * 256.0f;
 	float fy2 = y2 * 256.0f;
- 
+
+	if (_wireframe) {
+		// Push the black fill away from camera so wireframe edges always win
+		glEnable(GL_POLYGON_OFFSET_FILL);
+		glPolygonOffset(1.0f, 1.0f);
+		useColor(0);
+		glBegin(GL_QUADS);
+		glVertex3f(fx1, fy1, -160.0f);
+		glVertex3f(fx2, fy2, -160.0f);
+		glVertex3f(fx2, fy2, 160.0f);
+		glVertex3f(fx1, fy1, 160.0f);
+		glEnd();
+		glDisable(GL_POLYGON_OFFSET_FILL);
+		// Switch to wireframe for the colored edge pass
+		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+	}
 	useColor(color);
 	glBegin(GL_QUADS);
 	glVertex3f(fx1, fy1, -160.0f);
@@ -229,6 +245,9 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 	glVertex3f(fx2, fy2, 160.0f);
 	glVertex3f(fx1, fy1, 160.0f);
 	glEnd();
+	if (_wireframe) {
+		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+	}
 }
  
 void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) {
@@ -243,13 +262,28 @@ void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2
  
 void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) {
 	if (count < 3) return;
- 
+
+	if (_wireframe) {
+		// Push the black fill away from camera so wireframe edges always win
+		glEnable(GL_POLYGON_OFFSET_FILL);
+		glPolygonOffset(1.0f, 1.0f);
+		useColor(0);
+		glBegin(GL_POLYGON);
+		for (int i = 0; i < count; i++)
+			glVertex3f(x[i], y[i], z[i]);
+		glEnd();
+		glDisable(GL_POLYGON_OFFSET_FILL);
+		// Switch to wireframe for the colored edge pass
+		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+	}
 	useColor(color);
 	glBegin(GL_POLYGON);
-	for (int i = 0; i < count; i++) {
+	for (int i = 0; i < count; i++)
 		glVertex3f(x[i], y[i], z[i]);
-	}
 	glEnd();
+	if (_wireframe) {
+		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+	}
 }
  
 void OpenGLRenderer::end3D() {


Commit: cfb605eceaaca1d8622c5148543e0fd7d0adec52
    https://github.com/scummvm/scummvm/commit/cfb605eceaaca1d8622c5148543e0fd7d0adec52
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:09+02:00

Commit Message:
COLONY: fix object vertex overflow by clamping coordinates to -127..127

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 2f32e342f55..a5a38c97248 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -112,32 +112,32 @@ static const int kArmLeftPts[4][3] = {
 	{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
 };
 static const int kArmRightPts[4][3] = {
-	{40, 115, 90}, {40, 115, 0}, {40, 210, 0}, {40, 210, 90}
+	{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
 };
 static const int kArmSurf[2][8] = {
-	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 3, 3, 0, 2, 0, 0, 0}
+	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kBackPts[4][3] = {
-	{-40, 115, 130}, {40, 115, 130}, {40, 115, 0}, {-40, 115, 0}
+	{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
 };
 static const int kBackSurf[2][8] = {
-	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 3, 3, 0, 2, 0, 0, 0}
+	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kComputerPts[8][3] = {
-	{-50, 60, 100}, {50, 60, 100}, {50, -60, 100}, {-50, -60, 100},
-	{-50, 60, 130}, {50, 60, 130}, {50, -60, 130}, {-50, -60, 130}
-};
-static const int kComputerSurf[5][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
+	{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
+	{70, 25, 120}, {120, 25, 120}, {120, -25, 120}, {70, -25, 120}
 };
 static const int kMonitorPts[8][3] = {
-	{-40, 60, 130}, {40, 60, 130}, {40, -60, 130}, {-40, -60, 130},
-	{-40, 60, 200}, {40, 60, 200}, {40, -60, 200}, {-40, -60, 200}
+	{75, 20, 120}, {115, 20, 120}, {115, -20, 120}, {75, -20, 120},
+	{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
+};
+static const int kComputerSurf[5][8] = {
+	{0, 4, 7, 6, 5, 4, 0, 0}, {0, 4, 0, 3, 7, 4, 0, 0},
+	{0, 4, 3, 2, 6, 7, 0, 0}, {0, 4, 1, 0, 4, 5, 0, 0},
+	{0, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kDeskScreenPts[4][3] = {
-	{-30, -60, 135}, {30, -60, 135}, {30, -60, 195}, {-30, -60, 195}
+	{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
 };
 static const int kDeskScreenSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCSeatPts[4][3] = {
@@ -149,28 +149,33 @@ static const int kCArmLeftPts[4][3] = {
 };
 static const int kCArmLeftSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCArmRightPts[4][3] = {
-	{40, -40, 60}, {40, 40, 60}, {50, 40, 90}, {50, -40, 90}
+	{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
 };
 static const int kCArmRightSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCBackPts[4][3] = {
-	{-50, -40, 120}, {50, -40, 120}, {50, -40, 60}, {-50, -40, 60}
+	{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
 };
 static const int kCBackSurf[2][8] = {
-	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 3, 3, 0, 2, 0, 0, 0}
+	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kCBasePts[8][3] = {
-	{-8, 8, 0}, {8, 8, 0}, {8, -8, 0}, {-8, -8, 0},
-	{-8, 8, 60}, {8, 8, 60}, {8, -8, 60}, {-8, -8, 60}
+	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+	{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
 };
 static const int kCBaseSurf[4][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
 	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kConsolePts[8][3] = {
-	{-70, 110, 0}, {70, 110, 0}, {70, -110, 0}, {-70, -110, 0},
-	{-70, 110, 180}, {70, 110, 180}, {70, -110, 180}, {-70, -110, 180}
+	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+	{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
 };
 static const int kConsoleSurf[5][8] = {
+	{0, 4, 4, 0, 3, 7, 0, 0}, {0, 4, 7, 3, 2, 6, 0, 0},
+	{0, 4, 5, 1, 0, 4, 0, 0}, {0, 4, 6, 2, 1, 5, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kCouchSurf[5][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
 	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
 	{0, 4, 7, 6, 5, 4, 0, 0}
@@ -180,29 +185,24 @@ static const int kACouchPts[8][3] = {
 	{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50}
 };
 static const int kBCouchPts[8][3] = {
-	{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50},
-	{-50, 150, 100}, {50, 150, 100}, {50, -150, 100}, {-50, -150, 100}
+	{-80, 150, 0}, {-45, 150, 0}, {-45, -150, 0}, {-80, -150, 0},
+	{-80, 150, 120}, {-55, 150, 120}, {-55, -150, 120}, {-80, -150, 120}
 };
 static const int kCCouchPts[8][3] = {
-	{-70, 150, 0}, {-50, 150, 0}, {-50, -150, 0}, {-70, -150, 0},
-	{-70, 150, 100}, {-50, 150, 100}, {-50, -150, 100}, {-70, -150, 100}
+	{-70, 170, 0}, {50, 170, 0}, {50, 150, 0}, {-70, 150, 0},
+	{-70, 170, 80}, {50, 170, 80}, {50, 150, 80}, {-70, 150, 80}
 };
 static const int kDCouchPts[8][3] = {
-	{50, 150, 0}, {70, 150, 0}, {70, -150, 0}, {50, -150, 0},
-	{50, 150, 100}, {70, 150, 100}, {70, -150, 100}, {50, -150, 100}
-};
-static const int kCouchSurf[5][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 5, 1, 0, 4, 0, 0}, {0, 4, 6, 2, 1, 5, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
+	{-70, -150, 0}, {50, -150, 0}, {50, -170, 0}, {-70, -170, 0},
+	{-70, -150, 80}, {50, -150, 80}, {50, -170, 80}, {-70, -170, 80}
 };
 static const int kAChairPts[8][3] = {
-	{-70, 70, 0}, {50, 70, 0}, {50, -70, 0}, {-70, -70, 0},
-	{-70, 70, 40}, {50, 70, 40}, {50, -70, 40}, {-70, -70, 40}
+	{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
+	{-50, 50, 50}, {50, 50, 50}, {50, -50, 50}, {-50, -50, 50}
 };
 static const int kBChairPts[8][3] = {
-	{-70, 70, 40}, {50, 70, 40}, {50, -70, 40}, {-70, -70, 40},
-	{-70, 70, 80}, {50, 70, 80}, {50, -70, 80}, {-70, -70, 80}
+	{-80, 50, 0}, {-45, 50, 0}, {-45, -50, 0}, {-80, -50, 0},
+	{-80, 50, 120}, {-55, 50, 120}, {-55, -50, 120}, {-80, -50, 120}
 };
 static const int kCChairPts2[8][3] = {
 	{-70, 70, 0}, {50, 70, 0}, {50, 50, 0}, {-70, 50, 0},


Commit: 213d17b0abe3260bd4b98af62d862085c60f999a
    https://github.com/scummvm/scummvm/commit/213d17b0abe3260bd4b98af62d862085c60f999a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:09+02:00

Commit Message:
COLONY: wall features

Changed paths:
    engines/colony/colony.h
    engines/colony/render.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 48ad2ee4210..6890f98270b 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -239,6 +239,9 @@ private:
 	void draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, uint32 color);
 	bool drawStaticObjectPrisms3D(const Thing &obj, uint32 baseColor);
 	void renderCorridor3D();
+	void drawWallFeatures3D();
+	void drawWallFeature3D(int cellX, int cellY, int direction);
+	void getWallFace3D(int cellX, int cellY, int direction, float corners[4][3]);
 
 	int occupiedObjectAt(int x, int y, const Locate *pobject);
 	void interactWithObject(int objNum);
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index a5a38c97248..33ece248da3 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -457,6 +457,264 @@ void ColonyEngine::drawStaticObjects() {
 	}
 }
 
+// Get the 4 corners of a wall face in 3D world space.
+// corners[0] = bottom-left, corners[1] = bottom-right, corners[2] = top-right, corners[3] = top-left
+// "left" and "right" are as seen from the cell that owns the feature.
+void ColonyEngine::getWallFace3D(int cellX, int cellY, int direction, float corners[4][3]) {
+	float x0 = cellX * 256.0f;
+	float y0 = cellY * 256.0f;
+	float x1 = (cellX + 1) * 256.0f;
+	float y1 = (cellY + 1) * 256.0f;
+	const float zBot = -160.0f;
+	const float zTop = 160.0f;
+	// Offset slightly toward the cell interior to prevent z-fighting with the wall polygon
+	const float eps = 1.0f;
+
+	switch (direction) {
+	case kDirNorth: // Wall at y=cellY edge; viewed from inside cell (y > cellY*256)
+		corners[0][0] = x0;  corners[0][1] = y0 + eps;  corners[0][2] = zBot;
+		corners[1][0] = x1;  corners[1][1] = y0 + eps;  corners[1][2] = zBot;
+		corners[2][0] = x1;  corners[2][1] = y0 + eps;  corners[2][2] = zTop;
+		corners[3][0] = x0;  corners[3][1] = y0 + eps;  corners[3][2] = zTop;
+		break;
+	case kDirSouth: // Wall at y=(cellY+1) edge; viewed from inside cell
+		corners[0][0] = x1;  corners[0][1] = y1 - eps;  corners[0][2] = zBot;
+		corners[1][0] = x0;  corners[1][1] = y1 - eps;  corners[1][2] = zBot;
+		corners[2][0] = x0;  corners[2][1] = y1 - eps;  corners[2][2] = zTop;
+		corners[3][0] = x1;  corners[3][1] = y1 - eps;  corners[3][2] = zTop;
+		break;
+	case kDirEast: // Wall at x=(cellX+1) edge; viewed from inside cell
+		corners[0][0] = x1 - eps;  corners[0][1] = y1;  corners[0][2] = zBot;
+		corners[1][0] = x1 - eps;  corners[1][1] = y0;  corners[1][2] = zBot;
+		corners[2][0] = x1 - eps;  corners[2][1] = y0;  corners[2][2] = zTop;
+		corners[3][0] = x1 - eps;  corners[3][1] = y1;  corners[3][2] = zTop;
+		break;
+	case kDirWest: // Wall at x=cellX edge; viewed from inside cell
+		corners[0][0] = x0 + eps;  corners[0][1] = y0;  corners[0][2] = zBot;
+		corners[1][0] = x0 + eps;  corners[1][1] = y1;  corners[1][2] = zBot;
+		corners[2][0] = x0 + eps;  corners[2][1] = y1;  corners[2][2] = zTop;
+		corners[3][0] = x0 + eps;  corners[3][1] = y0;  corners[3][2] = zTop;
+		break;
+	default:
+		return;
+	}
+}
+
+// Interpolate a point on the wall face.
+// u: 0=left, 1=right (horizontal fraction)
+// v: 0=bottom, 1=top (vertical fraction)
+static void wallPoint(const float corners[4][3], float u, float v, float out[3]) {
+	float botX = corners[0][0] + (corners[1][0] - corners[0][0]) * u;
+	float botY = corners[0][1] + (corners[1][1] - corners[0][1]) * u;
+	float botZ = corners[0][2] + (corners[1][2] - corners[0][2]) * u;
+	float topX = corners[3][0] + (corners[2][0] - corners[3][0]) * u;
+	float topY = corners[3][1] + (corners[2][1] - corners[3][1]) * u;
+	float topZ = corners[3][2] + (corners[2][2] - corners[3][2]) * u;
+	out[0] = botX + (topX - botX) * v;
+	out[1] = botY + (topY - botY) * v;
+	out[2] = botZ + (topZ - botZ) * v;
+}
+
+// Draw a line on a wall face using normalized (u,v) coordinates
+static void wallLine(Renderer *gfx, const float corners[4][3], float u1, float v1, float u2, float v2, uint32 color) {
+	float p1[3], p2[3];
+	wallPoint(corners, u1, v1, p1);
+	wallPoint(corners, u2, v2, p2);
+	gfx->draw3DLine(p1[0], p1[1], p1[2], p2[0], p2[1], p2[2], color);
+}
+
+void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
+	const uint8 *map = mapFeatureAt(cellX, cellY, direction);
+	if (!map || map[0] == kWallFeatureNone)
+		return;
+
+	float corners[4][3];
+	getWallFace3D(cellX, cellY, direction, corners);
+
+	switch (map[0]) {
+	case kWallFeatureDoor: {
+		const uint32 dark = 160;
+		// Door frame: centered rectangle on the wall
+		float xl = 0.25f, xr = 0.75f;
+		float yb = 0.125f, yt = 0.875f;
+		if (map[1] == 0) {
+			// Open door — just the frame
+			wallLine(_gfx, corners, xl, yb, xl, yt, dark);
+			wallLine(_gfx, corners, xl, yt, xr, yt, dark);
+			wallLine(_gfx, corners, xr, yt, xr, yb, dark);
+			wallLine(_gfx, corners, xr, yb, xl, yb, dark);
+		} else {
+			// Closed door — frame + handle line
+			wallLine(_gfx, corners, xl, yb, xl, yt, dark);
+			wallLine(_gfx, corners, xl, yt, xr, yt, dark);
+			wallLine(_gfx, corners, xr, yt, xr, yb, dark);
+			wallLine(_gfx, corners, xr, yb, xl, yb, dark);
+			// Handle
+			float hx = 0.6f;
+			float hy1 = 0.45f, hy2 = 0.55f;
+			wallLine(_gfx, corners, hx, hy1, hx, hy2, dark);
+		}
+		break;
+	}
+	case kWallFeatureWindow: {
+		const uint32 dark = 160;
+		// Window: centered smaller rectangle with cross divider
+		float xl = 0.25f, xr = 0.75f;
+		float yb = 0.375f, yt = 0.75f;
+		wallLine(_gfx, corners, xl, yb, xr, yb, dark);
+		wallLine(_gfx, corners, xr, yb, xr, yt, dark);
+		wallLine(_gfx, corners, xr, yt, xl, yt, dark);
+		wallLine(_gfx, corners, xl, yt, xl, yb, dark);
+		// Cross dividers
+		float xc = 0.5f, yc = (yb + yt) * 0.5f;
+		wallLine(_gfx, corners, xc, yb, xc, yt, dark);
+		wallLine(_gfx, corners, xl, yc, xr, yc, dark);
+		break;
+	}
+	case kWallFeatureShelves: {
+		const uint32 dark = 170;
+		// Bookshelf: outer rectangle + horizontal shelf lines
+		float xl = 0.15f, xr = 0.85f;
+		float yb = 0.1f, yt = 0.9f;
+		wallLine(_gfx, corners, xl, yb, xr, yb, dark);
+		wallLine(_gfx, corners, xr, yb, xr, yt, dark);
+		wallLine(_gfx, corners, xr, yt, xl, yt, dark);
+		wallLine(_gfx, corners, xl, yt, xl, yb, dark);
+		// 6 shelves
+		for (int i = 1; i <= 6; i++) {
+			float t = yb + (yt - yb) * (float)i / 7.0f;
+			wallLine(_gfx, corners, xl, t, xr, t, dark);
+		}
+		break;
+	}
+	case kWallFeatureUpStairs: {
+		const uint32 dark = 170;
+		// Upward stairs: ascending step pattern
+		float xl = 0.15f, xr = 0.85f;
+		for (int i = 0; i < 6; i++) {
+			float u = xl + (xr - xl) * (float)i / 6.0f;
+			float u2 = xl + (xr - xl) * (float)(i + 1) / 6.0f;
+			float v = 0.1f + 0.8f * (float)i / 6.0f;
+			float v2 = 0.1f + 0.8f * (float)(i + 1) / 6.0f;
+			wallLine(_gfx, corners, u, v, u2, v2, dark);
+		}
+		// Side rails
+		wallLine(_gfx, corners, xl, 0.1f, xr, 0.9f, dark);
+		break;
+	}
+	case kWallFeatureDnStairs: {
+		const uint32 dark = 170;
+		// Downward stairs: descending step pattern
+		float xl = 0.15f, xr = 0.85f;
+		for (int i = 0; i < 6; i++) {
+			float u = xl + (xr - xl) * (float)i / 6.0f;
+			float u2 = xl + (xr - xl) * (float)(i + 1) / 6.0f;
+			float v = 0.9f - 0.8f * (float)i / 6.0f;
+			float v2 = 0.9f - 0.8f * (float)(i + 1) / 6.0f;
+			wallLine(_gfx, corners, u, v, u2, v2, dark);
+		}
+		wallLine(_gfx, corners, xl, 0.9f, xr, 0.1f, dark);
+		break;
+	}
+	case kWallFeatureGlyph: {
+		const uint32 dark = 170;
+		// Alien glyphs: horizontal lines (like hieroglyphics)
+		float xl = 0.1f, xr = 0.9f;
+		for (int i = 0; i < 9; i++) {
+			float v = 0.15f + 0.7f * (float)i / 8.0f;
+			wallLine(_gfx, corners, xl, v, xr, v, dark);
+		}
+		break;
+	}
+	case kWallFeatureElevator: {
+		const uint32 dark = 170;
+		// Elevator: tall rectangle with center divider line
+		float xl = 0.15f, xr = 0.85f;
+		float yb = 0.05f, yt = 0.95f;
+		wallLine(_gfx, corners, xl, yb, xl, yt, dark);
+		wallLine(_gfx, corners, xl, yt, xr, yt, dark);
+		wallLine(_gfx, corners, xr, yt, xr, yb, dark);
+		wallLine(_gfx, corners, xr, yb, xl, yb, dark);
+		// Center divider
+		float xc = 0.5f;
+		wallLine(_gfx, corners, xc, yb, xc, yt, dark);
+		break;
+	}
+	case kWallFeatureTunnel: {
+		const uint32 dark = 120;
+		// Tunnel: hexagonal opening
+		float pts[][2] = {
+			{0.0f, 0.5f}, {0.15f, 0.85f}, {0.5f, 1.0f},
+			{0.85f, 0.85f}, {1.0f, 0.5f}, {0.85f, 0.15f},
+			{0.5f, 0.0f}, {0.15f, 0.15f}
+		};
+		for (int i = 0; i < 8; i++) {
+			int n = (i + 1) % 8;
+			// Scale inward slightly
+			float u1 = 0.1f + pts[i][0] * 0.8f;
+			float v1 = 0.1f + pts[i][1] * 0.8f;
+			float u2 = 0.1f + pts[n][0] * 0.8f;
+			float v2 = 0.1f + pts[n][1] * 0.8f;
+			wallLine(_gfx, corners, u1, v1, u2, v2, dark);
+		}
+		break;
+	}
+	case kWallFeatureAirlock: {
+		const uint32 dark = map[1] == 0 ? (uint32)150 : (uint32)170;
+		// Airlock: octagonal shape
+		float pts[][2] = {
+			{0.0f, 0.5f}, {0.15f, 0.85f}, {0.5f, 1.0f},
+			{0.85f, 0.85f}, {1.0f, 0.5f}, {0.85f, 0.15f},
+			{0.5f, 0.0f}, {0.15f, 0.15f}
+		};
+		for (int i = 0; i < 8; i++) {
+			int n = (i + 1) % 8;
+			float u1 = 0.1f + pts[i][0] * 0.8f;
+			float v1 = 0.1f + pts[i][1] * 0.8f;
+			float u2 = 0.1f + pts[n][0] * 0.8f;
+			float v2 = 0.1f + pts[n][1] * 0.8f;
+			wallLine(_gfx, corners, u1, v1, u2, v2, dark);
+		}
+		if (map[1] != 0) {
+			// Closed: add cross lines through center
+			wallLine(_gfx, corners, 0.1f, 0.5f, 0.5f, 0.5f, dark);
+			wallLine(_gfx, corners, 0.5f, 0.1f, 0.5f, 0.5f, dark);
+			wallLine(_gfx, corners, 0.9f, 0.5f, 0.5f, 0.5f, dark);
+			wallLine(_gfx, corners, 0.5f, 0.9f, 0.5f, 0.5f, dark);
+		}
+		break;
+	}
+	case kWallFeatureColor: {
+		// Colored horizontal bands
+		for (int i = 1; i <= 3; i++) {
+			uint32 c = 120 + map[i] * 20;
+			if (c == 120 && map[i] == 0 && !map[1] && !map[2] && !map[3] && !map[4]) {
+				c = 100 + (_level * 15);
+			}
+			float v = (float)i / 4.0f;
+			wallLine(_gfx, corners, 0.0f, v, 1.0f, v, c);
+		}
+		break;
+	}
+	default:
+		break;
+	}
+}
+
+void ColonyEngine::drawWallFeatures3D() {
+	for (int y = 0; y < 31; y++) {
+		for (int x = 0; x < 31; x++) {
+			for (int dir = 0; dir < 4; dir++) {
+				const uint8 *map = mapFeatureAt(x, y, dir);
+				if (map && map[0] != kWallFeatureNone) {
+					drawWallFeature3D(x, y, dir);
+				}
+			}
+		}
+	}
+}
+
+
 void ColonyEngine::renderCorridor3D() {
 	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
  
@@ -485,6 +743,7 @@ void ColonyEngine::renderCorridor3D() {
 		}
 	}
 	
+	drawWallFeatures3D();
 	drawStaticObjects();
 		
 	_gfx->end3D();
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index a58e5e6b414..d0c4d69d42a 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -54,6 +54,7 @@ public:
 	virtual void draw3DWall(int x1, int y1, int x2, int y2, uint32 color) = 0;
 	virtual void draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) = 0;
 	virtual void draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) = 0;
+	virtual void draw3DLine(float x1, float y1, float z1, float x2, float y2, float z2, uint32 color) = 0;
 	virtual void end3D() = 0;
 
 	// Buffer management
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 90fd2138c87..55a22bb9d3d 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -52,6 +52,7 @@ public:
 	void draw3DWall(int x1, int y1, int x2, int y2, uint32 color) override;
 	void draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) override;
 	void draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) override;
+	void draw3DLine(float x1, float y1, float z1, float x2, float y2, float z2, uint32 color) override;
 	void end3D() override;
 
 	void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) override;
@@ -285,6 +286,14 @@ void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *
 		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 	}
 }
+
+void OpenGLRenderer::draw3DLine(float x1, float y1, float z1, float x2, float y2, float z2, uint32 color) {
+	useColor(color);
+	glBegin(GL_LINES);
+	glVertex3f(x1, y1, z1);
+	glVertex3f(x2, y2, z2);
+	glEnd();
+}
  
 void OpenGLRenderer::end3D() {
 	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);


Commit: 140d2c24e314e38c4d0107b690af19acba7b94aa
    https://github.com/scummvm/scummvm/commit/140d2c24e314e38c4d0107b690af19acba7b94aa
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:09+02:00

Commit Message:
COLONY: delete unused software renderer stub

Changed paths:
  R engines/colony/renderer_software.cpp


diff --git a/engines/colony/renderer_software.cpp b/engines/colony/renderer_software.cpp
deleted file mode 100644
index cd529d153bb..00000000000
--- a/engines/colony/renderer_software.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "common/system.h"
-#include "graphics/managed_surface.h"
-#include "colony/renderer.h"
-
-namespace Colony {
-
-class SoftwareRenderer : public Renderer {
-public:
-	SoftwareRenderer(OSystem *system, int width, int height);
-	~SoftwareRenderer();
-
-	void clear(uint32 color) override;
-	void drawLine(int x1, int y1, int x2, int y2, uint32 color) override;
-	void drawRect(const Common::Rect &rect, uint32 color) override;
-	void fillRect(const Common::Rect &rect, uint32 color) override;
-	void drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) override;
-	void scroll(int dx, int dy, uint32 background) override;
-	bool isAccelerated() const override { return false; }
-	void drawEllipse(int x, int y, int rx, int ry, uint32 color) override;
-	void fillEllipse(int x, int y, int rx, int ry, uint32 color) override;
-	void fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) override;
-	void setPixel(int x, int y, uint32 color) override;
-	void setPalette(const byte *palette, uint start, uint count) override;
-
-	void begin3D(int camX, int camY, int camZ, int angle, int angleY, const Common::Rect &viewport) override {}
-	void draw3DWall(int x1, int y1, int x2, int y2, uint32 color) override {}
-	void draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) override {}
-	void draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) override {}
-	void end3D() override {}
-
-	void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) override;
-	void drawPolygon(const int *x, const int *y, int count, uint32 color) override;
-	void copyToScreen() override;
-	void setWireframe(bool enable) override {}
-
-private:
-	OSystem *_system;
-	Graphics::ManagedSurface _surface;
-	int _width;
-	int _height;
-};
-
-SoftwareRenderer::SoftwareRenderer(OSystem *system, int width, int height) : _system(system), _width(width), _height(height) {
-	_surface.create(width, height, _system->getScreenFormat());
-}
-
-SoftwareRenderer::~SoftwareRenderer() {
-	_surface.free();
-}
-
-void SoftwareRenderer::clear(uint32 color) {
-	_surface.clear(color);
-}
-
-void SoftwareRenderer::drawLine(int x1, int y1, int x2, int y2, uint32 color) {
-	_surface.drawLine(x1, y1, x2, y2, color);
-}
-
-void SoftwareRenderer::drawRect(const Common::Rect &rect, uint32 color) {
-	_surface.frameRect(rect, color);
-}
-
-void SoftwareRenderer::fillRect(const Common::Rect &rect, uint32 color) {
-	_surface.fillRect(rect, color);
-}
-
-void SoftwareRenderer::drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) {
-	if (!font) return;
-	font->drawString(&_surface, str, x, y, (align == Graphics::kTextAlignCenter && x == 0) ? _width : (_width - x), color, align);
-}
-
-void SoftwareRenderer::scroll(int dx, int dy, uint32 background) {
-	if (abs(dx) >= _width || abs(dy) >= _height) {
-		clear(background);
-		return;
-	}
-
-	Graphics::ManagedSurface tmp;
-	tmp.create(_width, _height, _surface.format);
-	tmp.blitFrom(_surface);
-
-	clear(background);
-	_surface.blitFrom(tmp, Common::Rect(0, 0, _width, _height), Common::Rect(dx, dy, dx + _width, dy + _height));
-	tmp.free();
-}
-
-void SoftwareRenderer::drawEllipse(int x, int y, int rx, int ry, uint32 color) {
-	_surface.drawEllipse(x - rx, y - ry, x + rx, y + ry, color, false);
-}
-
-void SoftwareRenderer::fillEllipse(int x, int y, int rx, int ry, uint32 color) {
-	_surface.drawEllipse(x - rx, y - ry, x + rx, y + ry, color, true);
-}
-
-void SoftwareRenderer::fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) {
-	for (int y = rect.top; y < rect.bottom; y++) {
-		for (int x = rect.left; x < rect.right; x++) {
-			if ((x + y) % 2 == 0)
-				_surface.setPixel(x, y, color1);
-			else
-				_surface.setPixel(x, y, color2);
-		}
-	}
-}
-
-void SoftwareRenderer::setPixel(int x, int y, uint32 color) {
-	if (x >= 0 && x < _width && y >= 0 && y < _height)
-		_surface.setPixel(x, y, color);
-}
-
-void SoftwareRenderer::setPalette(const byte *palette, uint start, uint count) {
-	// Software renderer doesn't need to cache palette, ManagedSurface uses it from system if 8bpp
-}
-
-void SoftwareRenderer::drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) {
-	_surface.drawLine(x1, y1, x2, y2, color);
-	_surface.drawLine(x2, y2, x3, y3, color);
-	_surface.drawLine(x3, y3, x4, y4, color);
-	_surface.drawLine(x4, y4, x1, y1, color);
-}
-
-void SoftwareRenderer::drawPolygon(const int *x, const int *y, int count, uint32 color) {
-	if (count < 2) return;
-	for (int i = 0; i < count; i++) {
-		int next = (i + 1) % count;
-		_surface.drawLine(x[i], y[i], x[next], y[next], color);
-	}
-}
-
-void SoftwareRenderer::copyToScreen() {
-	_system->copyRectToScreen(_surface.getPixels(), _surface.pitch, 0, 0, _surface.w, _surface.h);
-	_system->updateScreen();
-}
-
-Renderer *createSoftwareRenderer(OSystem *system, int width, int height) {
-	return new SoftwareRenderer(system, width, height);
-}
-
-} // End of namespace Colony


Commit: 2f005ccd5b99a8acf7aba7045a4e05e27f07bcaa
    https://github.com/scummvm/scummvm/commit/2f005ccd5b99a8acf7aba7045a4e05e27f07bcaa
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:09+02:00

Commit Message:
COLONY: fix z-fighting with glPolygonOffset and improved depth testing

Changed paths:
    engines/colony/render.cpp
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 33ece248da3..ef85adae95c 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -467,8 +467,8 @@ void ColonyEngine::getWallFace3D(int cellX, int cellY, int direction, float corn
 	float y1 = (cellY + 1) * 256.0f;
 	const float zBot = -160.0f;
 	const float zTop = 160.0f;
-	// Offset slightly toward the cell interior to prevent z-fighting with the wall polygon
-	const float eps = 1.0f;
+	// No more manual eps offset; we'll use glPolygonOffset in the renderer
+	const float eps = 0.0f;
 
 	switch (direction) {
 	case kDirNorth: // Wall at y=cellY edge; viewed from inside cell (y > cellY*256)
@@ -702,6 +702,8 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 }
 
 void ColonyEngine::drawWallFeatures3D() {
+	// TODO
+	/*
 	for (int y = 0; y < 31; y++) {
 		for (int x = 0; x < 31; x++) {
 			for (int dir = 0; dir < 4; dir++) {
@@ -711,7 +713,7 @@ void ColonyEngine::drawWallFeatures3D() {
 				}
 			}
 		}
-	}
+	}*/
 }
 
 
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 55a22bb9d3d..1ceb5a23515 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -185,6 +185,9 @@ void OpenGLRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY
 	glViewport(vpX, vpY, vpW, vpH);
 	glScissor(vpX, vpY, vpW, vpH);
 	glEnable(GL_SCISSOR_TEST);
+	glEnable(GL_DEPTH_TEST);
+	glDepthFunc(GL_LEQUAL);
+	glDepthMask(GL_TRUE);
  
 	glMatrixMode(GL_PROJECTION);
 	glLoadIdentity();
@@ -225,9 +228,9 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 	float fy2 = y2 * 256.0f;
 
 	if (_wireframe) {
-		// Push the black fill away from camera so wireframe edges always win
+		// Pass 1: Draw black fill (pushed back)
 		glEnable(GL_POLYGON_OFFSET_FILL);
-		glPolygonOffset(1.0f, 1.0f);
+		glPolygonOffset(1.1f, 4.0f);
 		useColor(0);
 		glBegin(GL_QUADS);
 		glVertex3f(fx1, fy1, -160.0f);
@@ -235,55 +238,114 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 		glVertex3f(fx2, fy2, 160.0f);
 		glVertex3f(fx1, fy1, 160.0f);
 		glEnd();
-		glDisable(GL_POLYGON_OFFSET_FILL);
-		// Switch to wireframe for the colored edge pass
+
+		// Pass 2: Draw colored wireframe edges pulled forward relative to the fill
+		glEnable(GL_POLYGON_OFFSET_LINE);
+		glPolygonOffset(0.0f, -1.0f); // Pull lines forward from the fill
 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-	}
-	useColor(color);
-	glBegin(GL_QUADS);
-	glVertex3f(fx1, fy1, -160.0f);
-	glVertex3f(fx2, fy2, -160.0f);
-	glVertex3f(fx2, fy2, 160.0f);
-	glVertex3f(fx1, fy1, 160.0f);
-	glEnd();
-	if (_wireframe) {
+		useColor(color);
+		glBegin(GL_QUADS);
+		glVertex3f(fx1, fy1, -160.0f);
+		glVertex3f(fx2, fy2, -160.0f);
+		glVertex3f(fx2, fy2, 160.0f);
+		glVertex3f(fx1, fy1, 160.0f);
+		glEnd();
 		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+		glDisable(GL_POLYGON_OFFSET_LINE);
+		glDisable(GL_POLYGON_OFFSET_FILL);
+	} else {
+		// Normal mode: push the wall face back slightly.
+		// This ensures that wall features drawn later as lines at the same depth correctly win the depth test.
+		glEnable(GL_POLYGON_OFFSET_FILL);
+		glPolygonOffset(1.1f, 4.0f); // Positive pulls AWAY from camera
+		useColor(color);
+		glBegin(GL_QUADS);
+		glVertex3f(fx1, fy1, -160.0f);
+		glVertex3f(fx2, fy2, -160.0f);
+		glVertex3f(fx2, fy2, 160.0f);
+		glVertex3f(fx1, fy1, 160.0f);
+		glEnd();
+		glDisable(GL_POLYGON_OFFSET_FILL);
 	}
 }
  
 void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) {
-	useColor(color);
-	glBegin(GL_QUADS);
-	glVertex3f(x1, y1, z1);
-	glVertex3f(x2, y2, z2);
-	glVertex3f(x3, y3, z3);
-	glVertex3f(x4, y4, z4);
-	glEnd();
+	if (_wireframe) {
+		// Pass 1: Draw black fill (pushed back)
+		glEnable(GL_POLYGON_OFFSET_FILL);
+		glPolygonOffset(1.1f, 4.0f);
+		useColor(0);
+		glBegin(GL_QUADS);
+		glVertex3f(x1, y1, z1);
+		glVertex3f(x2, y2, z2);
+		glVertex3f(x3, y3, z3);
+		glVertex3f(x4, y4, z4);
+		glEnd();
+
+		// Pass 2: Draw colored wireframe edges pulled forward
+		glEnable(GL_POLYGON_OFFSET_LINE);
+		glPolygonOffset(0.0f, -1.0f);
+		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+		useColor(color);
+		glBegin(GL_QUADS);
+		glVertex3f(x1, y1, z1);
+		glVertex3f(x2, y2, z2);
+		glVertex3f(x3, y3, z3);
+		glVertex3f(x4, y4, z4);
+		glEnd();
+		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+		glDisable(GL_POLYGON_OFFSET_LINE);
+		glDisable(GL_POLYGON_OFFSET_FILL);
+	} else {
+		// Normal mode: push back to allow overlays (like wall features or floor decorations)
+		glEnable(GL_POLYGON_OFFSET_FILL);
+		glPolygonOffset(1.1f, 4.0f);
+		useColor(color);
+		glBegin(GL_QUADS);
+		glVertex3f(x1, y1, z1);
+		glVertex3f(x2, y2, z2);
+		glVertex3f(x3, y3, z3);
+		glVertex3f(x4, y4, z4);
+		glEnd();
+		glDisable(GL_POLYGON_OFFSET_FILL);
+	}
 }
  
 void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) {
 	if (count < 3) return;
 
 	if (_wireframe) {
-		// Push the black fill away from camera so wireframe edges always win
+		// Pass 1: Draw black fill (pushed back)
 		glEnable(GL_POLYGON_OFFSET_FILL);
-		glPolygonOffset(1.0f, 1.0f);
+		glPolygonOffset(1.1f, 4.0f);
 		useColor(0);
 		glBegin(GL_POLYGON);
 		for (int i = 0; i < count; i++)
 			glVertex3f(x[i], y[i], z[i]);
 		glEnd();
-		glDisable(GL_POLYGON_OFFSET_FILL);
-		// Switch to wireframe for the colored edge pass
+
+		// Pass 2: Draw colored wireframe edges pulled forward
+		glEnable(GL_POLYGON_OFFSET_LINE);
+		glPolygonOffset(0.0f, -1.0f);
 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-	}
-	useColor(color);
-	glBegin(GL_POLYGON);
-	for (int i = 0; i < count; i++)
-		glVertex3f(x[i], y[i], z[i]);
-	glEnd();
-	if (_wireframe) {
+		useColor(color);
+		glBegin(GL_POLYGON);
+		for (int i = 0; i < count; i++)
+			glVertex3f(x[i], y[i], z[i]);
+		glEnd();
 		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+		glDisable(GL_POLYGON_OFFSET_LINE);
+		glDisable(GL_POLYGON_OFFSET_FILL);
+	} else {
+		// Normal mode: push back
+		glEnable(GL_POLYGON_OFFSET_FILL);
+		glPolygonOffset(1.1f, 4.0f);
+		useColor(color);
+		glBegin(GL_POLYGON);
+		for (int i = 0; i < count; i++)
+			glVertex3f(x[i], y[i], z[i]);
+		glEnd();
+		glDisable(GL_POLYGON_OFFSET_FILL);
 	}
 }
 


Commit: c33f5707f00dc6f0617c2ad606c2aa6484a8cb0f
    https://github.com/scummvm/scummvm/commit/c33f5707f00dc6f0617c2ad606c2aa6484a8cb0f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:10+02:00

Commit Message:
COLONY: modern controls

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 49716d12a9d..8bc43aeb77c 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -222,6 +222,7 @@ Common::Error ColonyEngine::run() {
 					const bool allowInteraction = (event.kbd.flags & Common::KBD_CTRL) == 0;
 					switch (event.kbd.keycode) {
 					case Common::KEYCODE_UP:
+					case Common::KEYCODE_w:
 					{
 						int xnew = _me.xloc + (_cost[_me.look] >> 2);
 						int ynew = _me.yloc + (_sint[_me.look] >> 2);
@@ -229,18 +230,31 @@ Common::Error ColonyEngine::run() {
 						break;
 					}
 					case Common::KEYCODE_DOWN:
+					case Common::KEYCODE_s:
 					{
 						int xnew = _me.xloc - (_cost[_me.look] >> 2);
 						int ynew = _me.yloc - (_sint[_me.look] >> 2);
 						cCommand(xnew, ynew, allowInteraction);
 						break;
 					}
-				case Common::KEYCODE_LEFT:
-					_me.look = (uint8)((int)_me.look + 8);
-					break;
+					case Common::KEYCODE_LEFT:
+					case Common::KEYCODE_a:
+					{
+						uint8 strafeAngle = (uint8)((int)_me.look + 64);
+						int xnew = _me.xloc + (_cost[strafeAngle] >> 2);
+						int ynew = _me.yloc + (_sint[strafeAngle] >> 2);
+						cCommand(xnew, ynew, allowInteraction);
+						break;
+					}
 					case Common::KEYCODE_RIGHT:
-						_me.look = (uint8)((int)_me.look - 8);
+					case Common::KEYCODE_d:
+					{
+						uint8 strafeAngle = (uint8)((int)_me.look - 64);
+						int xnew = _me.xloc + (_cost[strafeAngle] >> 2);
+						int ynew = _me.yloc + (_sint[strafeAngle] >> 2);
+						cCommand(xnew, ynew, allowInteraction);
 						break;
+					}
 					case Common::KEYCODE_F7:
 						_wireframe = !_wireframe;
 						_gfx->setWireframe(_wireframe);


Commit: 6dbbde960292b8425c2b72703cab7ea211068383
    https://github.com/scummvm/scummvm/commit/6dbbde960292b8425c2b72703cab7ea211068383
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:10+02:00

Commit Message:
COLONY: wall features

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index ef85adae95c..4ae0107b321 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -467,33 +467,33 @@ void ColonyEngine::getWallFace3D(int cellX, int cellY, int direction, float corn
 	float y1 = (cellY + 1) * 256.0f;
 	const float zBot = -160.0f;
 	const float zTop = 160.0f;
-	// No more manual eps offset; we'll use glPolygonOffset in the renderer
-	const float eps = 0.0f;
+	// Pull slightly into the cell to prevent z-fighting
+	const float eps = 1.0f;
 
 	switch (direction) {
-	case kDirNorth: // Wall at y=cellY edge; viewed from inside cell (y > cellY*256)
-		corners[0][0] = x0;  corners[0][1] = y0 + eps;  corners[0][2] = zBot;
-		corners[1][0] = x1;  corners[1][1] = y0 + eps;  corners[1][2] = zBot;
-		corners[2][0] = x1;  corners[2][1] = y0 + eps;  corners[2][2] = zTop;
-		corners[3][0] = x0;  corners[3][1] = y0 + eps;  corners[3][2] = zTop;
+	case kDirNorth: // Wall at y1 (+Y); viewed from inside (looking North)
+		corners[0][0] = x0;  corners[0][1] = y1 - eps;  corners[0][2] = zBot; // BL
+		corners[1][0] = x1;  corners[1][1] = y1 - eps;  corners[1][2] = zBot; // BR
+		corners[2][0] = x1;  corners[2][1] = y1 - eps;  corners[2][2] = zTop; // TR
+		corners[3][0] = x0;  corners[3][1] = y1 - eps;  corners[3][2] = zTop; // TL
 		break;
-	case kDirSouth: // Wall at y=(cellY+1) edge; viewed from inside cell
-		corners[0][0] = x1;  corners[0][1] = y1 - eps;  corners[0][2] = zBot;
-		corners[1][0] = x0;  corners[1][1] = y1 - eps;  corners[1][2] = zBot;
-		corners[2][0] = x0;  corners[2][1] = y1 - eps;  corners[2][2] = zTop;
-		corners[3][0] = x1;  corners[3][1] = y1 - eps;  corners[3][2] = zTop;
+	case kDirSouth: // Wall at y0 (-Y); viewed from inside (looking South)
+		corners[0][0] = x1;  corners[0][1] = y0 + eps;  corners[0][2] = zBot; // BL
+		corners[1][0] = x0;  corners[1][1] = y0 + eps;  corners[1][2] = zBot; // BR
+		corners[2][0] = x0;  corners[2][1] = y0 + eps;  corners[2][2] = zTop; // TR
+		corners[3][0] = x1;  corners[3][1] = y0 + eps;  corners[3][2] = zTop; // TL
 		break;
-	case kDirEast: // Wall at x=(cellX+1) edge; viewed from inside cell
-		corners[0][0] = x1 - eps;  corners[0][1] = y1;  corners[0][2] = zBot;
-		corners[1][0] = x1 - eps;  corners[1][1] = y0;  corners[1][2] = zBot;
-		corners[2][0] = x1 - eps;  corners[2][1] = y0;  corners[2][2] = zTop;
-		corners[3][0] = x1 - eps;  corners[3][1] = y1;  corners[3][2] = zTop;
+	case kDirEast: // Wall at x1 (+X); viewed from inside (looking East)
+		corners[0][0] = x1 - eps;  corners[0][1] = y1;  corners[0][2] = zBot; // BL
+		corners[1][0] = x1 - eps;  corners[1][1] = y0;  corners[1][2] = zBot; // BR
+		corners[2][0] = x1 - eps;  corners[2][1] = y0;  corners[2][2] = zTop; // TR
+		corners[3][0] = x1 - eps;  corners[3][1] = y1;  corners[3][2] = zTop; // TL
 		break;
-	case kDirWest: // Wall at x=cellX edge; viewed from inside cell
-		corners[0][0] = x0 + eps;  corners[0][1] = y0;  corners[0][2] = zBot;
-		corners[1][0] = x0 + eps;  corners[1][1] = y1;  corners[1][2] = zBot;
-		corners[2][0] = x0 + eps;  corners[2][1] = y1;  corners[2][2] = zTop;
-		corners[3][0] = x0 + eps;  corners[3][1] = y0;  corners[3][2] = zTop;
+	case kDirWest: // Wall at x0 (-X); viewed from inside (looking West)
+		corners[0][0] = x0 + eps;  corners[0][1] = y0;  corners[0][2] = zBot; // BL
+		corners[1][0] = x0 + eps;  corners[1][1] = y1;  corners[1][2] = zBot; // BR
+		corners[2][0] = x0 + eps;  corners[2][1] = y1;  corners[2][2] = zTop; // TR
+		corners[3][0] = x0 + eps;  corners[3][1] = y0;  corners[3][2] = zTop; // TL
 		break;
 	default:
 		return;
@@ -702,8 +702,6 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 }
 
 void ColonyEngine::drawWallFeatures3D() {
-	// TODO
-	/*
 	for (int y = 0; y < 31; y++) {
 		for (int x = 0; x < 31; x++) {
 			for (int dir = 0; dir < 4; dir++) {
@@ -713,7 +711,7 @@ void ColonyEngine::drawWallFeatures3D() {
 				}
 			}
 		}
-	}*/
+	}
 }
 
 


Commit: 8ead1284326ac255398eee76a6b91f127b5d7538
    https://github.com/scummvm/scummvm/commit/8ead1284326ac255398eee76a6b91f127b5d7538
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:10+02:00

Commit Message:
COLONY: widescreen support

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/detection.cpp
    engines/colony/detection.h
    engines/colony/metaengine.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 8bc43aeb77c..f6631c8f1d1 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -57,6 +57,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_unlocked = false;
 	_weapons = 0;
 	_wireframe = true;
+	_widescreen = ConfMan.getBool("widescreen_mod");
 	
 	memset(_wall, 0, sizeof(_wall));
 	memset(_mapData, 0, sizeof(_mapData));
@@ -176,13 +177,17 @@ void ColonyEngine::initTrig() {
 }
 
 Common::Error ColonyEngine::run() {
-	// Create the renderer (follows Freescape pattern: always uses OpenGL)
+	_width = 640;
+	_height = 480;
+
+	if (_widescreen) {
+		_width = _height * 16 / 9;
+	}
+
 	_gfx = createRenderer(_system, _width, _height);
 	if (!_gfx)
 		return Common::kUserCanceled;
 
-	_width = _system->getWidth();
-	_height = _system->getHeight();
 	updateViewportLayout();
 	const Graphics::PixelFormat format = _system->getScreenFormat();
 	debug("Screen format: %d bytesPerPixel. Actual size: %dx%d", format.bytesPerPixel, _width, _height);
@@ -274,6 +279,8 @@ Common::Error ColonyEngine::run() {
 				// to prevent the warp from generating phantom deltas (Freescape pattern)
 				_system->warpMouse(_centerX, _centerY);
 				_system->getEventManager()->purgeMouseEvents();
+			} else if (event.type == Common::EVENT_SCREEN_CHANGED) {
+				_gfx->computeScreenViewport();
 			}
 		}
 
@@ -442,9 +449,9 @@ void ColonyEngine::playAnimation() {
 void ColonyEngine::drawAnimation() {
 	_gfx->clear(0); // Ensure entire screen is black
 
-	// Center 320x200 animation on 640x480 screen
-	int ox = (640 - 320) / 2;
-	int oy = (480 - 200) / 2;
+	// Center 320x200 animation on screen
+	int ox = (_width - 320) / 2;
+	int oy = (_height - 200) / 2;
 
 	// Draw background if active
 	if (_backgroundActive && _backgroundFG) {
@@ -542,8 +549,8 @@ Common::Rect ColonyEngine::readRect(Common::SeekableReadStream &file) {
 }
 
 int ColonyEngine::whichSprite(const Common::Point &p) {
-	int ox = (640 - 320) / 2;
-	int oy = (480 - 200) / 2;
+	int ox = (_width - 320) / 2;
+	int oy = (_height - 200) / 2;
 	Common::Point pt(p.x - ox, p.y - oy);
 
 	for (int i = _lSprites.size() - 1; i >= 0; i--) {
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 6890f98270b..19e898b3023 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -207,6 +207,7 @@ private:
 	bool _unlocked;
 	int _weapons;
 	bool _wireframe;
+	bool _widescreen;
 
 	int _frntxWall, _frntyWall;
 	int _sidexWall, _sideyWall;
diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
index 90df5bd0a64..be227bd58b3 100644
--- a/engines/colony/detection.cpp
+++ b/engines/colony/detection.cpp
@@ -47,6 +47,7 @@ const ADGameDescription gameDescriptions[] = {
 class ColonyMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
 public:
 	ColonyMetaEngineDetection() : AdvancedMetaEngineDetection(Colony::gameDescriptions, Colony::colonyGames) {
+		_guiOptions = GUIO1(GAMEOPTION_WIDESCREEN);
 	}
 
 	const char *getName() const override {
diff --git a/engines/colony/detection.h b/engines/colony/detection.h
index e8c913ab202..d397d6f43e9 100644
--- a/engines/colony/detection.h
+++ b/engines/colony/detection.h
@@ -32,6 +32,8 @@ enum ColonyDebugChannels {
 
 extern const ADGameDescription gameDescriptions[];
 
+#define GAMEOPTION_WIDESCREEN GUIO_GAMEOPTIONS1
+
 } // End of namespace Colony
 
 #endif // COLONY_DETECTION_H
diff --git a/engines/colony/metaengine.cpp b/engines/colony/metaengine.cpp
index 64849fc5445..0dc23c9631f 100644
--- a/engines/colony/metaengine.cpp
+++ b/engines/colony/metaengine.cpp
@@ -22,15 +22,37 @@
 #include "colony/colony.h"
 #include "colony/detection.h"
 #include "common/system.h"
+#include "common/translation.h"
+#include "common/config-manager.h"
 
 namespace Colony {
 
+static const ADExtraGuiOptionsMap optionsList[] = {
+	{
+		GAMEOPTION_WIDESCREEN,
+		{
+			_s("Widescreen mod"),
+			_s("Enable widescreen rendering in fullscreen mode."),
+			"widescreen_mod",
+			false,
+			0,
+			0
+		}
+	},
+
+	AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
 class ColonyMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
 public:
 	const char *getName() const override {
 		return "colony";
 	}
 
+	const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
+		return optionsList;
+	}
+
 	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const override {
 		*engine = new ColonyEngine(syst, gd);
 		return Common::kNoError;
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index d0c4d69d42a..52ef1458478 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -60,6 +60,7 @@ public:
 	// Buffer management
 	virtual void copyToScreen() = 0;
 	virtual void setWireframe(bool enable) = 0;
+	virtual void computeScreenViewport() = 0;
 
 	// Convenience color accessors
 	uint32 white() const { return 255; }
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 1ceb5a23515..3f58e88189c 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/system.h"
+#include "common/config-manager.h"
 #include "graphics/surface.h"
 #include "graphics/font.h"
 #include <math.h>
@@ -59,6 +60,7 @@ public:
 	void drawPolygon(const int *x, const int *y, int count, uint32 color) override;
 	void copyToScreen() override;
 	void setWireframe(bool enable) override { _wireframe = enable; }
+	void computeScreenViewport() override;
 
 private:
 	void useColor(uint32 color);
@@ -68,6 +70,7 @@ private:
 	int _height;
 	byte _palette[256 * 3];
 	bool _wireframe;
+	Common::Rect _screenViewport;
 };
 
 OpenGLRenderer::OpenGLRenderer(OSystem *system, int width, int height) : _system(system), _width(width), _height(height) {
@@ -90,7 +93,7 @@ OpenGLRenderer::OpenGLRenderer(OSystem *system, int width, int height) : _system
 	glMatrixMode(GL_MODELVIEW);
 	glLoadIdentity();
 
-	glViewport(0, 0, _system->getWidth(), _system->getHeight());
+	computeScreenViewport();
 	
 	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 	glClear(GL_COLOR_BUFFER_BIT);
@@ -173,12 +176,12 @@ void OpenGLRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY
 	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  
 	// Scale viewport coordinates to system pixels
-	float scaleX = (float)_system->getWidth() / _width;
-	float scaleY = (float)_system->getHeight() / _height;
+	float scaleX = (float)_screenViewport.width() / _width;
+	float scaleY = (float)_screenViewport.height() / _height;
 	int sysH = _system->getHeight();
  
-	int vpX = (int)(viewport.left * scaleX);
-	int vpY = sysH - (int)(viewport.bottom * scaleY);
+	int vpX = _screenViewport.left + (int)(viewport.left * scaleX);
+	int vpY = sysH - (_screenViewport.top + (int)(viewport.bottom * scaleY));
 	int vpW = (int)(viewport.width() * scaleX);
 	int vpH = (int)(viewport.height() * scaleY);
  
@@ -366,11 +369,39 @@ void OpenGLRenderer::end3D() {
 	glScissor(0, 0, _system->getWidth(), _system->getHeight());
  
 	// Reset to 2D
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+
+	computeScreenViewport();
+
 	glMatrixMode(GL_PROJECTION);
 	glLoadIdentity();
 	glOrtho(0, _width, _height, 0, -1, 1);
-	glMatrixMode(GL_MODELVIEW);
-	glLoadIdentity();
+}
+
+void OpenGLRenderer::computeScreenViewport() {
+	int32 screenWidth = _system->getWidth();
+	int32 screenHeight = _system->getHeight();
+
+	bool widescreen = ConfMan.getBool("widescreen_mod");
+
+	if (widescreen) {
+		_screenViewport = Common::Rect(screenWidth, screenHeight);
+	} else if (_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection)) {
+		// Aspect ratio correction (4:3)
+		int32 viewportWidth = MIN<int32>(screenWidth, screenHeight * 4 / 3);
+		int32 viewportHeight = MIN<int32>(screenHeight, screenWidth * 3 / 4);
+		_screenViewport = Common::Rect(viewportWidth, viewportHeight);
+
+		// Pillarboxing/Letterboxing
+		_screenViewport.translate((screenWidth - viewportWidth) / 2,
+		                           (screenHeight - viewportHeight) / 2);
+	} else {
+		_screenViewport = Common::Rect(screenWidth, screenHeight);
+	}
+
+	glViewport(_screenViewport.left, screenHeight - _screenViewport.bottom, _screenViewport.width(), _screenViewport.height());
+	glScissor(_screenViewport.left, screenHeight - _screenViewport.bottom, _screenViewport.width(), _screenViewport.height());
 }
 
 void OpenGLRenderer::scroll(int dx, int dy, uint32 background) {


Commit: 0f12c493dbbb30b683ce5273a3ec05e22e033091
    https://github.com/scummvm/scummvm/commit/0f12c493dbbb30b683ce5273a3ec05e22e033091
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:11+02:00

Commit Message:
COLONY: implement animation system with loadAnimation/playAnimation

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index f6631c8f1d1..d8eaefb27fd 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -41,7 +41,7 @@ namespace Colony {
 
 
 
-ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd) {
+ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd), _randomSource("colony") {
 	_level = 0;
 	_robotNum = 0;
 	_gfx = nullptr;
@@ -86,6 +86,18 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_backgroundFG = nullptr;
 	_backgroundActive = false;
 	_divideBG = 0;
+	_animationRunning = false;
+
+	for (int i = 0; i < 4; i++) {
+		_decode1[i] = _decode2[i] = _decode3[i] = 0;
+	}
+	for (int i = 0; i < 6; i++) _animDisplay[i] = 1;
+	for (int i = 0; i < 2; i++) _coreState[i] = _coreHeight[i] = 0;
+	for (int i = 0; i < 3; i++) _corePower[i] = 0;
+	_coreIndex = 0;
+	_orbit = 0;
+	_armor = 0;
+	_gametest = false;
 
 	initTrig();
 }
@@ -324,7 +336,7 @@ void ColonyEngine::scrollInfo() {
 	centerY += 5;
 
 	for (int i = 0; i < storyLength; i++) {
-		_gfx->drawString(font, story[i], 0, centerY + 10 * i, 9, Graphics::kTextAlignCenter);
+		_gfx->drawString(font, story[i], _width / 2, centerY + 10 * i, 9, Graphics::kTextAlignCenter);
 	}
 	_gfx->copyToScreen();
 
@@ -343,6 +355,8 @@ void ColonyEngine::scrollInfo() {
 }
 
 bool ColonyEngine::loadAnimation(const Common::String &name) {
+	_animationName = name;
+	for (int i = 0; i < 6; i++) _animDisplay[i] = 1;
 	Common::String fileName = name + ".pic";
 	Common::File file;
 	if (!file.open(Common::Path(fileName))) {
@@ -421,8 +435,30 @@ void ColonyEngine::deleteAnimation() {
 }
 
 void ColonyEngine::playAnimation() {
-	bool running = true;
-	while (running && !shouldQuit()) {
+	_animationRunning = true;
+	_system->lockMouse(false);
+	_system->showMouse(true);
+
+	if (_animationName == "security" && !_unlocked) {
+		for (int i = 0; i < 4; i++) {
+			_decode1[i] = (uint8)(2 + _randomSource.getRandomNumber(3));
+			SetObjectState(27 + i, _decode1[i]);
+		}
+	} else if (_animationName == "reactor") {
+		for (int i = 0; i < 6; i++) {
+			SetObjectOnOff(14 + i * 2, false);
+			SetObjectState(13 + i * 2, 1);
+		}
+	} else if (_animationName == "controls") {
+		switch (_corePower[_coreIndex]) {
+		case 0: SetObjectState(2, 1); SetObjectState(5, 1); break;
+		case 1: SetObjectState(2, 1); SetObjectState(5, 2); break;
+		case 2: SetObjectState(2, 2); SetObjectState(5, 1); break;
+		}
+	}
+
+	while (_animationRunning && !shouldQuit()) {
+		updateAnimation();
 		drawAnimation();
 		_gfx->copyToScreen();
 
@@ -432,20 +468,39 @@ void ColonyEngine::playAnimation() {
 				int item = whichSprite(event.mouse);
 				if (item > 0) {
 					handleAnimationClick(item);
-				} else {
-					// Clicked background or nothing? Exit for now as a safeguard
-					running = false;
 				}
 			} else if (event.type == Common::EVENT_KEYDOWN) {
 				if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
-					running = false;
+					_animationRunning = false;
 			}
 		}
 		_system->delayMillis(20);
 	}
+
+	_system->lockMouse(true);
+	_system->showMouse(false);
 	deleteAnimation();
 }
 
+void ColonyEngine::updateAnimation() {
+	static uint32 lastUpdate = 0;
+	uint32 now = _system->getMillis();
+	if (now - lastUpdate < 50) // Reduced to 50ms (20 fps) to make it "move"
+		return;
+	lastUpdate = now;
+
+	for (uint i = 0; i < _lSprites.size(); i++) {
+		ComplexSprite *ls = _lSprites[i];
+		// type 0 are displays that auto-animate
+		// Original NoShowIt ONLY checked !ls->locked
+		if (ls->onoff && ls->type == 0 && !ls->locked && ls->objects.size() > 1) {
+			ls->current++;
+			if (ls->current >= (int)ls->objects.size())
+				ls->current = 0;
+		}
+	}
+}
+
 void ColonyEngine::drawAnimation() {
 	_gfx->clear(0); // Ensure entire screen is black
 
@@ -453,9 +508,20 @@ void ColonyEngine::drawAnimation() {
 	int ox = (_width - 320) / 2;
 	int oy = (_height - 200) / 2;
 
+	// Fill background patterns
+	for (int y = 0; y < 200; y++) {
+		byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
+		byte row = pat[y % 8];
+		for (int x = 0; x < 320; x++) {
+			bool set = (row & (0x80 >> (x % 8))) != 0;
+			// Inverted as in original readanim
+			_gfx->setPixel(ox + x, oy + y, set ? 0 : 15);
+		}
+	}
+
 	// Draw background if active
 	if (_backgroundActive && _backgroundFG) {
-		drawAnimationImage(_backgroundFG, nullptr, ox + _backgroundLocate.left, oy + _backgroundLocate.top);
+		drawAnimationImage(_backgroundFG, _backgroundMask, ox + _backgroundLocate.left, oy + _backgroundLocate.top);
 	}
 
 	// Draw complex sprites
@@ -562,8 +628,215 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 }
 
 void ColonyEngine::handleAnimationClick(int item) {
-	// Dummy for now - real logic depends on specific animation
 	debug("Animation click on item %d", item);
+
+	if (_animationName == "desk") {
+		if (item >= 2 && item <= 5) {
+			int idx = item - 2;
+			uint8 *decode = (_level == 1) ? _decode2 : _decode3;
+			if (decode[idx] == 0) {
+				decode[idx] = (uint8)(2 + (_randomSource.getRandomNumber(3)));
+				_lSprites[item - 1]->current = decode[idx] - 1;
+				drawAnimation();
+				_gfx->copyToScreen();
+			}
+			return;
+		}
+	} else if (_animationName == "reactor" || _animationName == "security") {
+		if (item >= 1 && item <= 10) {
+			for (int i = 5; i >= 1; i--) _animDisplay[i] = _animDisplay[i - 1];
+			_animDisplay[0] = (uint8)(item + 1);
+			refreshAnimationDisplay();
+			drawAnimation();
+			_gfx->copyToScreen();
+			return;
+		} else if (item == 11) { // Clear
+			for (int i = 0; i < 6; i++) _animDisplay[i] = 1;
+			refreshAnimationDisplay();
+			drawAnimation();
+			_gfx->copyToScreen();
+			return;
+		} else if (item == 12) { // Enter
+			uint8 testarray[6];
+			if (_animationName == "reactor") {
+				if (_level == 1)
+					crypt(testarray, _decode2[3] - 2, _decode2[2] - 2, _decode2[1] - 2, _decode2[0] - 2);
+				else
+					crypt(testarray, _decode3[3] - 2, _decode3[2] - 2, _decode3[1] - 2, _decode3[0] - 2);
+
+				bool match = true;
+				for (int i = 0; i < 6; i++) {
+					if (testarray[i] != _animDisplay[5 - i]) match = false;
+				}
+				if (match) {
+					if (_coreState[_coreIndex] == 0) _coreState[_coreIndex] = 1;
+					else if (_coreState[_coreIndex] == 1) _coreState[_coreIndex] = 0;
+					_gametest = true;
+				}
+				_animationRunning = false;
+			} else { // security
+				crypt(testarray, _decode1[0] - 2, _decode1[1] - 2, _decode1[2] - 2, _decode1[3] - 2);
+				bool match = true;
+				for (int i = 0; i < 6; i++) {
+					if (testarray[i] != _animDisplay[5 - i]) match = false;
+				}
+				if (match) {
+					_unlocked = true;
+					_gametest = true;
+				}
+				_animationRunning = false;
+			}
+			return;
+		}
+	} else if (_animationName == "controls") {
+		switch (item) {
+		case 4: // Accelerator
+			if (_corePower[_coreIndex] >= 2 && _coreState[_coreIndex] == 0 && !_orbit) {
+				_orbit = 1;
+				debug("Taking off!");
+				_animationRunning = false;
+			} else {
+				debug("Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
+			}
+			break;
+		case 5: // Emergency power
+			if (_coreState[_coreIndex] < 2) {
+				if (_corePower[_coreIndex] == 0) _corePower[_coreIndex] = 1;
+				else if (_corePower[_coreIndex] == 1) _corePower[_coreIndex] = 0;
+			}
+			// Update power visual state
+			switch (_corePower[_coreIndex]) {
+			case 0: SetObjectState(2, 1); SetObjectState(5, 1); break;
+			case 1: SetObjectState(2, 1); SetObjectState(5, 2); break;
+			case 2: SetObjectState(2, 2); SetObjectState(5, 1); break;
+			}
+			drawAnimation();
+			_gfx->copyToScreen();
+			break;
+		case 6: // Ship weapons
+			if (!_orbit) {
+				debug("Firing ship weapons on ground! Game Over soon.");
+				_animationRunning = false;
+			} else {
+				debug("Firing ship weapons in orbit.");
+				_animationRunning = false;
+			}
+			break;
+		case 7: // Damage report
+			debug("Damage report button clicked.");
+			break;
+		}
+	} else if (_animationName == "suit") {
+		if (item == 1) { // Armor
+			_armor = (_armor + 1) % 4;
+			SetObjectState(1, _armor * 2 + 1);
+			SetObjectState(3, _armor + 1);
+			drawAnimation();
+			_gfx->copyToScreen();
+		} else if (item == 2) { // Weapons
+			_weapons = (_weapons + 1) % 4;
+			SetObjectState(2, _weapons * 2 + 1);
+			SetObjectState(4, _weapons + 1);
+			drawAnimation();
+			_gfx->copyToScreen();
+		}
+	}
+
+	dolSprite(item - 1);
+}
+
+void ColonyEngine::dolSprite(int index) {
+	if (index < 0 || index >= (int)_lSprites.size())
+		return;
+
+	ComplexSprite *ls = _lSprites[index];
+	switch (ls->type) {
+	case 1: // Key and control
+		if (ls->frozen) {
+			// Control logic
+			if (!ls->locked || ls->current) {
+				if (ls->current > 0) {
+					while (ls->current > 0) {
+						ls->current--;
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				} else {
+					while (ls->current < (int)ls->objects.size() - 1) {
+						ls->current++;
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				}
+			}
+		}
+		break;
+	case 2: // Container and object
+		if (ls->frozen) {
+			if (!ls->locked) {
+				if (ls->current > 0) {
+					while (ls->current > 0) {
+						ls->current--;
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				} else {
+					while (ls->current < (int)ls->objects.size() - 1) {
+						ls->current++;
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				}
+			}
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+void ColonyEngine::SetObjectState(int num, int state) {
+	num--;
+	if (num >= 0 && num < (int)_lSprites.size())
+		_lSprites[num]->current = state - 1;
+}
+
+void ColonyEngine::SetObjectOnOff(int num, bool on) {
+	num--;
+	if (num >= 0 && num < (int)_lSprites.size())
+		_lSprites[num]->onoff = on;
+}
+
+void ColonyEngine::refreshAnimationDisplay() {
+	for (int i = 0; i < 6; i++) {
+		if (_animDisplay[i] < 9) {
+			SetObjectOnOff(13 + i * 2, true);
+			SetObjectOnOff(14 + i * 2, false);
+			SetObjectState(13 + i * 2, _animDisplay[i]);
+		} else {
+			SetObjectOnOff(14 + i * 2, true);
+			SetObjectOnOff(13 + i * 2, false);
+			SetObjectState(14 + i * 2, _animDisplay[i] - 8);
+		}
+	}
+}
+
+void ColonyEngine::crypt(uint8 sarray[6], int i, int j, int k, int l) {
+	int res[6];
+	res[0] = ((3 * l) ^ i ^ j ^ k) % 10;
+	res[1] = ((i * 3) ^ (j * 7) ^ (k * 11) ^ (l * 13)) % 10;
+	res[2] = (3 + (l * 17) ^ (j * 19) ^ (k * 23) ^ (i * 29)) % 10;
+	res[3] = ((l * 19) ^ (j * 23) ^ (k * 29) ^ (i * 31)) % 10;
+	res[4] = ((l * 17) | (j * 19) | (k * 23) | (i * 29)) % 10;
+	res[5] = (29 + (l * 17) - (j * 19) - (k * 23) - (i * 29)) % 10;
+	for (int m = 0; m < 6; m++) {
+		if (res[m] < 0) res[m] = -res[m];
+		sarray[m] = (uint8)(res[m] + 2);
+	}
 }
 
 } // End of namespace Colony
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 19e898b3023..3b8b28f9cb9 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -25,6 +25,7 @@
 #include "engines/engine.h"
 #include "engines/advancedDetector.h"
 #include "common/array.h"
+#include "common/random.h"
 #include "common/rect.h"
 #include "colony/gfx.h"
 
@@ -209,6 +210,19 @@ private:
 	bool _wireframe;
 	bool _widescreen;
 
+	Common::RandomSource _randomSource;
+	uint8 _decode1[4];
+	uint8 _decode2[4];
+	uint8 _decode3[4];
+	uint8 _animDisplay[6];
+	int _coreState[2];
+	int _coreHeight[2];
+	int _corePower[3];
+	int _coreIndex;
+	int _orbit;
+	int _armor;
+	bool _gametest;
+
 	int _frntxWall, _frntyWall;
 	int _sidexWall, _sideyWall;
 	int _frntx, _frnty;
@@ -265,10 +279,13 @@ private:
 	byte _topBG[8];
 	byte _bottomBG[8];
 	int16 _divideBG;
+	Common::String _animationName;
+	bool _animationRunning;
 
 	bool loadAnimation(const Common::String &name);
 	void deleteAnimation();
 	void playAnimation();
+	void updateAnimation();
 	void drawAnimation();
 	void drawComplexSprite(int index, int ox, int oy);
 	void drawAnimationImage(Image *img, Image *mask, int x, int y);
@@ -277,6 +294,11 @@ private:
 	Common::Rect readRect(Common::SeekableReadStream &file);
 	int whichSprite(const Common::Point &p);
 	void handleAnimationClick(int item);
+	void dolSprite(int index);
+	void SetObjectState(int num, int state);
+	void SetObjectOnOff(int num, bool on);
+	void refreshAnimationDisplay();
+	void crypt(uint8 sarray[6], int i, int j, int k, int l);
 };
 
 } // End of namespace Colony
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 62a6313879b..688e54453ee 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -506,9 +506,8 @@ void ColonyEngine::interactWithObject(int objNum) {
 		}
 		break;
 	case kObjPowerSuit:
-		_weapons = MAX(_weapons, 1);
-		_crosshair = true;
-		debug("CCommand: POWERSUIT");
+		if (loadAnimation("suit"))
+			playAnimation();
 		break;
 	case kObjTeleport:
 	{


Commit: e667e8c1da008b0ed971c82cbd7a0ae4b4d3f292
    https://github.com/scummvm/scummvm/commit/e667e8c1da008b0ed971c82cbd7a0ae4b4d3f292
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:11+02:00

Commit Message:
COLONY: add mouse click handling and center animation viewport

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index d8eaefb27fd..e709c27c6da 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -35,6 +35,7 @@
 #include "graphics/fontman.h"
 #include "graphics/font.h"
 #include "graphics/fonts/dosfont.h"
+#include "graphics/cursorman.h"
 #include <math.h>
 
 namespace Colony {
@@ -46,7 +47,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_robotNum = 0;
 	_gfx = nullptr;
 	_width = 640;
-	_height = 480;
+	_height = 350;
 	_centerX = _width / 2;
 	_centerY = _height / 2;
 	_mouseSensitivity = 1;
@@ -190,7 +191,7 @@ void ColonyEngine::initTrig() {
 
 Common::Error ColonyEngine::run() {
 	_width = 640;
-	_height = 480;
+	_height = 350;
 
 	if (_widescreen) {
 		_width = _height * 16 / 9;
@@ -438,6 +439,10 @@ void ColonyEngine::playAnimation() {
 	_animationRunning = true;
 	_system->lockMouse(false);
 	_system->showMouse(true);
+	_system->warpMouse(_centerX, _centerY);
+	CursorMan.setDefaultArrowCursor(true);
+	CursorMan.showMouse(true);
+	_system->updateScreen();
 
 	if (_animationName == "security" && !_unlocked) {
 		for (int i = 0; i < 4; i++) {
@@ -469,9 +474,25 @@ void ColonyEngine::playAnimation() {
 				if (item > 0) {
 					handleAnimationClick(item);
 				}
+			} else if (event.type == Common::EVENT_MOUSEMOVE) {
+				debug(5, "Animation Mouse: %d, %d", event.mouse.x, event.mouse.y);
 			} else if (event.type == Common::EVENT_KEYDOWN) {
-				if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
+				int item = 0;
+				if (event.kbd.keycode >= Common::KEYCODE_0 && event.kbd.keycode <= Common::KEYCODE_9) {
+					item = 1 + (event.kbd.keycode - Common::KEYCODE_0);
+				} else if (event.kbd.keycode >= Common::KEYCODE_KP0 && event.kbd.keycode <= Common::KEYCODE_KP9) {
+					item = 1 + (event.kbd.keycode - Common::KEYCODE_KP0);
+				} else if (event.kbd.keycode == Common::KEYCODE_RETURN || event.kbd.keycode == Common::KEYCODE_KP_ENTER) {
+					item = 12; // Enter
+				} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE || event.kbd.keycode == Common::KEYCODE_DELETE) {
+					item = 11; // Clear
+				} else if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					_animationRunning = false;
+				}
+
+				if (item > 0) {
+					handleAnimationClick(item);
+				}
 			}
 		}
 		_system->delayMillis(20);
@@ -479,6 +500,8 @@ void ColonyEngine::playAnimation() {
 
 	_system->lockMouse(true);
 	_system->showMouse(false);
+	CursorMan.showMouse(false);
+	CursorMan.popAllCursors();
 	deleteAnimation();
 }
 
@@ -504,18 +527,20 @@ void ColonyEngine::updateAnimation() {
 void ColonyEngine::drawAnimation() {
 	_gfx->clear(0); // Ensure entire screen is black
 
-	// Center 320x200 animation on screen
-	int ox = (_width - 320) / 2;
-	int oy = (_height - 200) / 2;
+	// Center 416x264 animation area on screen (from original InitDejaVu)
+	int ox = (_width - 416) / 2;
+	ox = (ox / 8) * 8; // Round to 8 as in original code
+	int oy = (_height - 264) / 2;
 
-	// Fill background patterns
-	for (int y = 0; y < 200; y++) {
+	// Fill background patterns (416x264 area)
+	for (int y = 0; y < 264; y++) {
 		byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
 		byte row = pat[y % 8];
-		for (int x = 0; x < 320; x++) {
+		for (int x = 0; x < 416; x++) {
 			bool set = (row & (0x80 >> (x % 8))) != 0;
-			// Inverted as in original readanim
-			_gfx->setPixel(ox + x, oy + y, set ? 0 : 15);
+			// Pattern bit 1 is background color (15), bit 0 is foreground (0)
+			// matching original FillRect with inverted data.
+			_gfx->setPixel(ox + x, oy + y, set ? 15 : 0);
 		}
 	}
 
@@ -615,8 +640,9 @@ Common::Rect ColonyEngine::readRect(Common::SeekableReadStream &file) {
 }
 
 int ColonyEngine::whichSprite(const Common::Point &p) {
-	int ox = (_width - 320) / 2;
-	int oy = (_height - 200) / 2;
+	int ox = (_width - 416) / 2;
+	ox = (ox / 8) * 8;
+	int oy = (_height - 264) / 2;
 	Common::Point pt(p.x - ox, p.y - oy);
 
 	for (int i = _lSprites.size() - 1; i >= 0; i--) {


Commit: 4c14336caf6143a01462019be495d5ef2843d4c6
    https://github.com/scummvm/scummvm/commit/4c14336caf6143a01462019be495d5ef2843d4c6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:11+02:00

Commit Message:
COLONY: implement sprite rendering with per-pixel clipping for animations

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index e709c27c6da..1ca18ea74e0 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -645,16 +645,59 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 	int oy = (_height - 264) / 2;
 	Common::Point pt(p.x - ox, p.y - oy);
 
+	debug(1, "Click at (%d, %d), relative (%d, %d)", p.x, p.y, pt.x, pt.y);
+
 	for (int i = _lSprites.size() - 1; i >= 0; i--) {
-		if (_lSprites[i]->onoff && _lSprites[i]->bounds.contains(pt)) {
-			return i + 1;
+		ComplexSprite *ls = _lSprites[i];
+		if (ls->onoff) {
+			int cnum = ls->current;
+			if (cnum < 0 || cnum >= (int)ls->objects.size()) continue;
+
+			int spriteIdx = ls->objects[cnum].spritenum;
+			if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size()) continue;
+
+			Sprite *s = _cSprites[spriteIdx];
+			int xloc = ls->xloc + ls->objects[cnum].xloc;
+			int yloc = ls->yloc + ls->objects[cnum].yloc;
+
+			Common::Rect r = s->clip;
+			r.translate(xloc, yloc);
+
+			if (r.contains(pt)) {
+				debug(1, "Sprite %d hit. Frame %d, Base Sprite %d. Box: (%d, %d, %d, %d)", i + 1,
+					cnum, spriteIdx, r.left, r.top, r.right, r.bottom);
+				return i + 1;
+			}
+		}
+	}
+
+	// Dump accurately calculated bounds if debug is high enough
+	if (gDebugLevel >= 2) {
+		for (int i = 0; i < (int)_lSprites.size(); i++) {
+			ComplexSprite *ls = _lSprites[i];
+			if (ls->onoff) {
+				int cnum = ls->current;
+				if (cnum < 0 || cnum >= (int)ls->objects.size()) continue;
+				int spriteIdx = ls->objects[cnum].spritenum;
+				if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size()) continue;
+				Sprite *s = _cSprites[spriteIdx];
+
+				int xloc = ls->xloc + ls->objects[cnum].xloc;
+				int yloc = ls->yloc + ls->objects[cnum].yloc;
+				Common::Rect r = s->clip;
+				r.translate(xloc, yloc);
+
+				debug(2, "  Sprite %d: Frame=%d Box=(%d,%d,%d,%d)", i + 1,
+					cnum, r.left, r.top, r.right, r.bottom);
+			}
 		}
 	}
+
 	return 0;
 }
 
 void ColonyEngine::handleAnimationClick(int item) {
-	debug("Animation click on item %d", item);
+	debug(0, "Animation click on item %d in %s", item, _animationName.c_str());
 
 	if (_animationName == "desk") {
 		if (item >= 2 && item <= 5) {
@@ -666,23 +709,23 @@ void ColonyEngine::handleAnimationClick(int item) {
 				drawAnimation();
 				_gfx->copyToScreen();
 			}
-			return;
 		}
-	} else if (_animationName == "reactor" || _animationName == "security") {
-		if (item >= 1 && item <= 10) {
-			for (int i = 5; i >= 1; i--) _animDisplay[i] = _animDisplay[i - 1];
+	} else if (_animationName == "reactor" || _animationName == "security" || _animationName == "suit") {
+		if (item >= 1 && item <= 10 && _animationName != "suit") {
+			for (int i = 5; i >= 1; i--)
+				_animDisplay[i] = _animDisplay[i - 1];
 			_animDisplay[0] = (uint8)(item + 1);
 			refreshAnimationDisplay();
 			drawAnimation();
 			_gfx->copyToScreen();
-			return;
-		} else if (item == 11) { // Clear
-			for (int i = 0; i < 6; i++) _animDisplay[i] = 1;
+			// Don't return, let dolSprite animate the button
+		} else if (item == 11 && _animationName != "suit") { // Clear
+			for (int i = 0; i < 6; i++)
+				_animDisplay[i] = 1;
 			refreshAnimationDisplay();
 			drawAnimation();
 			_gfx->copyToScreen();
-			return;
-		} else if (item == 12) { // Enter
+		} else if (item == 12 && _animationName != "suit") { // Enter
 			uint8 testarray[6];
 			if (_animationName == "reactor") {
 				if (_level == 1)
@@ -692,19 +735,23 @@ void ColonyEngine::handleAnimationClick(int item) {
 
 				bool match = true;
 				for (int i = 0; i < 6; i++) {
-					if (testarray[i] != _animDisplay[5 - i]) match = false;
+					if (testarray[i] != _animDisplay[5 - i])
+						match = false;
 				}
 				if (match) {
-					if (_coreState[_coreIndex] == 0) _coreState[_coreIndex] = 1;
-					else if (_coreState[_coreIndex] == 1) _coreState[_coreIndex] = 0;
+					if (_coreState[_coreIndex] == 0)
+						_coreState[_coreIndex] = 1;
+					else if (_coreState[_coreIndex] == 1)
+						_coreState[_coreIndex] = 0;
 					_gametest = true;
 				}
 				_animationRunning = false;
-			} else { // security
+			} else if (_animationName == "security") { // security
 				crypt(testarray, _decode1[0] - 2, _decode1[1] - 2, _decode1[2] - 2, _decode1[3] - 2);
 				bool match = true;
 				for (int i = 0; i < 6; i++) {
-					if (testarray[i] != _animDisplay[5 - i]) match = false;
+					if (testarray[i] != _animDisplay[5 - i])
+						match = false;
 				}
 				if (match) {
 					_unlocked = true;
@@ -712,23 +759,43 @@ void ColonyEngine::handleAnimationClick(int item) {
 				}
 				_animationRunning = false;
 			}
-			return;
+		} else if (_animationName == "suit") {
+			if (item == 1) { // Armor
+				// Animation: toggle button state frame by frame
+				SetObjectState(1, (_armor * 2 + 1) + 1); // intermediate/pressed
+				drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(50);
+				_armor = (_armor + 1) % 4;
+				SetObjectState(1, _armor * 2 + 1); // target state
+				SetObjectState(3, _armor + 1); // display state
+				drawAnimation(); _gfx->copyToScreen();
+				if (_armor == 3 && _weapons == 3) _corePower[_coreIndex] = 2;
+			} else if (item == 2) { // Weapons
+				SetObjectState(2, (_weapons * 2 + 1) + 1); // intermediate/pressed
+				drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(50);
+				_weapons = (_weapons + 1) % 4;
+				SetObjectState(2, _weapons * 2 + 1);
+				SetObjectState(4, _weapons + 1);
+				drawAnimation(); _gfx->copyToScreen();
+				if (_armor == 3 && _weapons == 3) _corePower[_coreIndex] = 2;
+			}
 		}
 	} else if (_animationName == "controls") {
 		switch (item) {
 		case 4: // Accelerator
 			if (_corePower[_coreIndex] >= 2 && _coreState[_coreIndex] == 0 && !_orbit) {
 				_orbit = 1;
-				debug("Taking off!");
+				debug(0, "Taking off!");
 				_animationRunning = false;
 			} else {
-				debug("Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
+				debug(0, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
 			}
 			break;
 		case 5: // Emergency power
 			if (_coreState[_coreIndex] < 2) {
-				if (_corePower[_coreIndex] == 0) _corePower[_coreIndex] = 1;
-				else if (_corePower[_coreIndex] == 1) _corePower[_coreIndex] = 0;
+				if (_corePower[_coreIndex] == 0)
+					_corePower[_coreIndex] = 1;
+				else if (_corePower[_coreIndex] == 1)
+					_corePower[_coreIndex] = 0;
 			}
 			// Update power visual state
 			switch (_corePower[_coreIndex]) {
@@ -741,34 +808,31 @@ void ColonyEngine::handleAnimationClick(int item) {
 			break;
 		case 6: // Ship weapons
 			if (!_orbit) {
-				debug("Firing ship weapons on ground! Game Over soon.");
+				debug(0, "Firing ship weapons on ground! Game Over soon.");
 				_animationRunning = false;
+				_system->quit(); // Triggering end of game for now
 			} else {
-				debug("Firing ship weapons in orbit.");
+				debug(0, "Firing ship weapons in orbit.");
 				_animationRunning = false;
 			}
 			break;
 		case 7: // Damage report
-			debug("Damage report button clicked.");
+			debug(0, "Damage report button clicked.");
 			break;
 		}
-	} else if (_animationName == "suit") {
-		if (item == 1) { // Armor
-			_armor = (_armor + 1) % 4;
-			SetObjectState(1, _armor * 2 + 1);
-			SetObjectState(3, _armor + 1);
-			drawAnimation();
-			_gfx->copyToScreen();
-		} else if (item == 2) { // Weapons
-			_weapons = (_weapons + 1) % 4;
-			SetObjectState(2, _weapons * 2 + 1);
-			SetObjectState(4, _weapons + 1);
-			drawAnimation();
-			_gfx->copyToScreen();
-		}
 	}
 
-	dolSprite(item - 1);
+	if (item > 0) {
+		dolSprite(item - 1);
+		// After dolSprite, many buttons return to state 1 (unpressed)
+		if (_animationName == "reactor" || _animationName == "security") {
+			if (item <= 12) {
+				SetObjectState(item, 1);
+				drawAnimation();
+				_gfx->copyToScreen();
+			}
+		}
+	}
 }
 
 void ColonyEngine::dolSprite(int index) {


Commit: 48916b5d938231bfa584ec6285132b74c9ee1915
    https://github.com/scummvm/scummvm/commit/48916b5d938231bfa584ec6285132b74c9ee1915
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:12+02:00

Commit Message:
COLONY: allow to turn lights on

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/render.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 1ca18ea74e0..95b04a6803d 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -57,7 +57,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_hasKeycard = false;
 	_unlocked = false;
 	_weapons = 0;
-	_wireframe = true;
+	_wireframe = false;
 	_widescreen = ConfMan.getBool("widescreen_mod");
 	
 	memset(_wall, 0, sizeof(_wall));
@@ -99,6 +99,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_orbit = 0;
 	_armor = 0;
 	_gametest = false;
+	_blackoutColor = 15; // Set to white (vINTWHITE) for better visibility in darkness
 
 	initTrig();
 }
@@ -280,7 +281,7 @@ Common::Error ColonyEngine::run() {
 					default:
 						break;
 					}
-				debug("Me: x=%d y=%d look=%d", _me.xloc, _me.yloc, _me.look);
+				debug("Me: x=%d y=%d", _me.xloc, _me.yloc);
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
 				if (event.relMouse.x != 0) {
 					_me.look = (uint8)((int)_me.look - (event.relMouse.x / 2));
@@ -297,7 +298,7 @@ Common::Error ColonyEngine::run() {
 			}
 		}
 
-		_gfx->clear(_gfx->black());
+		_gfx->clear((_corePower[_coreIndex] > 0) ? 15 : 0);
 		
 		corridor();
 		drawDashboardStep1();
@@ -525,7 +526,7 @@ void ColonyEngine::updateAnimation() {
 }
 
 void ColonyEngine::drawAnimation() {
-	_gfx->clear(0); // Ensure entire screen is black
+	corridor(); // Draw 3D world backdrop first
 
 	// Center 416x264 animation area on screen (from original InitDejaVu)
 	int ox = (_width - 416) / 2;
@@ -697,6 +698,12 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 }
 
 void ColonyEngine::handleAnimationClick(int item) {
+	uint32 now = _system->getMillis();
+	if (now - _lastClickTime < 250) {
+		debug("Ignoring rapid click on item %d", item);
+		return;
+	}
+	_lastClickTime = now;
 	debug(0, "Animation click on item %d in %s", item, _animationName.c_str());
 
 	if (_animationName == "desk") {
@@ -710,6 +717,7 @@ void ColonyEngine::handleAnimationClick(int item) {
 				_gfx->copyToScreen();
 			}
 		}
+		return; // Handled
 	} else if (_animationName == "reactor" || _animationName == "security" || _animationName == "suit") {
 		if (item >= 1 && item <= 10 && _animationName != "suit") {
 			for (int i = 5; i >= 1; i--)
@@ -761,31 +769,63 @@ void ColonyEngine::handleAnimationClick(int item) {
 			}
 		} else if (_animationName == "suit") {
 			if (item == 1) { // Armor
-				// Animation: toggle button state frame by frame
-				SetObjectState(1, (_armor * 2 + 1) + 1); // intermediate/pressed
-				drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(50);
-				_armor = (_armor + 1) % 4;
+				if (_armor == 3) {
+					for (int i = 6; i >= 1; i--) {
+						SetObjectState(1, i);
+						SetObjectState(3, i / 2 + 1);
+						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(30);
+					}
+					_armor = 0;
+				} else {
+					SetObjectState(1, (_armor * 2 + 1) + 1); // intermediate/pressed
+					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(50);
+					_armor++;
+				}
 				SetObjectState(1, _armor * 2 + 1); // target state
 				SetObjectState(3, _armor + 1); // display state
 				drawAnimation(); _gfx->copyToScreen();
 				if (_armor == 3 && _weapons == 3) _corePower[_coreIndex] = 2;
 			} else if (item == 2) { // Weapons
-				SetObjectState(2, (_weapons * 2 + 1) + 1); // intermediate/pressed
-				drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(50);
-				_weapons = (_weapons + 1) % 4;
+				if (_weapons == 3) {
+					for (int i = 6; i >= 1; i--) {
+						SetObjectState(2, i);
+						SetObjectState(4, i / 2 + 1);
+						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(30);
+					}
+					_weapons = 0;
+				} else {
+					SetObjectState(2, (_weapons * 2 + 1) + 1); // intermediate/pressed
+					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(50);
+					_weapons++;
+				}
 				SetObjectState(2, _weapons * 2 + 1);
 				SetObjectState(4, _weapons + 1);
 				drawAnimation(); _gfx->copyToScreen();
 				if (_armor == 3 && _weapons == 3) _corePower[_coreIndex] = 2;
 			}
 		}
+		if (_animationName == "reactor" || _animationName == "security") {
+			if (item <= 12) {
+				dolSprite(item - 1); // Animate the button press
+				SetObjectState(item, 1); // Return to unpressed state
+				drawAnimation();
+				_gfx->copyToScreen();
+			}
+		}
+		return; // Handled
 	} else if (_animationName == "controls") {
 		switch (item) {
 		case 4: // Accelerator
 			if (_corePower[_coreIndex] >= 2 && _coreState[_coreIndex] == 0 && !_orbit) {
 				_orbit = 1;
 				debug(0, "Taking off!");
+				// Animate the lever moving full range
+				for (int i = 1; i <= 6; i++) {
+					SetObjectState(4, i);
+					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(30);
+				}
 				_animationRunning = false;
+				return; // Exit animation immediately on success
 			} else {
 				debug(0, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
 			}
@@ -796,8 +836,11 @@ void ColonyEngine::handleAnimationClick(int item) {
 					_corePower[_coreIndex] = 1;
 				else if (_corePower[_coreIndex] == 1)
 					_corePower[_coreIndex] = 0;
+			} else {
+				// If power is already high/locked, ensure visual state is correct
+				SetObjectState(5, 1);
 			}
-			// Update power visual state
+			// Update power visual state:
 			switch (_corePower[_coreIndex]) {
 			case 0: SetObjectState(2, 1); SetObjectState(5, 1); break;
 			case 1: SetObjectState(2, 1); SetObjectState(5, 2); break;
@@ -806,20 +849,24 @@ void ColonyEngine::handleAnimationClick(int item) {
 			drawAnimation();
 			_gfx->copyToScreen();
 			break;
-		case 6: // Ship weapons
-			if (!_orbit) {
-				debug(0, "Firing ship weapons on ground! Game Over soon.");
-				_animationRunning = false;
-				_system->quit(); // Triggering end of game for now
-			} else {
-				debug(0, "Firing ship weapons in orbit.");
-				_animationRunning = false;
-			}
-			break;
 		case 7: // Damage report
-			debug(0, "Damage report button clicked.");
+		{
+			const char *msg = "SHIP STATUS: NORMAL";
+			if (_corePower[_coreIndex] < 2) msg = "CRITICAL: MAIN POWER OFFLINE. EMERGENCY POWER ACTIVE.";
+			else if (!_orbit) msg = "ALL SYSTEMS NOMINAL. READY FOR LIFTOFF.";
+			else msg = "ORBITAL STABILIZATION AT 100%.";
+			
+			debug(0, "Damage Report: %s", msg);
+			
+			// Simple on-screen display for messages
+			Graphics::DosFont dosFont;
+			uint32 textColor = (_corePower[_coreIndex] > 0) ? 0 : 15;
+			_gfx->drawString(&dosFont, msg, _width / 2, _height - 20, textColor, Graphics::kTextAlignCenter);
+			_gfx->copyToScreen();
+			_system->delayMillis(2000);
 			break;
 		}
+		}
 	}
 
 	if (item > 0) {
@@ -831,10 +878,34 @@ void ColonyEngine::handleAnimationClick(int item) {
 				drawAnimation();
 				_gfx->copyToScreen();
 			}
+		} else if (_animationName == "controls") {
+			if (item == 4) { // Accelerator lever returns to state 1
+				for (int i = 6; i > 0; i--) {
+					SetObjectState(4, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(20);
+				}
+			}
 		}
 	}
 }
 
+void ColonyEngine::terminateGame(bool blowup) {
+	debug(0, "YOU HAVE BEEN TERMINATED! (blowup=%d)", blowup);
+	
+	// Flash effect
+	for (int i = 0; i < 4; i++) {
+		_gfx->clear(i % 2 == 0 ? 15 : 0);
+		_gfx->copyToScreen();
+		_system->delayMillis(100);
+	}
+
+	// In a real implementation we would show a menu here
+	// For now, just quit as requested by the user
+	_system->quit();
+}
+
 void ColonyEngine::dolSprite(int index) {
 	if (index < 0 || index >= (int)_lSprites.size())
 		return;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 3b8b28f9cb9..6edc7b64fd8 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -187,7 +187,7 @@ private:
 	uint8 _mapData[31][31][5][5];
 	uint8 _robotArray[32][32];
 	uint8 _foodArray[32][32];
-	
+
 	Locate _me;
 	Common::Array<Thing> _objects;
 	int _level;
@@ -219,9 +219,11 @@ private:
 	int _coreHeight[2];
 	int _corePower[3];
 	int _coreIndex;
-	int _orbit;
-	int _armor;
-	bool _gametest;
+	int _orbit = 0;
+	int _armor = 0;
+	bool _gametest = false;
+	uint32 _blackoutColor = 0;
+	uint32 _lastClickTime = 0;
 
 	int _frntxWall, _frntyWall;
 	int _sidexWall, _sideyWall;
@@ -267,6 +269,7 @@ private:
 	void drawDashboardStep1();
 	void drawCrosshair();
 	bool clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const;
+	void wallLine(const float corners[4][3], float u1, float v1, float u2, float v2, uint32 color);
 
 	// Animation system
 	Common::Array<Sprite *> _cSprites;
@@ -299,6 +302,7 @@ private:
 	void SetObjectOnOff(int num, bool on);
 	void refreshAnimationDisplay();
 	void crypt(uint8 sarray[6], int i, int j, int k, int l);
+	void terminateGame(bool blowup);
 };
 
 } // End of namespace Colony
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 4ae0107b321..8a24487d082 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -442,7 +442,8 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 		}
 
 		if (count >= 3) {
-			_gfx->draw3DPolygon(px, py, pz, count, color);
+			uint32 finalColor = (_corePower[_coreIndex] > 0) ? 0 : 15;
+			_gfx->draw3DPolygon(px, py, pz, count, finalColor);
 		}
 	}
 }
@@ -516,11 +517,12 @@ static void wallPoint(const float corners[4][3], float u, float v, float out[3])
 }
 
 // Draw a line on a wall face using normalized (u,v) coordinates
-static void wallLine(Renderer *gfx, const float corners[4][3], float u1, float v1, float u2, float v2, uint32 color) {
+void ColonyEngine::wallLine(const float corners[4][3], float u1, float v1, float u2, float v2, uint32 color) {
 	float p1[3], p2[3];
 	wallPoint(corners, u1, v1, p1);
 	wallPoint(corners, u2, v2, p2);
-	gfx->draw3DLine(p1[0], p1[1], p1[2], p2[0], p2[1], p2[2], color);
+	uint32 finalColor = (_corePower[_coreIndex] > 0) ? 0 : 15;
+	_gfx->draw3DLine(p1[0], p1[1], p1[2], p2[0], p2[1], p2[2], finalColor);
 }
 
 void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
@@ -539,20 +541,20 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		float yb = 0.125f, yt = 0.875f;
 		if (map[1] == 0) {
 			// Open door — just the frame
-			wallLine(_gfx, corners, xl, yb, xl, yt, dark);
-			wallLine(_gfx, corners, xl, yt, xr, yt, dark);
-			wallLine(_gfx, corners, xr, yt, xr, yb, dark);
-			wallLine(_gfx, corners, xr, yb, xl, yb, dark);
+			wallLine(corners, xl, yb, xl, yt, dark);
+			wallLine(corners, xl, yt, xr, yt, dark);
+			wallLine(corners, xr, yt, xr, yb, dark);
+			wallLine(corners, xr, yb, xl, yb, dark);
 		} else {
 			// Closed door — frame + handle line
-			wallLine(_gfx, corners, xl, yb, xl, yt, dark);
-			wallLine(_gfx, corners, xl, yt, xr, yt, dark);
-			wallLine(_gfx, corners, xr, yt, xr, yb, dark);
-			wallLine(_gfx, corners, xr, yb, xl, yb, dark);
+			wallLine(corners, xl, yb, xl, yt, dark);
+			wallLine(corners, xl, yt, xr, yt, dark);
+			wallLine(corners, xr, yt, xr, yb, dark);
+			wallLine(corners, xr, yb, xl, yb, dark);
 			// Handle
 			float hx = 0.6f;
 			float hy1 = 0.45f, hy2 = 0.55f;
-			wallLine(_gfx, corners, hx, hy1, hx, hy2, dark);
+			wallLine(corners, hx, hy1, hx, hy2, dark);
 		}
 		break;
 	}
@@ -561,14 +563,14 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		// Window: centered smaller rectangle with cross divider
 		float xl = 0.25f, xr = 0.75f;
 		float yb = 0.375f, yt = 0.75f;
-		wallLine(_gfx, corners, xl, yb, xr, yb, dark);
-		wallLine(_gfx, corners, xr, yb, xr, yt, dark);
-		wallLine(_gfx, corners, xr, yt, xl, yt, dark);
-		wallLine(_gfx, corners, xl, yt, xl, yb, dark);
+		wallLine(corners, xl, yb, xr, yb, dark);
+		wallLine(corners, xr, yb, xr, yt, dark);
+		wallLine(corners, xr, yt, xl, yt, dark);
+		wallLine(corners, xl, yt, xl, yb, dark);
 		// Cross dividers
 		float xc = 0.5f, yc = (yb + yt) * 0.5f;
-		wallLine(_gfx, corners, xc, yb, xc, yt, dark);
-		wallLine(_gfx, corners, xl, yc, xr, yc, dark);
+		wallLine(corners, xc, yb, xc, yt, dark);
+		wallLine(corners, xl, yc, xr, yc, dark);
 		break;
 	}
 	case kWallFeatureShelves: {
@@ -576,14 +578,14 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		// Bookshelf: outer rectangle + horizontal shelf lines
 		float xl = 0.15f, xr = 0.85f;
 		float yb = 0.1f, yt = 0.9f;
-		wallLine(_gfx, corners, xl, yb, xr, yb, dark);
-		wallLine(_gfx, corners, xr, yb, xr, yt, dark);
-		wallLine(_gfx, corners, xr, yt, xl, yt, dark);
-		wallLine(_gfx, corners, xl, yt, xl, yb, dark);
+		wallLine(corners, xl, yb, xr, yb, dark);
+		wallLine(corners, xr, yb, xr, yt, dark);
+		wallLine(corners, xr, yt, xl, yt, dark);
+		wallLine(corners, xl, yt, xl, yb, dark);
 		// 6 shelves
 		for (int i = 1; i <= 6; i++) {
 			float t = yb + (yt - yb) * (float)i / 7.0f;
-			wallLine(_gfx, corners, xl, t, xr, t, dark);
+			wallLine(corners, xl, t, xr, t, dark);
 		}
 		break;
 	}
@@ -596,10 +598,10 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 			float u2 = xl + (xr - xl) * (float)(i + 1) / 6.0f;
 			float v = 0.1f + 0.8f * (float)i / 6.0f;
 			float v2 = 0.1f + 0.8f * (float)(i + 1) / 6.0f;
-			wallLine(_gfx, corners, u, v, u2, v2, dark);
+			wallLine(corners, u, v, u2, v2, dark);
 		}
 		// Side rails
-		wallLine(_gfx, corners, xl, 0.1f, xr, 0.9f, dark);
+		wallLine(corners, xl, 0.1f, xr, 0.9f, dark);
 		break;
 	}
 	case kWallFeatureDnStairs: {
@@ -611,9 +613,9 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 			float u2 = xl + (xr - xl) * (float)(i + 1) / 6.0f;
 			float v = 0.9f - 0.8f * (float)i / 6.0f;
 			float v2 = 0.9f - 0.8f * (float)(i + 1) / 6.0f;
-			wallLine(_gfx, corners, u, v, u2, v2, dark);
+			wallLine(corners, u, v, u2, v2, dark);
 		}
-		wallLine(_gfx, corners, xl, 0.9f, xr, 0.1f, dark);
+		wallLine(corners, xl, 0.9f, xr, 0.1f, dark);
 		break;
 	}
 	case kWallFeatureGlyph: {
@@ -622,7 +624,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		float xl = 0.1f, xr = 0.9f;
 		for (int i = 0; i < 9; i++) {
 			float v = 0.15f + 0.7f * (float)i / 8.0f;
-			wallLine(_gfx, corners, xl, v, xr, v, dark);
+			wallLine(corners, xl, v, xr, v, dark);
 		}
 		break;
 	}
@@ -631,13 +633,13 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		// Elevator: tall rectangle with center divider line
 		float xl = 0.15f, xr = 0.85f;
 		float yb = 0.05f, yt = 0.95f;
-		wallLine(_gfx, corners, xl, yb, xl, yt, dark);
-		wallLine(_gfx, corners, xl, yt, xr, yt, dark);
-		wallLine(_gfx, corners, xr, yt, xr, yb, dark);
-		wallLine(_gfx, corners, xr, yb, xl, yb, dark);
+		wallLine(corners, xl, yb, xl, yt, dark);
+		wallLine(corners, xl, yt, xr, yt, dark);
+		wallLine(corners, xr, yt, xr, yb, dark);
+		wallLine(corners, xr, yb, xl, yb, dark);
 		// Center divider
 		float xc = 0.5f;
-		wallLine(_gfx, corners, xc, yb, xc, yt, dark);
+		wallLine(corners, xc, yb, xc, yt, dark);
 		break;
 	}
 	case kWallFeatureTunnel: {
@@ -655,7 +657,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 			float v1 = 0.1f + pts[i][1] * 0.8f;
 			float u2 = 0.1f + pts[n][0] * 0.8f;
 			float v2 = 0.1f + pts[n][1] * 0.8f;
-			wallLine(_gfx, corners, u1, v1, u2, v2, dark);
+			wallLine(corners, u1, v1, u2, v2, dark);
 		}
 		break;
 	}
@@ -673,14 +675,14 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 			float v1 = 0.1f + pts[i][1] * 0.8f;
 			float u2 = 0.1f + pts[n][0] * 0.8f;
 			float v2 = 0.1f + pts[n][1] * 0.8f;
-			wallLine(_gfx, corners, u1, v1, u2, v2, dark);
+			wallLine(corners, u1, v1, u2, v2, dark);
 		}
 		if (map[1] != 0) {
 			// Closed: add cross lines through center
-			wallLine(_gfx, corners, 0.1f, 0.5f, 0.5f, 0.5f, dark);
-			wallLine(_gfx, corners, 0.5f, 0.1f, 0.5f, 0.5f, dark);
-			wallLine(_gfx, corners, 0.9f, 0.5f, 0.5f, 0.5f, dark);
-			wallLine(_gfx, corners, 0.5f, 0.9f, 0.5f, 0.5f, dark);
+			wallLine(corners, 0.1f, 0.5f, 0.5f, 0.5f, dark);
+			wallLine(corners, 0.5f, 0.1f, 0.5f, 0.5f, dark);
+			wallLine(corners, 0.9f, 0.5f, 0.5f, 0.5f, dark);
+			wallLine(corners, 0.5f, 0.9f, 0.5f, 0.5f, dark);
 		}
 		break;
 	}
@@ -692,7 +694,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 				c = 100 + (_level * 15);
 			}
 			float v = (float)i / 4.0f;
-			wallLine(_gfx, corners, 0.0f, v, 1.0f, v, c);
+			wallLine(corners, 0.0f, v, 1.0f, v, c);
 		}
 		break;
 	}
@@ -716,20 +718,31 @@ void ColonyEngine::drawWallFeatures3D() {
 
 
 void ColonyEngine::renderCorridor3D() {
+	bool lit = (_corePower[_coreIndex] > 0);
+	bool oldWire = _wireframe;
+
+	// Authentic look: Always wireframe for walls. 
+	// Power ON = White background (fill), Black lines.
+	// Power OFF = Black background (fill), White lines.
+	_gfx->setWireframe(true, lit ? 15 : 0);
+
 	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
+	_gfx->clear(lit ? 15 : 0);
  
-	// Draw large black floor and ceiling quads
+	uint32 wallColor = lit ? 0 : 15; 
+	uint32 floorColor = lit ? 0 : 15; 
+
+	// Draw large floor and ceiling quads
+	// These will be filled with the background color in the occlusion pass
 	_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.1f, 
 	                100000.0f, -100000.0f, -160.1f, 
 	                100000.0f, 100000.0f, -160.1f, 
-	                -100000.0f, 100000.0f, -160.1f, 0); // Black floor
+	                -100000.0f, 100000.0f, -160.1f, floorColor);
  
 	_gfx->draw3DQuad(-100000.0f, -100000.0f, 160.1f, 
 	                100000.0f, -100000.0f, 160.1f, 
 	                100000.0f, 100000.0f, 160.1f, 
-	                -100000.0f, 100000.0f, 160.1f, 0); // Black ceiling
- 
-	uint32 wallColor = 15; // White
+	                -100000.0f, 100000.0f, 160.1f, floorColor);
  
 	for (int y = 0; y < 32; y++) {
 		for (int x = 0; x < 32; x++) {
@@ -747,6 +760,7 @@ void ColonyEngine::renderCorridor3D() {
 	drawStaticObjects();
 		
 	_gfx->end3D();
+	_gfx->setWireframe(oldWire);
 }
 
 bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj, uint32 baseColor) {
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index 52ef1458478..9c857d4866c 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -59,7 +59,7 @@ public:
 
 	// Buffer management
 	virtual void copyToScreen() = 0;
-	virtual void setWireframe(bool enable) = 0;
+	virtual void setWireframe(bool enable, int fillColor = -1) = 0;
 	virtual void computeScreenViewport() = 0;
 
 	// Convenience color accessors
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 3f58e88189c..fbb3647b963 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -59,7 +59,10 @@ public:
 	void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) override;
 	void drawPolygon(const int *x, const int *y, int count, uint32 color) override;
 	void copyToScreen() override;
-	void setWireframe(bool enable) override { _wireframe = enable; }
+	void setWireframe(bool enable, int fillColor = -1) override { 
+		_wireframe = enable; 
+		if (fillColor != -1) _wireframeFillColor = (uint32)fillColor;
+	}
 	void computeScreenViewport() override;
 
 private:
@@ -70,11 +73,13 @@ private:
 	int _height;
 	byte _palette[256 * 3];
 	bool _wireframe;
+	uint32 _wireframeFillColor;
 	Common::Rect _screenViewport;
 };
 
 OpenGLRenderer::OpenGLRenderer(OSystem *system, int width, int height) : _system(system), _width(width), _height(height) {
 	_wireframe = true;
+	_wireframeFillColor = 0;
 	memset(_palette, 0, sizeof(_palette));
 	
 	// Default to white for initial colors if setPalette isn't called yet
@@ -231,10 +236,10 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 	float fy2 = y2 * 256.0f;
 
 	if (_wireframe) {
-		// Pass 1: Draw black fill (pushed back)
+		// Pass 1: Draw background fill (pushed back)
 		glEnable(GL_POLYGON_OFFSET_FILL);
 		glPolygonOffset(1.1f, 4.0f);
-		useColor(0);
+		useColor(_wireframeFillColor);
 		glBegin(GL_QUADS);
 		glVertex3f(fx1, fy1, -160.0f);
 		glVertex3f(fx2, fy2, -160.0f);
@@ -274,10 +279,10 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
  
 void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) {
 	if (_wireframe) {
-		// Pass 1: Draw black fill (pushed back)
+		// Pass 1: Draw background fill (pushed back)
 		glEnable(GL_POLYGON_OFFSET_FILL);
 		glPolygonOffset(1.1f, 4.0f);
-		useColor(0);
+		useColor(_wireframeFillColor);
 		glBegin(GL_QUADS);
 		glVertex3f(x1, y1, z1);
 		glVertex3f(x2, y2, z2);
@@ -318,10 +323,10 @@ void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *
 	if (count < 3) return;
 
 	if (_wireframe) {
-		// Pass 1: Draw black fill (pushed back)
+		// Pass 1: Draw background fill (pushed back)
 		glEnable(GL_POLYGON_OFFSET_FILL);
 		glPolygonOffset(1.1f, 4.0f);
-		useColor(0);
+		useColor(_wireframeFillColor);
 		glBegin(GL_POLYGON);
 		for (int i = 0; i < count; i++)
 			glVertex3f(x[i], y[i], z[i]);


Commit: 5738756cb791af5a7e4094b4c61123d99740ae5d
    https://github.com/scummvm/scummvm/commit/5738756cb791af5a7e4094b4c61123d99740ae5d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:12+02:00

Commit Message:
COLONY: add surface color selection for lit/dark render modes

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 8a24487d082..2034f18b7a1 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -171,8 +171,8 @@ static const int kConsolePts[8][3] = {
 	{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
 };
 static const int kConsoleSurf[5][8] = {
-	{0, 4, 4, 0, 3, 7, 0, 0}, {0, 4, 7, 3, 2, 6, 0, 0},
-	{0, 4, 5, 1, 0, 4, 0, 0}, {0, 4, 6, 2, 1, 5, 0, 0},
+	{1, 4, 4, 0, 3, 7, 0, 0}, {1, 4, 7, 3, 2, 6, 0, 0},
+	{1, 4, 5, 1, 0, 4, 0, 0}, {1, 4, 6, 2, 1, 5, 0, 0},
 	{0, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kCouchSurf[5][8] = {
@@ -442,7 +442,17 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 		}
 
 		if (count >= 3) {
-			uint32 finalColor = (_corePower[_coreIndex] > 0) ? 0 : 15;
+			uint32 finalColor;
+			if (_corePower[_coreIndex] > 0) {
+				// Lit mode: use surface-specific colors if defined, else black.
+				if (def.surfaces[i][0] == 1)
+					finalColor = 4; // vRED
+				else
+					finalColor = 0; // vBLACK
+			} else {
+				// Dark mode: everything is white wireframe.
+				finalColor = 15; // vINTWHITE
+			}
 			_gfx->draw3DPolygon(px, py, pz, count, finalColor);
 		}
 	}


Commit: 72e37d927c43c029e6b381120d1f5286a51626bf
    https://github.com/scummvm/scummvm/commit/72e37d927c43c029e6b381120d1f5286a51626bf
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:12+02:00

Commit Message:
COLONY: assign per-object surface color indices across all props

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 2034f18b7a1..ffeaa0f6fb3 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -42,40 +42,40 @@ static const int kScreenPts[8][3] = {
 	{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
 };
 static const int kScreenSurf[4][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
+	{1, 4, 0, 3, 7, 4, 0, 0}, {1, 4, 3, 2, 6, 7, 0, 0},
+	{1, 4, 2, 1, 5, 6, 0, 0}, {1, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kTableTopPts[4][3] = {
 	{-64, 64, 100}, {64, 64, 100}, {64, -64, 100}, {-64, -64, 100}
 };
-static const int kTableTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kTableTopSurf[1][8] = {{6, 4, 3, 2, 1, 0, 0, 0}};
 static const int kTableBasePts[8][3] = {
 	{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0},
 	{-20, 20, 100}, {20, 20, 100}, {20, -20, 100}, {-20, -20, 100}
 };
 static const int kTableBaseSurf[4][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
+	{6, 4, 1, 0, 4, 5, 0, 0}, {6, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kBedPostPts[4][3] = {
 	{-80, 180, 0}, {80, 180, 0}, {80, 180, 100}, {-80, 180, 100}
 };
-static const int kBedPostSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kBedPostSurf[1][8] = {{6, 4, 3, 2, 1, 0, 0, 0}};
 static const int kBedBlanketPts[8][3] = {
 	{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
 	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
 };
 static const int kBlanketSurf[4][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
+	{2, 4, 0, 3, 7, 4, 0, 0}, {2, 4, 3, 2, 6, 7, 0, 0},
+	{2, 4, 2, 1, 5, 6, 0, 0}, {2, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kBedSheetPts[8][3] = {
 	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60},
 	{-80, 70, 80}, {80, 70, 80}, {80, -175, 80}, {-80, -175, 80}
 };
 static const int kSheetSurf[3][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
+	{15, 4, 0, 3, 7, 4, 0, 0}, {15, 4, 2, 1, 5, 6, 0, 0},
+	{15, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kBBedBlanketPts[8][3] = {
 	{-120, 96, 0}, {120, 96, 0}, {120, -96, 0}, {-120, -96, 0},
@@ -91,7 +91,7 @@ static const int kBBedPostPts[4][3] = {
 static const int kDeskTopPts[4][3] = {
 	{-128, 80, 100}, {128, 80, 100}, {128, -80, 100}, {-128, -80, 100}
 };
-static const int kDeskTopSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kDeskTopSurf[1][8] = {{6, 4, 3, 2, 1, 0, 0, 0}};
 static const int kDeskLeftPts[8][3] = {
 	{-128, 80, 0}, {-60, 80, 0}, {-60, -80, 0}, {-128, -80, 0},
 	{-128, 80, 100}, {-60, 80, 100}, {-60, -80, 100}, {-128, -80, 100}
@@ -101,13 +101,13 @@ static const int kDeskRightPts[8][3] = {
 	{60, 80, 100}, {128, 80, 100}, {128, -80, 100}, {60, -80, 100}
 };
 static const int kDeskCabSurf[4][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
+	{6, 4, 1, 0, 4, 5, 0, 0}, {6, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kSeatPts[4][3] = {
 	{-40, 210, 60}, {40, 210, 60}, {40, 115, 60}, {-40, 115, 60}
 };
-static const int kSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kSeatSurf[1][8] = {{1, 4, 3, 2, 1, 0, 0, 0}};
 static const int kArmLeftPts[4][3] = {
 	{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
 };
@@ -115,13 +115,13 @@ static const int kArmRightPts[4][3] = {
 	{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
 };
 static const int kArmSurf[2][8] = {
-	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	{1, 4, 3, 2, 1, 0, 0, 0}, {1, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kBackPts[4][3] = {
 	{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
 };
 static const int kBackSurf[2][8] = {
-	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	{1, 4, 3, 2, 1, 0, 0, 0}, {1, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kComputerPts[8][3] = {
 	{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
@@ -132,53 +132,53 @@ static const int kMonitorPts[8][3] = {
 	{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
 };
 static const int kComputerSurf[5][8] = {
-	{0, 4, 7, 6, 5, 4, 0, 0}, {0, 4, 0, 3, 7, 4, 0, 0},
-	{0, 4, 3, 2, 6, 7, 0, 0}, {0, 4, 1, 0, 4, 5, 0, 0},
-	{0, 4, 2, 1, 5, 6, 0, 0}
+	{6, 4, 7, 6, 5, 4, 0, 0}, {6, 4, 0, 3, 7, 4, 0, 0},
+	{6, 4, 3, 2, 6, 7, 0, 0}, {6, 4, 1, 0, 4, 5, 0, 0},
+	{6, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kDeskScreenPts[4][3] = {
 	{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
 };
-static const int kDeskScreenSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kDeskScreenSurf[1][8] = {{1, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCSeatPts[4][3] = {
 	{-40, 40, 60}, {40, 40, 60}, {40, -40, 60}, {-40, -40, 60}
 };
-static const int kCSeatSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCSeatSurf[1][8] = {{1, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCArmLeftPts[4][3] = {
 	{-50, 40, 90}, {-40, 40, 60}, {-40, -40, 60}, {-50, -40, 90}
 };
-static const int kCArmLeftSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCArmLeftSurf[1][8] = {{1, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCArmRightPts[4][3] = {
 	{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
 };
-static const int kCArmRightSurf[1][8] = {{0, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCArmRightSurf[1][8] = {{1, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCBackPts[4][3] = {
 	{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
 };
 static const int kCBackSurf[2][8] = {
-	{0, 4, 3, 2, 1, 0, 0, 0}, {0, 4, 0, 1, 2, 3, 0, 0}
+	{1, 4, 3, 2, 1, 0, 0, 0}, {1, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kCBasePts[8][3] = {
 	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
 	{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
 };
 static const int kCBaseSurf[4][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	{1, 4, 0, 3, 7, 4, 0, 0}, {1, 4, 3, 2, 6, 7, 0, 0},
+	{1, 4, 1, 0, 4, 5, 0, 0}, {1, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kConsolePts[8][3] = {
 	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
 	{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
 };
 static const int kConsoleSurf[5][8] = {
-	{1, 4, 4, 0, 3, 7, 0, 0}, {1, 4, 7, 3, 2, 6, 0, 0},
-	{1, 4, 5, 1, 0, 4, 0, 0}, {1, 4, 6, 2, 1, 5, 0, 0},
+	{4, 4, 4, 0, 3, 7, 0, 0}, {4, 4, 7, 3, 2, 6, 0, 0},
+	{4, 4, 5, 1, 0, 4, 0, 0}, {4, 4, 6, 2, 1, 5, 0, 0},
 	{0, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kCouchSurf[5][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
+	{4, 4, 0, 3, 7, 4, 0, 0}, {4, 4, 3, 2, 6, 7, 0, 0},
+	{4, 4, 1, 0, 4, 5, 0, 0}, {4, 4, 2, 1, 5, 6, 0, 0},
+	{4, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kACouchPts[8][3] = {
 	{-50, 150, 0}, {50, 150, 0}, {50, -150, 0}, {-50, -150, 0},
@@ -217,9 +217,9 @@ static const int kTVBodyPts[8][3] = {
 	{-30, 60, 120}, {30, 60, 120}, {30, -60, 120}, {-30, -60, 120}
 };
 static const int kTVBodySurf[5][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
+	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
+	{6, 4, 1, 0, 4, 5, 0, 0}, {6, 4, 2, 1, 5, 6, 0, 0},
+	{6, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kTVScreenPts[4][3] = {
 	{30, 50, 10}, {30, -50, 10}, {30, 50, 110}, {30, -50, 110}
@@ -230,9 +230,9 @@ static const int kDrawerPts[8][3] = {
 	{-80, 70, 100}, {0, 70, 100}, {0, -70, 100}, {-80, -70, 100}
 };
 static const int kDrawerSurf[5][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
+	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
+	{6, 4, 1, 0, 4, 5, 0, 0}, {6, 4, 2, 1, 5, 6, 0, 0},
+	{6, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kMirrorPts[4][3] = {
 	{-80, 65, 100}, {-80, -65, 100}, {-80, 65, 210}, {-80, -65, 210}
@@ -306,16 +306,16 @@ static const int kPlantPotPts[8][3] = {
 	{-20, 20, 40}, {20, 20, 40}, {20, -20, 40}, {-20, -20, 40}
 };
 static const int kPlantPotSurf[5][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 7, 6, 5, 4, 0, 0}
+	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
+	{6, 4, 1, 0, 4, 5, 0, 0}, {6, 4, 2, 1, 5, 6, 0, 0}, {6, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kPlantStemPts[8][3] = {
 	{-5, 5, 40}, {5, 5, 40}, {5, -5, 40}, {-5, -5, 40},
 	{-5, 5, 120}, {5, 5, 120}, {5, -5, 120}, {-5, -5, 120}
 };
 static const int kPlantStemSurf[4][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}
+	{2, 4, 0, 3, 7, 4, 0, 0}, {2, 4, 3, 2, 6, 7, 0, 0},
+	{2, 4, 1, 0, 4, 5, 0, 0}, {2, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kPlantLeaf1Pts[4][3] = {
 	{-40, 0, 120}, {40, 0, 120}, {40, 0, 140}, {-40, 0, 140}
@@ -323,7 +323,7 @@ static const int kPlantLeaf1Pts[4][3] = {
 static const int kPlantLeaf2Pts[4][3] = {
 	{0, 40, 120}, {0, -40, 120}, {0, 40, 140}, {0, -40, 140}
 };
-static const int kPlantLeafSurf[1][8] = {{0, 4, 0, 1, 2, 3, 0, 0}};
+static const int kPlantLeafSurf[1][8] = {{2, 4, 0, 1, 2, 3, 0, 0}};
 static const Colony::ColonyEngine::PrismPartDef kPlantParts[4] = {
 	{8, kPlantPotPts, 5, kPlantPotSurf},
 	{8, kPlantStemPts, 4, kPlantStemSurf},
@@ -444,11 +444,8 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 		if (count >= 3) {
 			uint32 finalColor;
 			if (_corePower[_coreIndex] > 0) {
-				// Lit mode: use surface-specific colors if defined, else black.
-				if (def.surfaces[i][0] == 1)
-					finalColor = 4; // vRED
-				else
-					finalColor = 0; // vBLACK
+				// Lit mode: use surface-specific colors defined in geometry
+				finalColor = (uint32)def.surfaces[i][0];
 			} else {
 				// Dark mode: everything is white wireframe.
 				finalColor = 15; // vINTWHITE


Commit: efe107c49cfea2e8c0dc779d9752ae505ec5769b
    https://github.com/scummvm/scummvm/commit/efe107c49cfea2e8c0dc779d9752ae505ec5769b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:13+02:00

Commit Message:
COLONY: expand 3D object geometry with additional surface definitions

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index ffeaa0f6fb3..6a9bcfd3c7d 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -89,16 +89,16 @@ static const int kBBedPostPts[4][3] = {
 	{-120, 96, 0}, {120, 96, 0}, {120, 96, 100}, {-120, 96, 100}
 };
 static const int kDeskTopPts[4][3] = {
-	{-128, 80, 100}, {128, 80, 100}, {128, -80, 100}, {-128, -80, 100}
+	{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
 };
 static const int kDeskTopSurf[1][8] = {{6, 4, 3, 2, 1, 0, 0, 0}};
 static const int kDeskLeftPts[8][3] = {
-	{-128, 80, 0}, {-60, 80, 0}, {-60, -80, 0}, {-128, -80, 0},
-	{-128, 80, 100}, {-60, 80, 100}, {-60, -80, 100}, {-128, -80, 100}
+	{-135, 95, 0}, {-55, 95, 0}, {-55, -95, 0}, {-135, -95, 0},
+	{-135, 95, 100}, {-55, 95, 100}, {-55, -95, 100}, {-135, -95, 100}
 };
 static const int kDeskRightPts[8][3] = {
-	{60, 80, 0}, {128, 80, 0}, {128, -80, 0}, {60, -80, 0},
-	{60, 80, 100}, {128, 80, 100}, {128, -80, 100}, {60, -80, 100}
+	{55, 95, 0}, {135, 95, 0}, {135, -95, 0}, {55, -95, 0},
+	{55, 95, 100}, {135, 95, 100}, {135, -95, 100}, {55, -95, 100}
 };
 static const int kDeskCabSurf[4][8] = {
 	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
@@ -301,34 +301,84 @@ static const int kCWallSurf[3][8] = {
 	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0}
 };
 static const Colony::ColonyEngine::PrismPartDef kCWallParts[1] = {{8, kCWallPts, 3, kCWallSurf}};
-static const int kPlantPotPts[8][3] = {
-	{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0},
-	{-20, 20, 40}, {20, 20, 40}, {20, -20, 40}, {-20, -20, 40}
+
+static const int kPlantPotPts[12][3] = {
+	{10, 17, 40}, {20, 0, 40}, {10, -17, 40}, {-10, -17, 40}, {-20, 0, 40}, {-10, 17, 40},
+	{8, 12, 0}, {15, 0, 0}, {8, -12, 0}, {-8, -12, 0}, {-15, 0, 0}, {-8, 12, 0}
+};
+static const int kPlantPotSurf[6][8] = {
+	{6, 4, 0, 1, 7, 6, 0, 0}, {6, 4, 1, 2, 8, 7, 0, 0}, {6, 4, 2, 3, 9, 8, 0, 0},
+	{6, 4, 3, 4, 10, 9, 0, 0}, {6, 4, 4, 5, 11, 10, 0, 0}, {6, 4, 5, 0, 6, 11, 0, 0}
+};
+static const int kPlantTopPotPts[6][3] = {
+	{10, 17, 40}, {20, 0, 40}, {10, -17, 40}, {-10, -17, 40}, {-20, 0, 40}, {-10, 17, 40}
 };
-static const int kPlantPotSurf[5][8] = {
+static const int kPlantTopPotSurf[1][8] = {{8, 6, 5, 4, 3, 2, 1, 0}};
+
+static const int kPlantLeaf0Pts[3][3] = {{0, 0, 0}, {20, 20, 150}, {70, 70, 100}};
+static const int kPlantLeaf1Pts[3][3] = {{0, 0, 0}, {-20, 30, 100}, {-60, 50, 50}};
+static const int kPlantLeaf2Pts[3][3] = {{0, 0, 0}, {-20, -10, 70}, {-90, -70, 50}};
+static const int kPlantLeaf3Pts[3][3] = {{0, 0, 0}, {20, -10, 50}, {90, -70, 70}};
+static const int kPlantLeaf4Pts[3][3] = {{0, 0, 0}, {20, -30, 190}, {40, -60, 150}};
+static const int kPlantLeaf5Pts[3][3] = {{0, 0, 0}, {20, -10, 130}, {50, -80, 200}};
+
+static const int kPlantLeafSurf[2][8] = {{2, 3, 0, 1, 2, 0, 0, 0}, {2, 3, 2, 1, 0, 0, 0, 0}};
+
+static const Colony::ColonyEngine::PrismPartDef kPlantParts[8] = {
+	{12, kPlantPotPts, 6, kPlantPotSurf},
+	{6, kPlantTopPotPts, 1, kPlantTopPotSurf},
+	{3, kPlantLeaf0Pts, 2, kPlantLeafSurf},
+	{3, kPlantLeaf1Pts, 2, kPlantLeafSurf},
+	{3, kPlantLeaf2Pts, 2, kPlantLeafSurf},
+	{3, kPlantLeaf3Pts, 2, kPlantLeafSurf},
+	{3, kPlantLeaf4Pts, 2, kPlantLeafSurf},
+	{3, kPlantLeaf5Pts, 2, kPlantLeafSurf}
+};
+
+static const int kBox1Pts[8][3] = {
+	{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
+	{-50, 50, 100}, {50, 50, 100}, {50, -50, 100}, {-50, -50, 100}
+};
+static const int kBox1Surf[5][8] = {
 	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
 	{6, 4, 1, 0, 4, 5, 0, 0}, {6, 4, 2, 1, 5, 6, 0, 0}, {6, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kPlantStemPts[8][3] = {
-	{-5, 5, 40}, {5, 5, 40}, {5, -5, 40}, {-5, -5, 40},
-	{-5, 5, 120}, {5, 5, 120}, {5, -5, 120}, {-5, -5, 120}
+static const int kBox2Pts[8][3] = {
+	{-50, 50, 100}, {50, 50, 100}, {50, -50, 100}, {-50, -50, 100},
+	{-50, 50, 200}, {50, 50, 200}, {50, -50, 200}, {-50, -50, 200}
 };
-static const int kPlantStemSurf[4][8] = {
-	{2, 4, 0, 3, 7, 4, 0, 0}, {2, 4, 3, 2, 6, 7, 0, 0},
-	{2, 4, 1, 0, 4, 5, 0, 0}, {2, 4, 2, 1, 5, 6, 0, 0}
+
+static const int kReactorCorePts[12][3] = {
+	{32, 55, 120}, {64, 0, 120}, {32, -55, 120}, {-32, -55, 120}, {-64, 0, 120}, {-32, 55, 120},
+	{32, 55, 168}, {64, 0, 168}, {32, -55, 168}, {-32, -55, 168}, {-64, 0, 168}, {-32, 55, 168}
 };
-static const int kPlantLeaf1Pts[4][3] = {
-	{-40, 0, 120}, {40, 0, 120}, {40, 0, 140}, {-40, 0, 140}
+static const int kReactorCoreSurf[7][8] = {
+	{15, 4, 0, 1, 7, 6, 0, 0}, {15, 4, 1, 2, 8, 7, 0, 0}, {15, 4, 2, 3, 9, 8, 0, 0},
+	{15, 4, 3, 4, 10, 9, 0, 0}, {15, 4, 4, 5, 11, 10, 0, 0}, {15, 4, 5, 0, 6, 11, 0, 0},
+	{15, 6, 5, 4, 3, 2, 1, 0}
 };
-static const int kPlantLeaf2Pts[4][3] = {
-	{0, 40, 120}, {0, -40, 120}, {0, 40, 140}, {0, -40, 140}
+static const int kReactorBasePts[8][3] = {
+	{-128, 128, 0}, {128, 128, 0}, {128, -128, 0}, {-128, -128, 0},
+	{-128, 128, 120}, {128, 128, 120}, {128, -128, 120}, {-128, -128, 120}
 };
-static const int kPlantLeafSurf[1][8] = {{2, 4, 0, 1, 2, 3, 0, 0}};
-static const Colony::ColonyEngine::PrismPartDef kPlantParts[4] = {
-	{8, kPlantPotPts, 5, kPlantPotSurf},
-	{8, kPlantStemPts, 4, kPlantStemSurf},
-	{4, kPlantLeaf1Pts, 1, kPlantLeafSurf},
-	{4, kPlantLeaf2Pts, 1, kPlantLeafSurf}
+static const int kReactorBaseSurf[6][8] = {
+	{4, 4, 0, 3, 7, 4, 0, 0}, {4, 4, 3, 2, 6, 7, 0, 0}, {4, 4, 1, 0, 4, 5, 0, 0},
+	{4, 4, 2, 1, 5, 6, 0, 0}, {4, 4, 7, 6, 5, 4, 0, 0}, {4, 4, 0, 1, 2, 3, 0, 0}
+};
+static const int kReactorTopPts[8][3] = {
+	{-128, 128, 168}, {128, 128, 168}, {128, -128, 168}, {-128, -128, 168},
+	{-128, 128, 288}, {128, 128, 288}, {128, -128, 288}, {-128, -128, 288}
+};
+
+static const Colony::ColonyEngine::PrismPartDef kBox1Part = {8, kBox1Pts, 5, kBox1Surf};
+static const Colony::ColonyEngine::PrismPartDef kBox2Parts[2] = {
+	{8, kBox2Pts, 4, kBox1Surf}, // Body (stacked on box1)
+	{8, kBox1Pts, 5, kBox1Surf}  // Lid (same geometry as box1 base)
+};
+static const Colony::ColonyEngine::PrismPartDef kReactorParts[3] = {
+	{12, kReactorCorePts, 7, kReactorCoreSurf},
+	{8, kReactorBasePts, 6, kReactorBaseSurf},
+	{8, kReactorTopPts, 6, kReactorBaseSurf}
 };
 
 
@@ -784,7 +834,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj, uint32 baseColor)
 			draw3DPrism(obj, kCChairParts[i], false, tint(baseColor, 0));
 		break;
 	case kObjPlant:
-		for (int i = 0; i < 4; i++)
+		for (int i = 0; i < 8; i++)
 			draw3DPrism(obj, kPlantParts[i], false, tint(baseColor, 0));
 		break;
 	case kObjCouch:
@@ -825,6 +875,31 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj, uint32 baseColor)
 		for (int i = 0; i < 10; i++)
 			draw3DPrism(obj, kDeskParts[i], false, tint(baseColor, 0));
 		break;
+	case kObjBox1:
+		draw3DPrism(obj, kBox1Part, false, tint(baseColor, 0));
+		break;
+	case kObjBox2:
+		draw3DPrism(obj, kBox2Parts[0], false, tint(baseColor, 0));
+		draw3DPrism(obj, kBox2Parts[1], false, tint(baseColor, 0));
+		break;
+	case kObjReactor:
+		for (int i = 0; i < 3; i++)
+			draw3DPrism(obj, kReactorParts[i], false, tint(baseColor, 0));
+		break;
+	case kObjPowerSuit:
+		draw3DPrism(obj, kConsolePart, false, tint(baseColor, 0)); // Placeholder
+		break;
+	case kObjTeleport:
+		draw3DPrism(obj, kCWallParts[0], false, tint(baseColor, 50)); // Placeholder
+		break;
+	case kObjSink:
+	case kObjTub:
+	case kObjToilet:
+		draw3DPrism(obj, kBox1Part, false, tint(baseColor, 0)); // Placeholder
+		break;
+	case kObjForkLift:
+		draw3DPrism(obj, kBox1Part, false, tint(baseColor, 0)); // Placeholder
+		break;
 	default:
 		return false;
 	}


Commit: 57097beff22a6b1aac0e1471beb4957ea253ea3f
    https://github.com/scummvm/scummvm/commit/57097beff22a6b1aac0e1471beb4957ea253ea3f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:13+02:00

Commit Message:
COLONY: improved doors

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 6edc7b64fd8..d22e698b710 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -270,6 +270,8 @@ private:
 	void drawCrosshair();
 	bool clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const;
 	void wallLine(const float corners[4][3], float u1, float v1, float u2, float v2, uint32 color);
+	void wallPolygon(const float corners[4][3], const float *u, const float *v, int count, uint32 color);
+	void wallChar(const float corners[4][3], uint8 cnum);
 
 	// Animation system
 	Common::Array<Sprite *> _cSprites;
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 6a9bcfd3c7d..8404cabea82 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -578,8 +578,41 @@ void ColonyEngine::wallLine(const float corners[4][3], float u1, float v1, float
 	float p1[3], p2[3];
 	wallPoint(corners, u1, v1, p1);
 	wallPoint(corners, u2, v2, p2);
-	uint32 finalColor = (_corePower[_coreIndex] > 0) ? 0 : 15;
-	_gfx->draw3DLine(p1[0], p1[1], p1[2], p2[0], p2[1], p2[2], finalColor);
+	// We assume this is only called when lit (handled in drawWallFeatures3D)
+	_gfx->draw3DLine(p1[0], p1[1], p1[2], p2[0], p2[1], p2[2], color);
+}
+
+// Draw a filled polygon on a wall face using normalized (u,v) coordinates
+void ColonyEngine::wallPolygon(const float corners[4][3], const float *u, const float *v, int count, uint32 color) {
+	float px[8], py[8], pz[8];
+	if (count > 8) count = 8;
+	for (int i = 0; i < count; i++) {
+		float p[3];
+		wallPoint(corners, u[i], v[i], p);
+		px[i] = p[0]; py[i] = p[1]; pz[i] = p[2];
+	}
+	_gfx->draw3DPolygon(px, py, pz, count, color);
+}
+
+void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
+	// Character 'b' (right arrow) and 'c' (left arrow) coordinates on 0-6 grid
+	static const uint8 wallchar_b[] = {7, 0,3, 3,0, 3,2, 6,2, 6,4, 3,4, 3,6};
+	static const uint8 wallchar_c[] = {7, 0,2, 0,4, 3,4, 3,6, 6,3, 3,0, 3,2};
+	
+	const uint8 *data = nullptr;
+	if (cnum == 'b') data = wallchar_b;
+	else if (cnum == 'c') data = wallchar_c;
+	
+	if (data) {
+		int count = data[0];
+		float u[8], v[8];
+		for (int i = 0; i < count; i++) {
+			u[i] = 0.2f + (data[1 + i*2] / 6.0f) * 0.6f;
+			v[i] = 0.2f + (data[2 + i*2] / 6.0f) * 0.6f;
+		}
+		// Characters are usually drawn in black or level-specific color. Color 0 is safe.
+		wallPolygon(corners, u, v, count, 0); 
+	}
 }
 
 void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
@@ -592,164 +625,157 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 
 	switch (map[0]) {
 	case kWallFeatureDoor: {
-		const uint32 dark = 160;
-		// Door frame: centered rectangle on the wall
-		float xl = 0.25f, xr = 0.75f;
-		float yb = 0.125f, yt = 0.875f;
-		if (map[1] == 0) {
-			// Open door — just the frame
-			wallLine(corners, xl, yb, xl, yt, dark);
-			wallLine(corners, xl, yt, xr, yt, dark);
-			wallLine(corners, xr, yt, xr, yb, dark);
-			wallLine(corners, xr, yb, xl, yb, dark);
+		bool shipLevel = (_level == 1 || _level == 5 || _level == 6);
+		
+		if (shipLevel) {
+			// Octagonal ship door (Weighted 7-point grid from source)
+			// Horizontal indices: 2, 1, 1, 2, 4, 5, 5, 4 (0.25..0.75 width)
+			// Vertical indices:   0, 1, 5, 6, 6, 5, 1, 0 (0.125..0.875 height)
+			static const float u_ss[8] = { 0.375f, 0.250f, 0.250f, 0.375f, 0.625f, 0.750f, 0.750f, 0.625f };
+			static const float v_ss[8] = { 0.125f, 0.250f, 0.750f, 0.875f, 0.875f, 0.750f, 0.250f, 0.125f };
+			
+			if (map[1] == 0) {
+				wallPolygon(corners, u_ss, v_ss, 8, 0);
+			} else {
+				wallPolygon(corners, u_ss, v_ss, 8, 7);
+				for (int i = 0; i < 8; i++)
+					wallLine(corners, u_ss[i], v_ss[i], u_ss[(i + 1) % 8], v_ss[(i + 1) % 8], 0);
+				// Ship door inner panel
+				wallLine(corners, 0.375f, 0.25f, 0.375f, 0.75f, 0);
+				wallLine(corners, 0.625f, 0.25f, 0.625f, 0.75f, 0);
+				wallLine(corners, 0.375f, 0.25f, 0.625f, 0.25f, 0);
+				wallLine(corners, 0.375f, 0.75f, 0.625f, 0.75f, 0);
+			}
 		} else {
-			// Closed door — frame + handle line
-			wallLine(corners, xl, yb, xl, yt, dark);
-			wallLine(corners, xl, yt, xr, yt, dark);
-			wallLine(corners, xr, yt, xr, yb, dark);
-			wallLine(corners, xr, yb, xl, yb, dark);
-			// Handle
-			float hx = 0.6f;
-			float hy1 = 0.45f, hy2 = 0.55f;
-			wallLine(corners, hx, hy1, hx, hy2, dark);
+			// Standard rectangular door (Lab levels) - Smaller look
+			float xl = 0.25f, xr = 0.75f;
+			float yb = 0.125f, yt = 0.875f; // Suspended 1/8th from both ends
+			float f_u[4] = {xl, xr, xr, xl};
+			float f_v[4] = {yb, yb, yt, yt};
+			
+			if (map[1] == 0) {
+				wallPolygon(corners, f_u, f_v, 4, 0);
+				float leaf_u[4] = {xl, xl + 0.1f, xl + 0.1f, xl};
+				float leaf_v[4] = {yb, yb + 0.05f, yt - 0.05f, yt};
+				wallPolygon(corners, leaf_u, leaf_v, 4, 15);
+			} else {
+				uint32 fill = (_level >= 1 && _level <= 3) ? 12 : 7;
+				wallPolygon(corners, f_u, f_v, 4, fill);
+				wallLine(corners, xl, yb, xl, yt, 0);
+				wallLine(corners, xl, yt, xr, yt, 0);
+				wallLine(corners, xr, yt, xr, yb, 0);
+				wallLine(corners, xr, yb, xl, yb, 0);
+				// Diagonal Handle (Centered in the door)
+				wallLine(corners, xr - 0.15f, 0.45f, xr - 0.05f, 0.55f, 0);
+			}
 		}
 		break;
 	}
 	case kWallFeatureWindow: {
-		const uint32 dark = 160;
-		// Window: centered smaller rectangle with cross divider
-		float xl = 0.25f, xr = 0.75f;
-		float yb = 0.375f, yt = 0.75f;
-		wallLine(corners, xl, yb, xr, yb, dark);
-		wallLine(corners, xr, yb, xr, yt, dark);
-		wallLine(corners, xr, yt, xl, yt, dark);
-		wallLine(corners, xl, yt, xl, yb, dark);
-		// Cross dividers
-		float xc = 0.5f, yc = (yb + yt) * 0.5f;
-		wallLine(corners, xc, yb, xc, yt, dark);
-		wallLine(corners, xl, yc, xr, yc, dark);
+		// Window: Uses average midpoint shifts. 1/8th suspended.
+		float xl = 0.3125f, xr = 0.6875f;
+		float yb = 0.3125f, yt = 0.6875f; 
+		float u[4] = {xl, xr, xr, xl};
+		float v[4] = {yb, yb, yt, yt};
+		wallPolygon(corners, u, v, 4, 7); 
+		wallLine(corners, xl, yb, xr, yb, 0);
+		wallLine(corners, xr, yb, xr, yt, 0);
+		wallLine(corners, xr, yt, xl, yt, 0);
+		wallLine(corners, xl, yt, xl, yb, 0);
+		wallLine(corners, 0.5f, yb, 0.5f, yt, 0);
+		wallLine(corners, xl, 0.5f, xr, 0.5f, 0);
 		break;
 	}
 	case kWallFeatureShelves: {
-		const uint32 dark = 170;
-		// Bookshelf: outer rectangle + horizontal shelf lines
 		float xl = 0.15f, xr = 0.85f;
 		float yb = 0.1f, yt = 0.9f;
-		wallLine(corners, xl, yb, xr, yb, dark);
-		wallLine(corners, xr, yb, xr, yt, dark);
-		wallLine(corners, xr, yt, xl, yt, dark);
-		wallLine(corners, xl, yt, xl, yb, dark);
-		// 6 shelves
+		wallLine(corners, xl, yb, xr, yb, 0);
+		wallLine(corners, xr, yb, xr, yt, 0);
+		wallLine(corners, xr, yt, xl, yt, 0);
+		wallLine(corners, xl, yt, xl, yb, 0);
 		for (int i = 1; i <= 6; i++) {
 			float t = yb + (yt - yb) * (float)i / 7.0f;
-			wallLine(corners, xl, t, xr, t, dark);
+			wallLine(corners, xl, t, xr, t, 0);
 		}
 		break;
 	}
 	case kWallFeatureUpStairs: {
-		const uint32 dark = 170;
-		// Upward stairs: ascending step pattern
 		float xl = 0.15f, xr = 0.85f;
 		for (int i = 0; i < 6; i++) {
 			float u = xl + (xr - xl) * (float)i / 6.0f;
 			float u2 = xl + (xr - xl) * (float)(i + 1) / 6.0f;
 			float v = 0.1f + 0.8f * (float)i / 6.0f;
 			float v2 = 0.1f + 0.8f * (float)(i + 1) / 6.0f;
-			wallLine(corners, u, v, u2, v2, dark);
+			wallLine(corners, u, v, u2, v2, 0);
 		}
-		// Side rails
-		wallLine(corners, xl, 0.1f, xr, 0.9f, dark);
+		wallLine(corners, xl, 0.1f, xr, 0.9f, 0);
 		break;
 	}
 	case kWallFeatureDnStairs: {
-		const uint32 dark = 170;
-		// Downward stairs: descending step pattern
 		float xl = 0.15f, xr = 0.85f;
 		for (int i = 0; i < 6; i++) {
 			float u = xl + (xr - xl) * (float)i / 6.0f;
 			float u2 = xl + (xr - xl) * (float)(i + 1) / 6.0f;
 			float v = 0.9f - 0.8f * (float)i / 6.0f;
 			float v2 = 0.9f - 0.8f * (float)(i + 1) / 6.0f;
-			wallLine(corners, u, v, u2, v2, dark);
+			wallLine(corners, u, v, u2, v2, 0);
 		}
-		wallLine(corners, xl, 0.9f, xr, 0.1f, dark);
+		wallLine(corners, xl, 0.9f, xr, 0.1f, 0);
 		break;
 	}
+	case kWallFeatureChar:
+		wallChar(corners, map[1]);
+		break;
 	case kWallFeatureGlyph: {
-		const uint32 dark = 170;
-		// Alien glyphs: horizontal lines (like hieroglyphics)
-		float xl = 0.1f, xr = 0.9f;
-		for (int i = 0; i < 9; i++) {
-			float v = 0.15f + 0.7f * (float)i / 8.0f;
-			wallLine(corners, xl, v, xr, v, dark);
+		for (int i = 0; i < 7; i++) {
+			float v = 0.2f + i * 0.1f;
+			wallLine(corners, 0.2f, v, 0.8f, v, 0);
 		}
 		break;
 	}
 	case kWallFeatureElevator: {
-		const uint32 dark = 170;
-		// Elevator: tall rectangle with center divider line
-		float xl = 0.15f, xr = 0.85f;
-		float yb = 0.05f, yt = 0.95f;
-		wallLine(corners, xl, yb, xl, yt, dark);
-		wallLine(corners, xl, yt, xr, yt, dark);
-		wallLine(corners, xr, yt, xr, yb, dark);
-		wallLine(corners, xr, yb, xl, yb, dark);
-		// Center divider
-		float xc = 0.5f;
-		wallLine(corners, xc, yb, xc, yt, dark);
+		float xl = 0.2f, xr = 0.8f;
+		float yb = 0.1f, yt = 0.9f;
+		wallLine(corners, xl, yb, xl, yt, 0);
+		wallLine(corners, xl, yt, xr, yt, 0);
+		wallLine(corners, xr, yt, xr, yb, 0);
+		wallLine(corners, xr, yb, xl, yb, 0);
+		wallLine(corners, 0.5f, yb, 0.5f, yt, 0);
 		break;
 	}
 	case kWallFeatureTunnel: {
-		const uint32 dark = 120;
-		// Tunnel: hexagonal opening
-		float pts[][2] = {
-			{0.0f, 0.5f}, {0.15f, 0.85f}, {0.5f, 1.0f},
-			{0.85f, 0.85f}, {1.0f, 0.5f}, {0.85f, 0.15f},
-			{0.5f, 0.0f}, {0.15f, 0.15f}
-		};
-		for (int i = 0; i < 8; i++) {
-			int n = (i + 1) % 8;
-			// Scale inward slightly
-			float u1 = 0.1f + pts[i][0] * 0.8f;
-			float v1 = 0.1f + pts[i][1] * 0.8f;
-			float u2 = 0.1f + pts[n][0] * 0.8f;
-			float v2 = 0.1f + pts[n][1] * 0.8f;
-			wallLine(corners, u1, v1, u2, v2, dark);
-		}
+		// Tunnel: hexagonal opening from Grid (0,0 0,5 1,6 5,6 6,5 6,0)
+		static const float u_t[6] = { 0.0f,    0.0f,    1/6.0f,  5/6.0f,  1.0f,    1.0f };
+		static const float v_t[6] = { 0.0f,    0.750f,  0.875f,  0.875f,  0.750f,  0.0f };
+		wallPolygon(corners, u_t, v_t, 6, 0); // Black tunnel fill
 		break;
 	}
 	case kWallFeatureAirlock: {
-		const uint32 dark = map[1] == 0 ? (uint32)150 : (uint32)170;
-		// Airlock: octagonal shape
-		float pts[][2] = {
-			{0.0f, 0.5f}, {0.15f, 0.85f}, {0.5f, 1.0f},
-			{0.85f, 0.85f}, {1.0f, 0.5f}, {0.85f, 0.15f},
-			{0.5f, 0.0f}, {0.15f, 0.15f}
-		};
+		float pts[][2] = {{0.0f, 0.5f}, {0.15f, 0.85f}, {0.5f, 1.0f}, {0.85f, 0.85f},
+		                  {1.0f, 0.5f}, {0.85f, 0.15f}, {0.5f, 0.0f}, {0.15f, 0.15f}};
+		float u[8], v[8];
 		for (int i = 0; i < 8; i++) {
-			int n = (i + 1) % 8;
-			float u1 = 0.1f + pts[i][0] * 0.8f;
-			float v1 = 0.1f + pts[i][1] * 0.8f;
-			float u2 = 0.1f + pts[n][0] * 0.8f;
-			float v2 = 0.1f + pts[n][1] * 0.8f;
-			wallLine(corners, u1, v1, u2, v2, dark);
+			u[i] = 0.1f + pts[i][0] * 0.8f;
+			v[i] = 0.1f + pts[i][1] * 0.8f;
 		}
-		if (map[1] != 0) {
-			// Closed: add cross lines through center
-			wallLine(corners, 0.1f, 0.5f, 0.5f, 0.5f, dark);
-			wallLine(corners, 0.5f, 0.1f, 0.5f, 0.5f, dark);
-			wallLine(corners, 0.9f, 0.5f, 0.5f, 0.5f, dark);
-			wallLine(corners, 0.5f, 0.9f, 0.5f, 0.5f, dark);
+		if (map[1] == 0) {
+			wallPolygon(corners, u, v, 8, 0);
+		} else {
+			wallPolygon(corners, u, v, 8, 7);
+			for (int i = 0; i < 8; i++) {
+				int n = (i + 1) % 8;
+				wallLine(corners, u[i], v[i], u[n], v[n], 0);
+			}
+			wallLine(corners, 0.1f, 0.5f, 0.9f, 0.5f, 0);
+			wallLine(corners, 0.5f, 0.1f, 0.5f, 0.9f, 0);
 		}
 		break;
 	}
 	case kWallFeatureColor: {
-		// Colored horizontal bands
 		for (int i = 1; i <= 3; i++) {
 			uint32 c = 120 + map[i] * 20;
-			if (c == 120 && map[i] == 0 && !map[1] && !map[2] && !map[3] && !map[4]) {
+			if (c == 120 && map[i] == 0 && !map[1] && !map[2] && !map[3] && !map[4])
 				c = 100 + (_level * 15);
-			}
 			float v = (float)i / 4.0f;
 			wallLine(corners, 0.0f, v, 1.0f, v, c);
 		}
@@ -761,6 +787,9 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 }
 
 void ColonyEngine::drawWallFeatures3D() {
+	if (_corePower[_coreIndex] == 0)
+		return;
+
 	for (int y = 0; y < 31; y++) {
 		for (int x = 0; x < 31; x++) {
 			for (int dir = 0; dir < 4; dir++) {


Commit: 8f543d1cfe950480d3d88605d881f77612acc938
    https://github.com/scummvm/scummvm/commit/8f543d1cfe950480d3d88605d881f77612acc938
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:13+02:00

Commit Message:
COLONY: add ceiling detail rendering

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index d22e698b710..94f90c9518a 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -50,7 +50,8 @@ enum MapDirection {
 	kDirNorth = 0,
 	kDirEast = 1,
 	kDirWest = 2,
-	kDirSouth = 3
+	kDirSouth = 3,
+	kDirCenter = 4
 };
 
 enum ObjectType {
@@ -258,7 +259,9 @@ private:
 	void renderCorridor3D();
 	void drawWallFeatures3D();
 	void drawWallFeature3D(int cellX, int cellY, int direction);
+	void drawCellFeature3D(int cellX, int cellY);
 	void getWallFace3D(int cellX, int cellY, int direction, float corners[4][3]);
+	void getCellFace3D(int cellX, int cellY, bool ceiling, float corners[4][3]);
 
 	int occupiedObjectAt(int x, int y, const Locate *pobject);
 	void interactWithObject(int objNum);
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 8404cabea82..8739780c518 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -615,6 +615,59 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 	}
 }
 
+void ColonyEngine::getCellFace3D(int cellX, int cellY, bool ceiling, float corners[4][3]) {
+	float z = ceiling ? 160.0f : -160.0f;
+	float x0 = cellX * 256.0f;
+	float y0 = cellY * 256.0f;
+	float x1 = x0 + 256.0f;
+	float y1 = y0 + 256.0f;
+	const float eps = 0.1f;
+	if (ceiling) z -= eps; else z += eps;
+
+	corners[0][0] = x0; corners[0][1] = y0; corners[0][2] = z;
+	corners[1][0] = x1; corners[1][1] = y0; corners[1][2] = z;
+	corners[2][0] = x1; corners[2][1] = y1; corners[2][2] = z;
+	corners[3][0] = x0; corners[3][1] = y1; corners[3][2] = z;
+}
+
+void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
+	const uint8 *map = mapFeatureAt(cellX, cellY, kDirCenter);
+	if (!map || map[0] == 0)
+		return;
+
+	float corners[4][3];
+	bool ceiling = (map[0] == 3 || map[0] == 4); // SMHOLECEIL, LGHOLECEIL
+	getCellFace3D(cellX, cellY, ceiling, corners);
+
+	switch (map[0]) {
+	case 1: // SMHOLEFLR
+	case 3: // SMHOLECEIL
+	{
+		float u[4] = {0.375f, 0.625f, 0.625f, 0.375f};
+		float v[4] = {0.375f, 0.375f, 0.625f, 0.625f};
+		wallPolygon(corners, u, v, 4, 7); // LTGRAY
+		break;
+	}
+	case 2: // LGHOLEFLR
+	case 4: // LGHOLECEIL
+	{
+		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+		wallPolygon(corners, u, v, 4, 7); // LTGRAY
+		break;
+	}
+	case 5: // HOTFOOT
+	{
+		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+		wallPolygon(corners, u, v, 4, 4); // RED
+		break;
+	}
+	default:
+		break;
+	}
+}
+
 void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	const uint8 *map = mapFeatureAt(cellX, cellY, direction);
 	if (!map || map[0] == kWallFeatureNone)
@@ -792,6 +845,7 @@ void ColonyEngine::drawWallFeatures3D() {
 
 	for (int y = 0; y < 31; y++) {
 		for (int x = 0; x < 31; x++) {
+			drawCellFeature3D(x, y);
 			for (int dir = 0; dir < 4; dir++) {
 				const uint8 *map = mapFeatureAt(x, y, dir);
 				if (map && map[0] != kWallFeatureNone) {
@@ -830,6 +884,16 @@ void ColonyEngine::renderCorridor3D() {
 	                100000.0f, 100000.0f, 160.1f, 
 	                -100000.0f, 100000.0f, 160.1f, floorColor);
  
+	// Draw ceiling grid (Cuadricule) - Historically only on ceiling
+	for (int i = 0; i <= 32; i++) {
+		float d = i * 256.0f;
+		float max_d = 32.0f * 256.0f;
+		float zCeil = 160.0f;
+		
+		_gfx->draw3DLine(d, 0.0f, zCeil, d, max_d, zCeil, wallColor);
+		_gfx->draw3DLine(0.0f, d, zCeil, max_d, d, zCeil, wallColor);
+	}
+
 	for (int y = 0; y < 32; y++) {
 		for (int x = 0; x < 32; x++) {
 			uint8 w = _wall[x][y];


Commit: 29fd55b2e18fbee9481273fc05f0f4966f5c7a61
    https://github.com/scummvm/scummvm/commit/29fd55b2e18fbee9481273fc05f0f4966f5c7a61
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:13+02:00

Commit Message:
COLONY: add 3D geometry definitions for additional room objects

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 8739780c518..a41b89be730 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -74,8 +74,8 @@ static const int kBedSheetPts[8][3] = {
 	{-80, 70, 80}, {80, 70, 80}, {80, -175, 80}, {-80, -175, 80}
 };
 static const int kSheetSurf[3][8] = {
-	{15, 4, 0, 3, 7, 4, 0, 0}, {15, 4, 2, 1, 5, 6, 0, 0},
-	{15, 4, 7, 6, 5, 4, 0, 0}
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kBBedBlanketPts[8][3] = {
 	{-120, 96, 0}, {120, 96, 0}, {120, -96, 0}, {-120, -96, 0},
@@ -238,6 +238,73 @@ static const int kMirrorPts[4][3] = {
 	{-80, 65, 100}, {-80, -65, 100}, {-80, 65, 210}, {-80, -65, 210}
 };
 static const int kMirrorSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
+
+// Bathtub geometry
+static const int kTubPts[8][3] = {
+	{-128, 128,  0}, {   0, 128,  0}, {   0,-128,  0}, {-128,-128,  0},
+	{-128, 128, 70}, {   0, 128, 70}, {   0,-128, 70}, {-128,-128, 70}
+};
+static const int kTubSurf[5][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kDTubPts[6][3] = {
+	{-16, 112, 70}, {-8, 0, 70}, {-16, -112, 70}, {-112, -112, 70}, {-120, 0, 70}, {-112, 112, 70}
+};
+static const int kDTubSurf[1][8] = {{1, 6, 5, 4, 3, 2, 1, 0}};
+
+// Toilet geometry
+static const int kAToiletPts[8][3] = {
+	{-128, 45, 30}, {-100, 45, 30}, {-100, -45, 30}, {-128, -45, 30},
+	{-128, 45, 100}, {-100, 45, 100}, {-100, -45, 100}, {-128, -45, 100}
+};
+static const int kAToiletSurf[5][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kBToiletPts[12][3] = {
+	{-100, 20, 50}, {-60, 40, 50}, {-20, 20, 50}, {-20, -20, 50}, {-60, -40, 50}, {-100, -20, 50},
+	{-80, 10,  0}, {-60, 20,  0}, {-40, 10,  0}, {-40, -10,  0}, {-60, -20,  0}, {-80, -10,  0}
+};
+static const int kBToiletSurf[7][8] = {
+	{0, 4, 0, 1, 7, 6, 0, 0}, {0, 4, 1, 2, 8, 7, 0, 0}, {0, 4, 2, 3, 9, 8, 0, 0},
+	{0, 4, 3, 4, 10, 9, 0, 0}, {0, 4, 4, 5, 11, 10, 0, 0}, {0, 4, 5, 0, 6, 11, 0, 0},
+	{0, 6, 5, 4, 3, 2, 1, 0}
+};
+static const int kCToiletPts[6][3] = {
+	{-95, 15, 50}, {-60, 35, 50}, {-25, 15, 50}, {-25, -15, 50}, {-60, -35, 50}, {-95, -15, 50}
+};
+static const int kCToiletSurf[1][8] = {{1, 6, 5, 4, 3, 2, 1, 0}};
+static const int kDToiletPts[6][3] = {
+	{-100, 20, 50}, {-100, 40, 90}, {-100, 20, 130}, {-100, -20, 130}, {-100, -40, 90}, {-100, -20, 50}
+};
+static const int kDToiletSurf[1][8] = {{10, 6, 5, 4, 3, 2, 1, 0}};
+static const int kEToiletPts[4][3] = {
+	{-128,-128, 20}, {-128,-128, 200}, { 128,-128, 200}, { 128,-128, 20}
+};
+static const int kEToiletSurf[1][8] = {{0, 4, 0, 1, 2, 3, 0, 0}};
+
+// Sink geometry
+static const int kSinkPts[8][3] = {
+	{-128, 50, 70}, {-50, 50, 90}, {-50,-50, 90}, {-128,-50, 70},
+	{-128, 50, 110}, {-50, 50, 110}, {-50,-50, 110}, {-128,-50, 110}
+};
+static const int kSinkSurf[5][8] = {
+	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
+	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
+	{0, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kDSinkPts[6][3] = {
+	{-55, 0, 110}, {-60, -45, 110}, {-118, -45, 110}, {-123, 0, 110}, {-118, 45, 110}, {-60, 45, 110}
+};
+static const int kDSinkSurf[1][8] = {{1, 6, 5, 4, 3, 2, 1, 0}};
+static const int kSinkMirrorPts[4][3] = {
+	{-128, 65, 130}, {-128, -65, 130}, {-128, 65, 250}, {-128, -65, 250}
+};
+static const int kSinkMirrorSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
+
 static const Colony::ColonyEngine::PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
 static const Colony::ColonyEngine::PrismPartDef kTableParts[2] = {
 	{4, kTableTopPts, 1, kTableTopSurf},
@@ -293,6 +360,29 @@ static const Colony::ColonyEngine::PrismPartDef kDrawerParts[2] = {
 	{8, kDrawerPts, 5, kDrawerSurf},
 	{4, kMirrorPts, 1, kMirrorSurf}
 };
+static const Colony::ColonyEngine::PrismPartDef kTubParts[2] = {
+	{8, kTubPts, 5, kTubSurf},
+	{6, kDTubPts, 1, kDTubSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kSinkParts[3] = {
+	{8, kSinkPts, 5, kSinkSurf},
+	{6, kDSinkPts, 1, kDSinkSurf},
+	{4, kSinkMirrorPts, 1, kSinkMirrorSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kToiletParts[4] = {
+	{8, kAToiletPts, 5, kAToiletSurf},
+	{12, kBToiletPts, 7, kBToiletSurf},
+	{6, kCToiletPts, 1, kCToiletSurf},
+	{6, kDToiletPts, 1, kDToiletSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kPToiletParts[5] = {
+	{8, kAToiletPts, 5, kAToiletSurf},
+	{12, kBToiletPts, 7, kBToiletSurf},
+	{6, kCToiletPts, 1, kCToiletSurf},
+	{6, kDToiletPts, 1, kDToiletSurf},
+	{4, kEToiletPts, 1, kEToiletSurf}
+};
+
 static const int kCWallPts[8][3] = {
 	{-128, 128, 0}, {0, 112, 0}, {112, 0, 0}, {128, -128, 0},
 	{-128, 128, 320},  {0, 112, 320},  {112, 0, 320},  {128, -128, 320}
@@ -313,7 +403,7 @@ static const int kPlantPotSurf[6][8] = {
 static const int kPlantTopPotPts[6][3] = {
 	{10, 17, 40}, {20, 0, 40}, {10, -17, 40}, {-10, -17, 40}, {-20, 0, 40}, {-10, 17, 40}
 };
-static const int kPlantTopPotSurf[1][8] = {{8, 6, 5, 4, 3, 2, 1, 0}};
+static const int kPlantTopPotSurf[1][8] = {{0, 6, 5, 4, 3, 2, 1, 0}};
 
 static const int kPlantLeaf0Pts[3][3] = {{0, 0, 0}, {20, 20, 150}, {70, 70, 100}};
 static const int kPlantLeaf1Pts[3][3] = {{0, 0, 0}, {-20, 30, 100}, {-60, 50, 50}};
@@ -353,9 +443,9 @@ static const int kReactorCorePts[12][3] = {
 	{32, 55, 168}, {64, 0, 168}, {32, -55, 168}, {-32, -55, 168}, {-64, 0, 168}, {-32, 55, 168}
 };
 static const int kReactorCoreSurf[7][8] = {
-	{15, 4, 0, 1, 7, 6, 0, 0}, {15, 4, 1, 2, 8, 7, 0, 0}, {15, 4, 2, 3, 9, 8, 0, 0},
-	{15, 4, 3, 4, 10, 9, 0, 0}, {15, 4, 4, 5, 11, 10, 0, 0}, {15, 4, 5, 0, 6, 11, 0, 0},
-	{15, 6, 5, 4, 3, 2, 1, 0}
+	{0, 4, 0, 1, 7, 6, 0, 0}, {0, 4, 1, 2, 8, 7, 0, 0}, {0, 4, 2, 3, 9, 8, 0, 0},
+	{0, 4, 3, 4, 10, 9, 0, 0}, {0, 4, 4, 5, 11, 10, 0, 0}, {0, 4, 5, 0, 6, 11, 0, 0},
+	{0, 6, 5, 4, 3, 2, 1, 0}
 };
 static const int kReactorBasePts[8][3] = {
 	{-128, 128, 0}, {128, 128, 0}, {128, -128, 0}, {-128, -128, 0},
@@ -985,11 +1075,23 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj, uint32 baseColor)
 	case kObjTeleport:
 		draw3DPrism(obj, kCWallParts[0], false, tint(baseColor, 50)); // Placeholder
 		break;
-	case kObjSink:
 	case kObjTub:
+		for (int i = 0; i < 2; i++)
+			draw3DPrism(obj, kTubParts[i], false, tint(baseColor, 0));
+		break;
+	case kObjSink:
+		for (int i = 0; i < 3; i++)
+			draw3DPrism(obj, kSinkParts[i], false, tint(baseColor, 0));
+		break;
 	case kObjToilet:
-		draw3DPrism(obj, kBox1Part, false, tint(baseColor, 0)); // Placeholder
+		for (int i = 0; i < 4; i++)
+			draw3DPrism(obj, kToiletParts[i], false, tint(baseColor, 0));
+		break;
+	case kObjPToilet:
+		for (int i = 0; i < 5; i++)
+			draw3DPrism(obj, kPToiletParts[i], false, tint(baseColor, 0));
 		break;
+
 	case kObjForkLift:
 		draw3DPrism(obj, kBox1Part, false, tint(baseColor, 0)); // Placeholder
 		break;


Commit: 56f6ae3d5b8ab42b32b6a676c38a61635e1993ab
    https://github.com/scummvm/scummvm/commit/56f6ae3d5b8ab42b32b6a676c38a61635e1993ab
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:14+02:00

Commit Message:
COLONY: backface culling

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index a41b89be730..12021ade75f 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -763,6 +763,17 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	if (!map || map[0] == kWallFeatureNone)
 		return;
 
+	// Backface culling: only draw features for the side facing the camera.
+	// This prevents backside decorations (like Level 2 lines) from bleeding through.
+	// We use non-inclusive comparisons so features remain visible while standing on the boundary.
+	switch (direction) {
+	case kDirNorth: if (_me.yloc > (cellY + 1) * 256) return; break;
+	case kDirSouth: if (_me.yloc < cellY * 256) return;       break;
+	case kDirWest:  if (_me.xloc < cellX * 256) return;       break;
+	case kDirEast:  if (_me.xloc > (cellX + 1) * 256) return; break;
+	default: break;
+	}
+	
 	float corners[4][3];
 	getWallFace3D(cellX, cellY, direction, corners);
 


Commit: 3e02a5a548d7d450c7f718fcaeb5b20210993b05
    https://github.com/scummvm/scummvm/commit/3e02a5a548d7d450c7f718fcaeb5b20210993b05
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:14+02:00

Commit Message:
COLONY: fix object vertex overflow by clamping -128 to -127

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 12021ade75f..6b12b8f26d7 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -241,8 +241,8 @@ static const int kMirrorSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
 
 // Bathtub geometry
 static const int kTubPts[8][3] = {
-	{-128, 128,  0}, {   0, 128,  0}, {   0,-128,  0}, {-128,-128,  0},
-	{-128, 128, 70}, {   0, 128, 70}, {   0,-128, 70}, {-128,-128, 70}
+	{-127, 127,  0}, {   0, 127,  0}, {   0,-127,  0}, {-127,-127,  0},
+	{-127, 127, 70}, {   0, 127, 70}, {   0,-127, 70}, {-127,-127, 70}
 };
 static const int kTubSurf[5][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
@@ -256,8 +256,8 @@ static const int kDTubSurf[1][8] = {{1, 6, 5, 4, 3, 2, 1, 0}};
 
 // Toilet geometry
 static const int kAToiletPts[8][3] = {
-	{-128, 45, 30}, {-100, 45, 30}, {-100, -45, 30}, {-128, -45, 30},
-	{-128, 45, 100}, {-100, 45, 100}, {-100, -45, 100}, {-128, -45, 100}
+	{-127, 45, 30}, {-100, 45, 30}, {-100, -45, 30}, {-127, -45, 30},
+	{-127, 45, 100}, {-100, 45, 100}, {-100, -45, 100}, {-127, -45, 100}
 };
 static const int kAToiletSurf[5][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
@@ -282,14 +282,14 @@ static const int kDToiletPts[6][3] = {
 };
 static const int kDToiletSurf[1][8] = {{10, 6, 5, 4, 3, 2, 1, 0}};
 static const int kEToiletPts[4][3] = {
-	{-128,-128, 20}, {-128,-128, 200}, { 128,-128, 200}, { 128,-128, 20}
+	{-127,-127, 20}, {-127,-127, 200}, { 127,-127, 200}, { 127,-127, 20}
 };
 static const int kEToiletSurf[1][8] = {{0, 4, 0, 1, 2, 3, 0, 0}};
 
 // Sink geometry
 static const int kSinkPts[8][3] = {
-	{-128, 50, 70}, {-50, 50, 90}, {-50,-50, 90}, {-128,-50, 70},
-	{-128, 50, 110}, {-50, 50, 110}, {-50,-50, 110}, {-128,-50, 110}
+	{-127, 50, 70}, {-50, 50, 90}, {-50,-50, 90}, {-127,-50, 70},
+	{-127, 50, 110}, {-50, 50, 110}, {-50,-50, 110}, {-127,-50, 110}
 };
 static const int kSinkSurf[5][8] = {
 	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
@@ -301,7 +301,7 @@ static const int kDSinkPts[6][3] = {
 };
 static const int kDSinkSurf[1][8] = {{1, 6, 5, 4, 3, 2, 1, 0}};
 static const int kSinkMirrorPts[4][3] = {
-	{-128, 65, 130}, {-128, -65, 130}, {-128, 65, 250}, {-128, -65, 250}
+	{-127, 65, 130}, {-127, -65, 130}, {-127, 65, 250}, {-127, -65, 250}
 };
 static const int kSinkMirrorSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
 
@@ -448,16 +448,16 @@ static const int kReactorCoreSurf[7][8] = {
 	{0, 6, 5, 4, 3, 2, 1, 0}
 };
 static const int kReactorBasePts[8][3] = {
-	{-128, 128, 0}, {128, 128, 0}, {128, -128, 0}, {-128, -128, 0},
-	{-128, 128, 120}, {128, 128, 120}, {128, -128, 120}, {-128, -128, 120}
+	{-127, 127, 0}, {127, 127, 0}, {127, -127, 0}, {-127, -127, 0},
+	{-127, 127, 120}, {127, 127, 120}, {127, -127, 120}, {-127, -127, 120}
 };
 static const int kReactorBaseSurf[6][8] = {
 	{4, 4, 0, 3, 7, 4, 0, 0}, {4, 4, 3, 2, 6, 7, 0, 0}, {4, 4, 1, 0, 4, 5, 0, 0},
 	{4, 4, 2, 1, 5, 6, 0, 0}, {4, 4, 7, 6, 5, 4, 0, 0}, {4, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kReactorTopPts[8][3] = {
-	{-128, 128, 168}, {128, 128, 168}, {128, -128, 168}, {-128, -128, 168},
-	{-128, 128, 288}, {128, 128, 288}, {128, -128, 288}, {-128, -128, 288}
+	{-127, 127, 168}, {127, 127, 168}, {127, -127, 168}, {-127, -127, 168},
+	{-127, 127, 288}, {127, 127, 288}, {127, -127, 288}, {-127, -127, 288}
 };
 
 static const Colony::ColonyEngine::PrismPartDef kBox1Part = {8, kBox1Pts, 5, kBox1Surf};


Commit: dd2cfc0aeeda1e116d1d23946f8a59160ea18dff
    https://github.com/scummvm/scummvm/commit/dd2cfc0aeeda1e116d1d23946f8a59160ea18dff
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:14+02:00

Commit Message:
COLONY: implement in-game message display system

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 95b04a6803d..c251b1a62a2 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -716,8 +716,59 @@ void ColonyEngine::handleAnimationClick(int item) {
 				drawAnimation();
 				_gfx->copyToScreen();
 			}
+		} else if (item == 7) { // Letter
+			if (_lSprites[6]->current == 1)
+				doText(_action1, 0);
+		} else if (item == 9) { // Clipboard
+			doText(_action1, 0);
+		} else if (item == 17) { // Screen
+			doText(_action0, 0);
+		} else if (item == 22) { // Book
+			doText(_action1, 0);
+		} else if (item == 24) { // Cigarette
+			doText(55, 0);
+			terminateGame(false);
+		} else if (item == 25) { // Post-it
+			doText(_action1, 0);
 		}
 		return; // Handled
+	} else if (_animationName == "vanity") {
+		if (item == 13) { // Paper
+			doText(_action0, 0);
+		} else if (item == 14) { // Badge
+			doText(80, 0);
+		} else if (item == 4) { // Diary
+			doText(_action0, 0);
+		} else if (item == 7) { // Book
+			doText(_action0, 0);
+		}
+		return;
+	} else if (_animationName == "slides") {
+		if (item == 2) { // Speaker
+			doText(261 + _creature, 0);
+		} else if (item == 5) { // Prev
+			_creature--;
+			if (_creature == 0) _creature = 8;
+			SetObjectState(1, _creature);
+		} else if (item == 6) { // Next
+			_creature++;
+			if (_creature == 9) _creature = 1;
+			SetObjectState(1, _creature);
+		}
+		return;
+	} else if (_animationName == "teleshow") {
+		if (item == 2) { // Speaker
+			doText(269 + _creature, 0);
+		} else if (item == 5) { // Prev
+			_creature--;
+			if (_creature == 0) _creature = 7;
+			SetObjectState(1, _creature);
+		} else if (item == 6) { // Next
+			_creature++;
+			if (_creature == 8) _creature = 1;
+			SetObjectState(1, _creature);
+		}
+		return;
 	} else if (_animationName == "reactor" || _animationName == "security" || _animationName == "suit") {
 		if (item >= 1 && item <= 10 && _animationName != "suit") {
 			for (int i = 5; i >= 1; i--)
@@ -893,6 +944,7 @@ void ColonyEngine::handleAnimationClick(int item) {
 
 void ColonyEngine::terminateGame(bool blowup) {
 	debug(0, "YOU HAVE BEEN TERMINATED! (blowup=%d)", blowup);
+	doText(65, 0);
 	
 	// Flash effect
 	for (int i = 0; i < 4; i++) {
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 94f90c9518a..70dd0eaa76f 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -181,6 +181,11 @@ public:
 	void cCommand(int xnew, int ynew, bool allowInteraction);
 	void scrollInfo();
 
+	void doText(int entry, int center);
+	void inform(const char *text, bool hold);
+	void printMessage(const char *text[], bool hold);
+	void makeMessageRect(Common::Rect &r);
+
 private:
 	const ADGameDescription *_gameDescription;
 
@@ -225,6 +230,8 @@ private:
 	bool _gametest = false;
 	uint32 _blackoutColor = 0;
 	uint32 _lastClickTime = 0;
+	int _action0, _action1;
+	int _creature;
 
 	int _frntxWall, _frntyWall;
 	int _sidexWall, _sideyWall;
@@ -276,6 +283,12 @@ private:
 	void wallPolygon(const float corners[4][3], const float *u, const float *v, int count, uint32 color);
 	void wallChar(const float corners[4][3], uint8 cnum);
 
+	struct TextIndex {
+		uint32 offset;
+		uint16 ch;
+		uint16 lines;
+	};
+
 	// Animation system
 	Common::Array<Sprite *> _cSprites;
 	Common::Array<ComplexSprite *> _lSprites;
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index fbb3647b963..b9f5d2c4e84 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -434,6 +434,14 @@ void OpenGLRenderer::fillEllipse(int x, int y, int rx, int ry, uint32 color) {
 
 void OpenGLRenderer::fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) {
 	fillRect(rect, color1);
+	useColor(color2);
+	glBegin(GL_POINTS);
+	for (int y = rect.top; y < rect.bottom; y++) {
+		for (int x = rect.left + (y % 2); x < rect.right; x += 2) {
+			glVertex2i(x, y);
+		}
+	}
+	glEnd();
 }
 
 void OpenGLRenderer::setPixel(int x, int y, uint32 color) {
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 688e54453ee..04fe2f10fea 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -24,7 +24,10 @@
 #include "common/system.h"
 #include "common/util.h"
 #include "common/debug.h"
+#include "common/file.h"
+#include "common/events.h"
 #include "graphics/palette.h"
+#include "graphics/fonts/dosfont.h"
 #include <math.h>
 
 namespace Colony {
@@ -442,13 +445,17 @@ bool ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Lo
 			setDoorState(fromX, fromY, direction, 0);
 			return true;
 		}
+		doText(75, 0); // "Door is locked" or similar from T.DAT
 		return false;
 	case kWallFeatureAirlock:
 		if (map[1] == 0)
 			return true;
-		if (pobject == &_me && _unlocked) {
-			setDoorState(fromX, fromY, direction, 0);
-			return true;
+		if (pobject == &_me) {
+			if (_unlocked) {
+				setDoorState(fromX, fromY, direction, 0);
+				return true;
+			}
+			inform("AIRLOCK IS SEALED.", true);
 		}
 		return false;
 	default:
@@ -466,15 +473,13 @@ void ColonyEngine::interactWithObject(int objNum) {
 
 	const int x = CLIP<int>(obj.where.xindex, 0, 30);
 	const int y = CLIP<int>(obj.where.yindex, 0, 30);
-	const int action0 = _mapData[x][y][4][3];
+	_action0 = _mapData[x][y][4][3];
+	_action1 = _mapData[x][y][4][4];
+	_creature = 1;
 
 	switch (obj.type) {
-	case kObjDesk:
-		if (loadAnimation("desk"))
-			playAnimation();
-		break;
 	case kObjConsole:
-		switch (action0) {
+		switch (_action0) {
 		case 1: // Reactor console
 			if (loadAnimation("reactor"))
 				playAnimation();
@@ -488,20 +493,22 @@ void ColonyEngine::interactWithObject(int objNum) {
 				playAnimation();
 			break;
 		default:
-			debug("CCommand: CONSOLE action=%d", action0);
+			inform("IT DOES NOT SEEM TO BE WORKING.", true);
 			break;
 		}
 		break;
 	case kObjProjector:
-		switch (action0) {
+		switch (_action0) {
 		case 1:
-			debug("CCommand: PROJECTOR creatures");
+			if (loadAnimation("slides"))
+				playAnimation();
 			break;
 		case 2:
-			debug("CCommand: PROJECTOR teleporters");
+			if (loadAnimation("teleshow")) // "teleshow" matches original interaction
+				playAnimation();
 			break;
 		default:
-			debug("CCommand: PROJECTOR action=%d", action0);
+			inform("PROJECTOR OFFLINE", true);
 			break;
 		}
 		break;
@@ -513,10 +520,10 @@ void ColonyEngine::interactWithObject(int objNum) {
 	{
 		const int targetLevelRaw = _mapData[x][y][4][2];
 		const int targetLevel = (targetLevelRaw == 0) ? _level : targetLevelRaw;
-		const int targetX = _mapData[x][y][4][3];
-		const int targetY = _mapData[x][y][4][4];
+		const int targetX = _action0;
+		const int targetY = _action1;
 		if (targetLevel >= 100 || targetX <= 0 || targetX >= 31 || targetY <= 0 || targetY >= 31) {
-			debug("CCommand: TELEPORT ignored invalid target L%d (%d,%d)", targetLevelRaw, targetX, targetY);
+			inform("TELEPORTER INITIALIZATION FAILED", true);
 			break;
 		}
 		if (targetLevel != _level)
@@ -532,7 +539,6 @@ void ColonyEngine::interactWithObject(int objNum) {
 			_robotArray[oldX][oldY] = 0;
 		if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 			_robotArray[_me.xindex][_me.yindex] = MENUM;
-			debug("CCommand: TELEPORT to L%d (%d,%d)", targetLevel, targetX, targetY);
 		break;
 	}
 	case kObjDrawer:
@@ -540,47 +546,208 @@ void ColonyEngine::interactWithObject(int objNum) {
 			playAnimation();
 		break;
 	case kObjScreen:
-		debug("CCommand: SCREEN");
+		// original game shows "Full of stars" effect/text
+		inform("I CAN SEE THROUGH IT...", true);
 		break;
 	case kObjToilet:
 	case kObjPToilet:
-		debug("CCommand: TOILET");
+		inform("IT'S A TOILET.", true); 
 		break;
 	case kObjTub:
-		debug("CCommand: TUB");
+		inform("A BATHTUB. NO TIME FOR A SOAK.", true);
 		break;
 	case kObjSink:
-		debug("CCommand: SINK");
+		inform("A SINK. IT'S DRY.", true);
 		break;
 	case kObjCryo:
-		debug("CCommand: CRYO text=%d", action0);
+		doText(_action0, 0);
 		break;
 	case kObjTV:
-		debug("CCommand: TV level=%d", _level);
+		if (_level == 1) doText(56, 0);
+		else doText(16, 0);
 		break;
 	case kObjForkLift:
 	case kObjReactor:
 	case kObjBox1:
 	case kObjBox2:
-		debug("CCommand: object type %d requires forklift/reactor flow", obj.type);
-		break;
-	case kObjPlant:
-	case kObjCChair:
-	case kObjBed:
-	case kObjTable:
-	case kObjCouch:
-	case kObjChair:
-	case kObjBBed:
-	case kObjFWall:
-	case kObjCWall:
-		// Matches DOS CCommand where these objects are non-interactive blockers.
+		inform("NEEDS FORKLIFT INTERACTION", true);
 		break;
 	default:
-		debug("CCommand: object type %d", obj.type);
 		break;
 	}
 }
 
+void ColonyEngine::inform(const char *text, bool hold) {
+	const char *msg[3];
+	msg[0] = text;
+	msg[1] = hold ? "-Press Any Key to Continue-" : nullptr;
+	msg[2] = nullptr;
+	printMessage(msg, hold);
+}
+
+void ColonyEngine::printMessage(const char *text[], bool hold) {
+	int numLines = 0;
+	int width = 0;
+	Graphics::DosFont font;
+	
+	while (text[numLines] != nullptr) {
+		int w = font.getStringWidth(text[numLines]);
+		if (w > width) width = w;
+		numLines++;
+	}
+
+	int px_per_inch_x = 72;
+	int px_per_inch_y = 72;
+
+	Common::Rect rr;
+	rr.top = _centerY - (numLines + 1) * (px_per_inch_y / 4);
+	rr.bottom = _centerY + (numLines + 1) * (px_per_inch_y / 4);
+	rr.left = _centerX - width / 2 - (px_per_inch_x / 2);
+	rr.right = _centerX + width / 2 + (px_per_inch_x / 2);
+
+	_gfx->fillDitherRect(_screenR, 0, 15);
+	makeMessageRect(rr);
+
+	int start;
+	int step;
+	if (numLines > 1) {
+		start = rr.top + (px_per_inch_y / 4) * 2;
+		step = (rr.height() - (px_per_inch_y / 4) * 4) / (numLines - 1);
+	} else {
+		start = (rr.top + rr.bottom) / 2;
+		step = 0;
+	}
+
+	for (int i = 0; i < numLines; i++) {
+		_gfx->drawString(&font, text[i], (rr.left + rr.right) / 2, start + i * step, 0, Graphics::kTextAlignCenter);
+	}
+
+	_gfx->copyToScreen();
+
+	if (hold) {
+		bool waiting = true;
+		while (waiting && !shouldQuit()) {
+			Common::Event event;
+			while (_system->getEventManager()->pollEvent(event)) {
+				if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
+					waiting = false;
+			}
+			_system->delayMillis(10);
+		}
+	}
+}
+
+void ColonyEngine::makeMessageRect(Common::Rect &rr) {
+	_gfx->fillRect(rr, 15);
+	_gfx->drawRect(rr, 0);
+	Common::Rect inner = rr;
+	inner.grow(-2);
+	_gfx->drawRect(inner, 0);
+}
+
+void ColonyEngine::doText(int entry, int center) {
+	Common::File file;
+	if (!file.open("T.DAT")) {
+		warning("doText: Could not open T.DAT");
+		return;
+	}
+
+	uint32 entries = file.readUint32BE();
+	if (entry < 0 || (uint32)entry >= entries) {
+		warning("doText: Entry %d out of range (max %d)", entry, entries);
+		file.close();
+		return;
+	}
+
+	file.seek(4 + entry * 8);
+	uint32 offset = file.readUint32BE();
+	uint16 ch = file.readUint16BE();
+	file.readUint16BE(); // lines (unused)
+
+	if (ch == 0) {
+		file.close();
+		return;
+	}
+
+	byte *page = (byte *)malloc(ch + 1);
+	file.seek(offset);
+	file.read(page, ch);
+	file.close();
+	page[ch] = 0;
+
+	// Decode: Chain XOR starting from end with '\'
+	page[ch - 1] ^= '\\';
+	for (int n = ch - 2; n >= 0; n--)
+		page[n] ^= page[n + 1];
+
+	Common::Array<Common::String> lineArray;
+	char *p = (char *)page;
+	int start = 0;
+	for (int i = 0; i < ch; i++) {
+		if (p[i] == '\r' || p[i] == '\n') {
+			p[i] = 0;
+			if (p[start]) lineArray.push_back(&p[start]);
+			start = i + 1;
+		}
+	}
+	if (start < ch && p[start]) lineArray.push_back(&p[start]);
+
+	Graphics::DosFont font;
+	int width = 0;
+	for (uint i = 0; i < lineArray.size(); i++) {
+		int w = font.getStringWidth(lineArray[i]);
+		if (w > width) width = w;
+	}
+	const char *kpress = "-Press Any Key to Continue-";
+	int kw = font.getStringWidth(kpress);
+	if (kw > width) width = kw;
+	width += 12;
+
+	int lineheight = 14;
+	int maxlines = (_screenR.height() / lineheight) - 2;
+	if (maxlines > (int)lineArray.size()) maxlines = lineArray.size();
+
+	Common::Rect r;
+	r.top = _centerY - (((maxlines + 1) * lineheight / 2) + 4);
+	r.bottom = _centerY + (((maxlines + 1) * lineheight / 2) + 4);
+	r.left = _centerX - (width / 2);
+	r.right = _centerX + (width / 2);
+
+	_gfx->fillDitherRect(_screenR, 0, 15);
+	
+	// Draw shadow/border (original draws 3 frames total)
+	for (int i = 0; i < 2; i++) {
+		_gfx->drawRect(r, 0);
+		r.translate(-1, -1);
+	}
+	_gfx->fillRect(r, 15);
+	_gfx->drawRect(r, 0);
+
+	for (int i = 0; i < maxlines; i++) {
+		_gfx->drawString(&font, lineArray[i], r.left + 3, r.top + 4 + i * lineheight, 0);
+		if (center == 2) {
+			// Teletype sound effect placeholder
+			_system->delayMillis(20); 
+		}
+	}
+
+	_gfx->drawString(&font, (int)lineArray.size() > maxlines ? "-More-" : kpress, (r.left + r.right) / 2, r.top + 6 + maxlines * lineheight, 0, Graphics::kTextAlignCenter);
+	_gfx->copyToScreen();
+
+	// Wait for key
+	bool waiting = true;
+	while (waiting && !shouldQuit()) {
+		Common::Event event;
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
+				waiting = false;
+		}
+		_system->delayMillis(10);
+	}
+
+	free(page);
+}
+
 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;


Commit: a627906fd9da83b76c8728db3b966a511226f36a
    https://github.com/scummvm/scummvm/commit/a627906fd9da83b76c8728db3b966a511226f36a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:15+02:00

Commit Message:
COLONY: implement desk animation handler with keypad decode logic

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c251b1a62a2..7bf7ca0bbc9 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -461,6 +461,60 @@ void ColonyEngine::playAnimation() {
 		case 1: SetObjectState(2, 1); SetObjectState(5, 2); break;
 		case 2: SetObjectState(2, 2); SetObjectState(5, 1); break;
 		}
+	} else if (_animationName == "desk") {
+		if (!(_action0 == 11 || _action0 == 18)) {
+			for (int i = 1; i <= 5; i++) SetObjectOnOff(i, false);
+		} else {
+			uint8 *decode = (_action0 == 11) ? _decode2 : _decode3;
+			for (int i = 0; i < 4; i++) {
+				if (decode[i] == (_action0 == 11 ? _decode2[i] : _decode3[i])) // This check is weird in original but effectively sets state
+					SetObjectState(i + 2, decode[i]);
+				else
+					SetObjectState(i + 2, 1);
+			}
+		}
+
+		if (_action0 != 10) {
+			SetObjectOnOff(23, false);
+			SetObjectOnOff(24, false);
+		}
+
+		int ntype = _action1 / 10;
+		switch (ntype) {
+		case 0:
+		case 1:
+		case 2:
+		case 3:
+			SetObjectOnOff(7, false);
+			SetObjectOnOff(8, false);
+			SetObjectOnOff(9, false);
+			SetObjectOnOff(22, false);
+			SetObjectOnOff(25, false);
+			break;
+		case 4: // letters
+			SetObjectOnOff(22, false);
+			SetObjectOnOff(9, false);
+			SetObjectOnOff(25, false);
+			break;
+		case 5: // book
+			SetObjectOnOff(7, false);
+			SetObjectOnOff(8, false);
+			SetObjectOnOff(9, false);
+			SetObjectOnOff(25, false);
+			break;
+		case 6: // clipboard
+			SetObjectOnOff(22, false);
+			SetObjectOnOff(7, false);
+			SetObjectOnOff(8, false);
+			SetObjectOnOff(25, false);
+			break;
+		case 7: // postit
+			SetObjectOnOff(22, false);
+			SetObjectOnOff(7, false);
+			SetObjectOnOff(8, false);
+			SetObjectOnOff(9, false);
+			break;
+		}
 	}
 
 	while (_animationRunning && !shouldQuit()) {
@@ -526,7 +580,7 @@ void ColonyEngine::updateAnimation() {
 }
 
 void ColonyEngine::drawAnimation() {
-	corridor(); // Draw 3D world backdrop first
+	_gfx->clear(0);
 
 	// Center 416x264 animation area on screen (from original InitDejaVu)
 	int ox = (_width - 416) / 2;
@@ -539,8 +593,12 @@ void ColonyEngine::drawAnimation() {
 		byte row = pat[y % 8];
 		for (int x = 0; x < 416; x++) {
 			bool set = (row & (0x80 >> (x % 8))) != 0;
-			// Pattern bit 1 is background color (15), bit 0 is foreground (0)
-			// matching original FillRect with inverted data.
+			// Pattern bit: 1->Black(0), 0->White(15) based on original inversion
+			// Actually Invert in readanim: ~data.
+			// Let's assume set means "white" (15) and unset "black" (0) or vice versa.
+			// In original: BackColor(Black). Pattern 1s draw ForeColor. 0s draw BackColor.
+			// If we want "not black", we likely want some white pixels.
+			// Let's try: set -> 15 (White), !set -> 0 (Black).
 			_gfx->setPixel(ox + x, oy + y, set ? 15 : 0);
 		}
 	}


Commit: 309c531bc18db6186fcdf80ca1bdd78a01e23414
    https://github.com/scummvm/scummvm/commit/309c531bc18db6186fcdf80ca1bdd78a01e23414
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:15+02:00

Commit Message:
COLONY: trigger desk animation on object interaction

Changed paths:
    engines/colony/ui.cpp


diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 04fe2f10fea..bc6a9df3043 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -478,6 +478,10 @@ void ColonyEngine::interactWithObject(int objNum) {
 	_creature = 1;
 
 	switch (obj.type) {
+	case kObjDesk:
+		if (loadAnimation("desk"))
+			playAnimation();
+		break;
 	case kObjConsole:
 		switch (_action0) {
 		case 1: // Reactor console


Commit: 8f78e7ef1bff3d3731b8da112ff0a92c79ea11d7
    https://github.com/scummvm/scummvm/commit/8f78e7ef1bff3d3731b8da112ff0a92c79ea11d7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:15+02:00

Commit Message:
COLONY: fix setWireframe to pass uint32 color instead of int cast

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 6b12b8f26d7..d59e7ec89e6 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -965,13 +965,13 @@ void ColonyEngine::renderCorridor3D() {
 	// Authentic look: Always wireframe for walls. 
 	// Power ON = White background (fill), Black lines.
 	// Power OFF = Black background (fill), White lines.
-	_gfx->setWireframe(true, lit ? 15 : 0);
+	_gfx->setWireframe(true, lit ? 7 : 0);
 
 	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
-	_gfx->clear(lit ? 15 : 0);
+	_gfx->clear(lit ? 7 : 0);
  
-	uint32 wallColor = lit ? 0 : 15; 
-	uint32 floorColor = lit ? 0 : 15; 
+	uint32 wallColor = lit ? 0 : 7; 
+	uint32 floorColor = lit ? 0 : 7; 
 
 	// Draw large floor and ceiling quads
 	// These will be filled with the background color in the occlusion pass


Commit: ffa4a3543e7e6bb9fc169c13c0015cd386d7ce1d
    https://github.com/scummvm/scummvm/commit/ffa4a3543e7e6bb9fc169c13c0015cd386d7ce1d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:16+02:00

Commit Message:
COLONY: add mouse delta tracking and keypad button reset in animation mode

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 7bf7ca0bbc9..19a2aad96fd 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -233,6 +233,8 @@ Common::Error ColonyEngine::run() {
 	_system->lockMouse(true);
 	_system->warpMouse(_centerX, _centerY);
 
+	int mouseDX = 0, mouseDY = 0;
+	bool mouseMoved = false;
 	while (!shouldQuit()) {
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
@@ -283,21 +285,29 @@ Common::Error ColonyEngine::run() {
 					}
 				debug("Me: x=%d y=%d", _me.xloc, _me.yloc);
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
-				if (event.relMouse.x != 0) {
-					_me.look = (uint8)((int)_me.look - (event.relMouse.x / 2));
-				}
-				if (event.relMouse.y != 0) {
-					_me.lookY = (int8)CLIP<int>((int)_me.lookY - (event.relMouse.y / 2), -64, 64);
-				}
-				// Warp back to center and purge remaining mouse events
-				// to prevent the warp from generating phantom deltas (Freescape pattern)
-				_system->warpMouse(_centerX, _centerY);
-				_system->getEventManager()->purgeMouseEvents();
+				mouseDX += event.relMouse.x;
+				mouseDY += event.relMouse.y;
+				mouseMoved = true;
 			} else if (event.type == Common::EVENT_SCREEN_CHANGED) {
 				_gfx->computeScreenViewport();
 			}
 		}
 
+		if (mouseMoved) {
+			if (mouseDX != 0) {
+				_me.look = (uint8)((int)_me.look - (mouseDX * _mouseSensitivity));
+			}
+			if (mouseDY != 0) {
+				_me.lookY = (int8)CLIP<int>((int)_me.lookY - (mouseDY * _mouseSensitivity), -64, 64);
+			}
+			// Warp back to center and purge remaining mouse events
+			// to prevent the warp from generating phantom deltas (Freescape pattern)
+			_system->warpMouse(_centerX, _centerY);
+			_system->getEventManager()->purgeMouseEvents();
+			mouseMoved = false;
+			mouseDX = mouseDY = 0;
+		}
+
 		_gfx->clear((_corePower[_coreIndex] > 0) ? 15 : 0);
 		
 		corridor();
@@ -839,6 +849,9 @@ void ColonyEngine::handleAnimationClick(int item) {
 		} else if (item == 11 && _animationName != "suit") { // Clear
 			for (int i = 0; i < 6; i++)
 				_animDisplay[i] = 1;
+			// Reset keypad buttons to unpressed state
+			for (int i = 1; i <= 10; i++)
+				SetObjectState(i, 1);
 			refreshAnimationDisplay();
 			drawAnimation();
 			_gfx->copyToScreen();
@@ -915,8 +928,10 @@ void ColonyEngine::handleAnimationClick(int item) {
 		}
 		if (_animationName == "reactor" || _animationName == "security") {
 			if (item <= 12) {
+				SetObjectState(item, 1); // Reset to ensure animation runs Off -> On
 				dolSprite(item - 1); // Animate the button press
-				SetObjectState(item, 1); // Return to unpressed state
+				if (item > 10) // Clear/Enter should return to Off
+					SetObjectState(item, 1);
 				drawAnimation();
 				_gfx->copyToScreen();
 			}
@@ -937,19 +952,25 @@ void ColonyEngine::handleAnimationClick(int item) {
 				return; // Exit animation immediately on success
 			} else {
 				debug(0, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
+				// Fail animation click
+				SetObjectState(4, 1);
+				dolSprite(3); // Animate lever moving and returning
+				for (int i = 6; i > 0; i--) {
+					SetObjectState(4, i);
+					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(20);
+				}
 			}
 			break;
 		case 5: // Emergency power
+			SetObjectState(5, 1); // Reset to ensure animation runs Off -> On
+			dolSprite(4); // Animate the button press
 			if (_coreState[_coreIndex] < 2) {
 				if (_corePower[_coreIndex] == 0)
 					_corePower[_coreIndex] = 1;
 				else if (_corePower[_coreIndex] == 1)
 					_corePower[_coreIndex] = 0;
-			} else {
-				// If power is already high/locked, ensure visual state is correct
-				SetObjectState(5, 1);
 			}
-			// Update power visual state:
+			// Finalize visual state according to power settings
 			switch (_corePower[_coreIndex]) {
 			case 0: SetObjectState(2, 1); SetObjectState(5, 1); break;
 			case 1: SetObjectState(2, 1); SetObjectState(5, 2); break;
@@ -960,22 +981,26 @@ void ColonyEngine::handleAnimationClick(int item) {
 			break;
 		case 7: // Damage report
 		{
-			const char *msg = "SHIP STATUS: NORMAL";
-			if (_corePower[_coreIndex] < 2) msg = "CRITICAL: MAIN POWER OFFLINE. EMERGENCY POWER ACTIVE.";
-			else if (!_orbit) msg = "ALL SYSTEMS NOMINAL. READY FOR LIFTOFF.";
-			else msg = "ORBITAL STABILIZATION AT 100%.";
-			
-			debug(0, "Damage Report: %s", msg);
+			dolSprite(6); // Button animation
+			if (_corePower[_coreIndex] < 2) {
+				doText(15, 0); // Critical status
+			} else if (!_orbit) {
+				doText(49, 0); // Ready for liftoff
+			} else {
+				doText(66, 0); // Orbital stabilization
+			}
 			
-			// Simple on-screen display for messages
-			Graphics::DosFont dosFont;
-			uint32 textColor = (_corePower[_coreIndex] > 0) ? 0 : 15;
-			_gfx->drawString(&dosFont, msg, _width / 2, _height - 20, textColor, Graphics::kTextAlignCenter);
-			_gfx->copyToScreen();
-			_system->delayMillis(2000);
+			SetObjectState(7, 1); // Reset button
+			drawAnimation(); _gfx->copyToScreen();
 			break;
 		}
+		default:
+			if (item > 0) {
+				dolSprite(item - 1);
+			}
+			break;
 		}
+		return;
 	}
 
 	if (item > 0) {
@@ -987,15 +1012,6 @@ void ColonyEngine::handleAnimationClick(int item) {
 				drawAnimation();
 				_gfx->copyToScreen();
 			}
-		} else if (_animationName == "controls") {
-			if (item == 4) { // Accelerator lever returns to state 1
-				for (int i = 6; i > 0; i--) {
-					SetObjectState(4, i);
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(20);
-				}
-			}
 		}
 	}
 }


Commit: 34f60ad43a68defc44357e23da1a66a9d0dd638f
    https://github.com/scummvm/scummvm/commit/34f60ad43a68defc44357e23da1a66a9d0dd638f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:16+02:00

Commit Message:
COLONY: fix animation sprite positioning to use screen-relative coordinates

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 19a2aad96fd..ddf0f1f567a 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -488,6 +488,10 @@ void ColonyEngine::playAnimation() {
 			SetObjectOnOff(23, false);
 			SetObjectOnOff(24, false);
 		}
+		if (_action0 != 30) SetObjectOnOff(6, false); // Teeth
+		if (_action0 != 33) { // Jack-in-the-box
+			for (int i = 18; i <= 21; i++) SetObjectOnOff(i, false);
+		}
 
 		int ntype = _action1 / 10;
 		switch (ntype) {
@@ -593,9 +597,9 @@ void ColonyEngine::drawAnimation() {
 	_gfx->clear(0);
 
 	// Center 416x264 animation area on screen (from original InitDejaVu)
-	int ox = (_width - 416) / 2;
-	ox = (ox / 8) * 8; // Round to 8 as in original code
-	int oy = (_height - 264) / 2;
+	int ox = _screenR.left + (_screenR.width() - 416) / 2;
+	ox = (ox / 8) * 8;
+	int oy = _screenR.top + (_screenR.height() - 264) / 2;
 
 	// Fill background patterns (416x264 area)
 	for (int y = 0; y < 264; y++) {
@@ -636,8 +640,8 @@ void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
 	if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size()) return;
 
 	Sprite *s = _cSprites[spriteIdx];
-	int x = ox + ls->xloc + ls->objects[cnum].xloc;
-	int y = oy + ls->yloc + ls->objects[cnum].yloc;
+	int x = ox + ls->xloc + ls->objects[cnum].xloc + s->clip.left;
+	int y = oy + ls->yloc + ls->objects[cnum].yloc + s->clip.top;
 
 	drawAnimationImage(s->fg, s->mask, x, y);
 }
@@ -701,17 +705,17 @@ void ColonyEngine::unpackBytes(Common::SeekableReadStream &file, byte *dst, uint
 }
 
 Common::Rect ColonyEngine::readRect(Common::SeekableReadStream &file) {
-	int16 top = file.readSint16LE();
 	int16 left = file.readSint16LE();
-	int16 bottom = file.readSint16LE();
+	int16 top = file.readSint16LE();
 	int16 right = file.readSint16LE();
+	int16 bottom = file.readSint16LE();
 	return Common::Rect(left, top, right, bottom);
 }
 
 int ColonyEngine::whichSprite(const Common::Point &p) {
-	int ox = (_width - 416) / 2;
+	int ox = _screenR.left + (_screenR.width() - 416) / 2;
 	ox = (ox / 8) * 8;
-	int oy = (_height - 264) / 2;
+	int oy = _screenR.top + (_screenR.height() - 264) / 2;
 	Common::Point pt(p.x - ox, p.y - oy);
 
 	debug(1, "Click at (%d, %d), relative (%d, %d)", p.x, p.y, pt.x, pt.y);
@@ -774,6 +778,10 @@ void ColonyEngine::handleAnimationClick(int item) {
 	_lastClickTime = now;
 	debug(0, "Animation click on item %d in %s", item, _animationName.c_str());
 
+	if (item > 0) {
+		dolSprite(item - 1);
+	}
+
 	if (_animationName == "desk") {
 		if (item >= 2 && item <= 5) {
 			int idx = item - 2;
@@ -785,7 +793,7 @@ void ColonyEngine::handleAnimationClick(int item) {
 				_gfx->copyToScreen();
 			}
 		} else if (item == 7) { // Letter
-			if (_lSprites[6]->current == 1)
+			if (_lSprites[6]->current > 0)
 				doText(_action1, 0);
 		} else if (item == 9) { // Clipboard
 			doText(_action1, 0);
@@ -799,7 +807,6 @@ void ColonyEngine::handleAnimationClick(int item) {
 		} else if (item == 25) { // Post-it
 			doText(_action1, 0);
 		}
-		return; // Handled
 	} else if (_animationName == "vanity") {
 		if (item == 13) { // Paper
 			doText(_action0, 0);
@@ -810,7 +817,6 @@ void ColonyEngine::handleAnimationClick(int item) {
 		} else if (item == 7) { // Book
 			doText(_action0, 0);
 		}
-		return;
 	} else if (_animationName == "slides") {
 		if (item == 2) { // Speaker
 			doText(261 + _creature, 0);
@@ -823,7 +829,6 @@ void ColonyEngine::handleAnimationClick(int item) {
 			if (_creature == 9) _creature = 1;
 			SetObjectState(1, _creature);
 		}
-		return;
 	} else if (_animationName == "teleshow") {
 		if (item == 2) { // Speaker
 			doText(269 + _creature, 0);
@@ -836,7 +841,6 @@ void ColonyEngine::handleAnimationClick(int item) {
 			if (_creature == 8) _creature = 1;
 			SetObjectState(1, _creature);
 		}
-		return;
 	} else if (_animationName == "reactor" || _animationName == "security" || _animationName == "suit") {
 		if (item >= 1 && item <= 10 && _animationName != "suit") {
 			for (int i = 5; i >= 1; i--)
@@ -928,15 +932,13 @@ void ColonyEngine::handleAnimationClick(int item) {
 		}
 		if (_animationName == "reactor" || _animationName == "security") {
 			if (item <= 12) {
-				SetObjectState(item, 1); // Reset to ensure animation runs Off -> On
-				dolSprite(item - 1); // Animate the button press
+				// SetObjectState(item, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
 				if (item > 10) // Clear/Enter should return to Off
 					SetObjectState(item, 1);
 				drawAnimation();
 				_gfx->copyToScreen();
 			}
 		}
-		return; // Handled
 	} else if (_animationName == "controls") {
 		switch (item) {
 		case 4: // Accelerator
@@ -954,7 +956,7 @@ void ColonyEngine::handleAnimationClick(int item) {
 				debug(0, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
 				// Fail animation click
 				SetObjectState(4, 1);
-				dolSprite(3); // Animate lever moving and returning
+				// dolSprite(3); // Animate lever moving and returning - handled by top dolSprite
 				for (int i = 6; i > 0; i--) {
 					SetObjectState(4, i);
 					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(20);
@@ -962,8 +964,8 @@ void ColonyEngine::handleAnimationClick(int item) {
 			}
 			break;
 		case 5: // Emergency power
-			SetObjectState(5, 1); // Reset to ensure animation runs Off -> On
-			dolSprite(4); // Animate the button press
+			// SetObjectState(5, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
+			// dolSprite(4); // Animate the button press - handled by top dolSprite
 			if (_coreState[_coreIndex] < 2) {
 				if (_corePower[_coreIndex] == 0)
 					_corePower[_coreIndex] = 1;
@@ -981,7 +983,7 @@ void ColonyEngine::handleAnimationClick(int item) {
 			break;
 		case 7: // Damage report
 		{
-			dolSprite(6); // Button animation
+			// dolSprite(6); // Button animation - handled by top dolSprite
 			if (_corePower[_coreIndex] < 2) {
 				doText(15, 0); // Critical status
 			} else if (!_orbit) {
@@ -994,41 +996,21 @@ void ColonyEngine::handleAnimationClick(int item) {
 			drawAnimation(); _gfx->copyToScreen();
 			break;
 		}
-		default:
-			if (item > 0) {
-				dolSprite(item - 1);
-			}
 			break;
 		}
-		return;
-	}
-
-	if (item > 0) {
-		dolSprite(item - 1);
-		// After dolSprite, many buttons return to state 1 (unpressed)
-		if (_animationName == "reactor" || _animationName == "security") {
-			if (item <= 12) {
-				SetObjectState(item, 1);
-				drawAnimation();
-				_gfx->copyToScreen();
-			}
-		}
 	}
 }
 
 void ColonyEngine::terminateGame(bool blowup) {
 	debug(0, "YOU HAVE BEEN TERMINATED! (blowup=%d)", blowup);
-	doText(65, 0);
 	
-	// Flash effect
-	for (int i = 0; i < 4; i++) {
-		_gfx->clear(i % 2 == 0 ? 15 : 0);
-		_gfx->copyToScreen();
-		_system->delayMillis(100);
-	}
-
-	// In a real implementation we would show a menu here
-	// For now, just quit as requested by the user
+	const char *msg[] = {
+		"   YOU HAVE BEEN TERMINATED!   ",
+		" Type 'q' to quit the game.    ",
+		nullptr
+	};
+	printMessage(msg, true);
+	
 	_system->quit();
 }
 


Commit: 78ae7323638870b709847bc971d372832fa8989a
    https://github.com/scummvm/scummvm/commit/78ae7323638870b709847bc971d372832fa8989a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:16+02:00

Commit Message:
COLONY: change crosshair color to black when core power is active

Changed paths:
    engines/colony/ui.cpp


diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index bc6a9df3043..ec311f37e5a 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -237,7 +237,10 @@ void ColonyEngine::drawCrosshair() {
 	if (!_crosshair || _screenR.width() <= 0 || _screenR.height() <= 0)
 		return;
 
-	const uint32 color = (_weapons > 0) ? 15 : 7;
+	uint32 color = (_weapons > 0) ? 15 : 7;
+	if (_corePower[_coreIndex] > 0)
+		color = 0;
+
 	const int cx = _centerX;
 	const int cy = _centerY;
 	const int qx = MAX(2, _screenR.width() / 32);


Commit: 7514fe5b9051fe943ada47c8f6707e0441698490
    https://github.com/scummvm/scummvm/commit/7514fe5b9051fe943ada47c8f6707e0441698490
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:17+02:00

Commit Message:
COLONY: implement sound effects playback

Changed paths:
  A engines/colony/sound.cpp
  A engines/colony/sound.h
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/module.mk
    engines/colony/ui.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index ddf0f1f567a..25bb44331de 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -101,14 +101,18 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_gametest = false;
 	_blackoutColor = 15; // Set to white (vINTWHITE) for better visibility in darkness
 
+	_sound = new Sound(this);
 	initTrig();
 }
 
+
 ColonyEngine::~ColonyEngine() {
 	deleteAnimation();
 	delete _gfx;
+	delete _sound;
 }
 
+
 void ColonyEngine::loadMap(int mnum) {
 	Common::String mapName = Common::String::format("MAP.%d", mnum);
 	Common::File file;
@@ -1003,6 +1007,8 @@ void ColonyEngine::handleAnimationClick(int item) {
 
 void ColonyEngine::terminateGame(bool blowup) {
 	debug(0, "YOU HAVE BEEN TERMINATED! (blowup=%d)", blowup);
+	if (blowup)
+		_sound->play(Sound::kExplode);
 	
 	const char *msg[] = {
 		"   YOU HAVE BEEN TERMINATED!   ",
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 70dd0eaa76f..35d6b9e6e6d 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -28,6 +28,8 @@
 #include "common/random.h"
 #include "common/rect.h"
 #include "colony/gfx.h"
+#include "colony/sound.h"
+
 
 namespace Colony {
 
@@ -200,6 +202,8 @@ private:
 	int _robotNum;
 
 	Renderer *_gfx;
+	Sound *_sound;
+
 
 	int _tsin, _tcos;
 	int _sint[256];
diff --git a/engines/colony/module.mk b/engines/colony/module.mk
index cc50ad9ec34..97383af05a2 100644
--- a/engines/colony/module.mk
+++ b/engines/colony/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS := \
 	metaengine.o \
 	render.o \
 	renderer_opengl.o \
+	sound.o \
 	ui.o
 
 MODULE_DIRS += \
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
new file mode 100644
index 00000000000..023aa0f85a1
--- /dev/null
+++ b/engines/colony/sound.cpp
@@ -0,0 +1,222 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/sound.h"
+#include "colony/colony.h"
+
+namespace Colony {
+
+Sound::Sound(ColonyEngine *vm) : _vm(vm) {
+	_speaker = new Audio::PCSpeaker();
+	_speaker->init();
+}
+
+Sound::~Sound() {
+	delete _speaker;
+}
+
+void Sound::stop() {
+	if (_speaker->isPlaying())
+		_speaker->stop();
+}
+
+void Sound::play(int soundID) {
+	if (_speaker->isPlaying())
+		_speaker->stop();
+	playPCSpeaker(soundID);
+}
+
+void Sound::playPCSpeaker(int soundID) {
+	const uint32 tickUs = 54945; // ~18.2 Hz (PIT 1193181 / 65536)
+
+	auto queueTick = [&](uint32 divider, uint32 ticks) {
+		if (divider == 0)
+			_speaker->playQueue(Audio::PCSpeaker::kWaveFormSilence, 0, ticks * tickUs);
+		else
+			_speaker->playQueue(Audio::PCSpeaker::kWaveFormSquare, 1193180.0f / divider, ticks * tickUs);
+	};
+
+	switch (soundID) {
+	case kShoot: // "Them"
+		queueTick(8000, 10);
+		for (int i = 0; i < 10; i++) {
+			queueTick(8000, 1);
+			queueTick(0, 1);
+		}
+		break;
+	case kDoor: // "Door1"
+		queueTick(1000, 7);
+		queueTick(2000, 3);
+		break;
+	case kBang: // "Ugh"
+	{
+		uint32 div = 400;
+		for (int i = 0; i < 30; i++) {
+			queueTick(div, 1);
+			div += 134;
+		}
+		break;
+	}
+	case kOuch:
+	case kBonk:
+		queueTick(4649, 3);
+		break;
+	case kKlaxon:
+		for (int j = 0; j < 4; j++) {
+			uint32 div = 4649;
+			while (div > 4000) {
+				queueTick(div, 1);
+				div -= 64;
+			}
+		}
+		break;
+	case kChime:
+		queueTick(4649, 7);
+		queueTick(3690, 7);
+		queueTick(3103, 7);
+		break;
+	case kEat:
+		queueTick(0x8888, 2);
+		queueTick(0x7777, 2);
+		queueTick(0x6666, 2);
+		queueTick(0x5555, 2);
+		queueTick(0x4444, 2);
+		queueTick(0x3333, 2);
+		queueTick(0x2222, 2);
+		queueTick(0x1111, 2);
+		queueTick(1536, 2); // 0600h
+		break;
+	case kExplode: // "Explode2"
+	{
+		uint32 div = 1280;
+		queueTick(div, 1);
+		for (int i = 0; i < 48; i++) {
+			div -= 2;
+			queueTick(div, 1);
+		}
+		div = 36864;
+		queueTick(div, 1);
+		for (int i = 0; i < 80; i++) {
+			queueTick(div, 1);
+		}
+		break;
+	}
+	case kAirlock:
+		queueTick(57344, 150);
+		queueTick(512, 2);
+		queueTick(1024, 1);
+		break;
+	case kElevator:
+		for (int i = 0; i < 8; i++) {
+			queueTick(512, 2);
+			queueTick(1024, 75);
+		}
+		break;
+	case kTeleport:
+	{
+		uint32 div = 400;
+		queueTick(div, 1);
+		for (int i = 0; i < 100; i++) {
+			div += 134;
+			queueTick(div, 1);
+		}
+		break;
+	}
+	case kLift:
+	{
+		uint32 div = 4649;
+		queueTick(div, 1);
+		while (div > 3103) {
+			div -= 8;
+			queueTick(div, 1);
+		}
+		break;
+	}
+	case kDrop:
+	{
+		uint32 div = 3103;
+		queueTick(div, 1);
+		while (div < 4649) {
+			div += 8;
+			queueTick(div, 1);
+		}
+		break;
+	}
+	case kGlass:
+		for (int j = 0; j < 20; j++) {
+			uint32 div = 60000;
+			queueTick(div, 1);
+			while (div > 50000) {
+				div -= 1000;
+				queueTick(div, 1);
+			}
+			queueTick(0, 10);
+			queueTick(div, 1);
+		}
+		break;
+	case kSink:
+		for (int j = 0; j < 1; j++) {
+			uint32 div = 200;
+			queueTick(div, 1);
+			while (div < 1600) {
+				div += 200;
+				queueTick(div, 1);
+			}
+		}
+		break;
+	case kDit:
+		queueTick(210, 3);
+		queueTick(9210, 2);
+		queueTick(3000, 3);
+		queueTick(1000, 2);
+		queueTick(40000, 3);
+		break;
+	case kStars1:
+	case kStars2:
+	case kStars3:
+	case kStars4:
+		queueTick(4000, 2);
+		queueTick(8000, 2);
+		queueTick(2000, 2);
+		queueTick(6000, 2);
+		break;
+	case kToilet: // "Sailor's Hornpipe"
+		queueTick(2651, 4); // G
+		queueTick(1985, 4); // C
+		queueTick(1985, 2); // C
+		queueTick(1985, 4); // C
+		queueTick(1768, 2); // D
+		queueTick(1575, 4); // E
+		queueTick(1768, 4); // D
+		queueTick(1985, 4); // C
+		break;
+	case kBath: // "Rubber Ducky"
+		queueTick(1985, 4); // C
+		queueTick(1575, 4); // E
+		queueTick(1324, 4); // G
+		queueTick(1182, 8); // A
+		break;
+	default:
+		break;
+	}
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/sound.h b/engines/colony/sound.h
new file mode 100644
index 00000000000..dd247caef16
--- /dev/null
+++ b/engines/colony/sound.h
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef COLONY_SOUND_H
+#define COLONY_SOUND_H
+
+#include "audio/softsynth/pcspk.h"
+#include "common/ptr.h"
+
+namespace Colony {
+
+class ColonyEngine;
+
+class Sound {
+public:
+	Sound(ColonyEngine *vm);
+	~Sound();
+
+	void play(int soundID);
+	void stop();
+
+	enum {
+		kKlaxon,
+		kAirlock,
+		kOuch,
+		kChime,
+		kBang,
+		kShoot,
+		kEat,
+		kBonk,
+		kBzzz,
+		kExplode,
+		kElevator,
+		kPShot,
+		kTest,
+		kDit,
+		kSink,
+		kClatter,
+		kStop,
+		kTeleport,
+		kSlug,
+		kTunnel2,
+		kLift,
+		kDrop,
+		kGlass,
+		kDoor,
+		kStars1,
+		kStars2,
+		kStars3,
+		kStars4,
+		kToilet,
+		kBath
+	};
+
+private:
+	ColonyEngine *_vm;
+	Audio::PCSpeaker *_speaker;
+
+	void playPCSpeaker(int soundID);
+};
+
+} // End of namespace Colony
+
+#endif // COLONY_SOUND_H
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index ec311f37e5a..c09f77f120e 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -318,7 +318,9 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 			if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirNorth, pobject))
 				return moveTo();
 			debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
+			_sound->play(Sound::kBang);
 			return -1;
+
 		}
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 1))
@@ -326,7 +328,9 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirSouth, pobject))
 			return moveTo();
 		debug("Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
+		_sound->play(Sound::kBang);
 		return -1;
+
 	}
 
 	if (yind2 == pobject->yindex) {
@@ -336,7 +340,9 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 			if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirEast, pobject))
 				return moveTo();
 			debug("Collision East at x=%d y=%d", xind2, pobject->yindex);
+			_sound->play(Sound::kBang);
 			return -1;
+
 		}
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 2))
@@ -344,7 +350,9 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirWest, pobject))
 			return moveTo();
 		debug("Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
+		_sound->play(Sound::kBang);
 		return -1;
+
 	}
 
 	// Diagonal
@@ -386,6 +394,9 @@ bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
 		return false;
 
 	_mapData[x][y][direction][1] = (uint8)state;
+	if (state == 0)
+		_sound->play(Sound::kDoor);
+
 
 	int nx = x;
 	int ny = y;
@@ -455,9 +466,11 @@ bool ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Lo
 			return true;
 		if (pobject == &_me) {
 			if (_unlocked) {
+				_sound->play(Sound::kAirlock);
 				setDoorState(fromX, fromY, direction, 0);
 				return true;
 			}
+
 			inform("AIRLOCK IS SEALED.", true);
 		}
 		return false;
@@ -525,7 +538,9 @@ void ColonyEngine::interactWithObject(int objNum) {
 		break;
 	case kObjTeleport:
 	{
+		_sound->play(Sound::kTeleport);
 		const int targetLevelRaw = _mapData[x][y][4][2];
+
 		const int targetLevel = (targetLevelRaw == 0) ? _level : targetLevelRaw;
 		const int targetX = _action0;
 		const int targetY = _action1;
@@ -554,16 +569,22 @@ void ColonyEngine::interactWithObject(int objNum) {
 		break;
 	case kObjScreen:
 		// original game shows "Full of stars" effect/text
+		_sound->play(Sound::kStars1);
 		inform("I CAN SEE THROUGH IT...", true);
 		break;
+
 	case kObjToilet:
 	case kObjPToilet:
+		_sound->play(Sound::kToilet);
 		inform("IT'S A TOILET.", true); 
 		break;
 	case kObjTub:
+		_sound->play(Sound::kBath);
 		inform("A BATHTUB. NO TIME FOR A SOAK.", true);
 		break;
+
 	case kObjSink:
+		_sound->play(Sound::kSink);
 		inform("A SINK. IT'S DRY.", true);
 		break;
 	case kObjCryo:
@@ -733,8 +754,8 @@ void ColonyEngine::doText(int entry, int center) {
 	for (int i = 0; i < maxlines; i++) {
 		_gfx->drawString(&font, lineArray[i], r.left + 3, r.top + 4 + i * lineheight, 0);
 		if (center == 2) {
-			// Teletype sound effect placeholder
-			_system->delayMillis(20); 
+			_sound->play(Sound::kDit);
+			_system->delayMillis(20);
 		}
 	}
 


Commit: 2497037be8220d6d0343aabac59ab21ed12c9a8d
    https://github.com/scummvm/scummvm/commit/2497037be8220d6d0343aabac59ab21ed12c9a8d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:17+02:00

Commit Message:
COLONY: add named color constants enum for object rendering

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 35d6b9e6e6d..fdda2ce611e 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -85,6 +85,40 @@ enum ObjectType {
 	kObjBBed = 42
 };
 
+enum ObjColor {
+	kColorClear = 0,
+	kColorBlack = 1,
+	kColorDkGray = 9,
+	kColorLtGreen = 11,
+	kColorBath = 17,
+	kColorWater = 18,
+	kColorSilver = 19,
+	kColorReactor = 20,
+	kColorBlanket = 21,
+	kColorSheet = 22,
+	kColorBed = 23,
+	kColorBox = 24,
+	kColorChair = 26,
+	kColorChairBase = 27,
+	kColorCouch = 28,
+	kColorConsole = 29,
+	kColorTV = 30,
+	kColorTVScreen = 31,
+	kColorDrawer = 32,
+	kColorDesk = 37,
+	kColorDeskTop = 38,
+	kColorDeskChair = 39,
+	kColorMac = 40,
+	kColorMacScreen = 41,
+	kColorPot = 52,
+	kColorPlant = 53,
+	kColorTable = 61,
+	kColorTableBase = 62,
+	kColorWall = 77,
+	kColorRainbow1 = 80,
+	kColorRainbow2 = 81
+};
+
 #define BASEOBJECT 20
 #define MENUM 101
 
@@ -254,7 +288,6 @@ private:
 	uint8 wallAt(int x, int y) const;
 	const uint8 *mapFeatureAt(int x, int y, int direction) const;
 	void drawStaticObjects();
-	uint32 objectColor(int type) const;
 
 public:
 	struct PrismPartDef {
@@ -265,8 +298,8 @@ public:
 	};
 
 private:
-	void draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, uint32 color);
-	bool drawStaticObjectPrisms3D(const Thing &obj, uint32 baseColor);
+	void draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook);
+	bool drawStaticObjectPrisms3D(const Thing &obj);
 	void renderCorridor3D();
 	void drawWallFeatures3D();
 	void drawWallFeature3D(int cellX, int cellY, int direction);
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index d59e7ec89e6..8e600258bda 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -36,46 +36,87 @@ static const int g_indexTable[4][10] = {
 	{0, 0,  0, 1,  1,  0,  0, -1,  2, 1}
 };
 
+// Color lookup: maps ObjColor constant → LINECOLOR (EGA palette index)
+// From DOS ROBOCOLR.C lsColor[index][LINECOLOR] (field 4).
+// Default DOS mode is polyfill=False: objects are wireframe-only, no polygon fills.
+// The fill color stays as the wall background; only outline color varies per surface.
+static uint8 lookupLineColor(int colorIdx) {
+	switch (colorIdx) {
+	case kColorClear:     return 0;  // Transparent (skipped before drawing)
+	case kColorBlack:     return 0;  // vBLACK
+	case kColorDkGray:    return 8;  // vDKGRAY
+	case kColorLtGreen:   return 10; // vLTGREEN
+	case kColorBath:      return 0;  // vBLACK
+	case kColorWater:     return 1;  // vBLUE
+	case kColorSilver:    return 1;  // vBLUE
+	case kColorReactor:   return 7;  // vWHITE
+	case kColorBlanket:   return 2;  // vGREEN
+	case kColorSheet:     return 15; // vINTWHITE
+	case kColorBed:       return 6;  // vBROWN
+	case kColorBox:       return 6;  // vBROWN
+	case kColorChair:     return 1;  // vBLUE
+	case kColorChairBase: return 1;  // vBLUE
+	case kColorCouch:     return 4;  // vRED
+	case kColorConsole:   return 4;  // vRED
+	case kColorTV:        return 6;  // vBROWN
+	case kColorTVScreen:  return 8;  // vDKGRAY
+	case kColorDrawer:    return 6;  // vBROWN
+	case kColorDesk:      return 6;  // vBROWN
+	case kColorDeskTop:   return 6;  // vBROWN
+	case kColorDeskChair: return 2;  // vGREEN
+	case kColorMac:       return 0;  // vBLACK
+	case kColorMacScreen: return 8;  // vDKGRAY
+	case kColorPot:       return 6;  // vBROWN
+	case kColorPlant:     return 2;  // vGREEN (hardcoded in MakePlant via DrawLeaf)
+	case kColorTable:     return 6;  // vBROWN
+	case kColorTableBase: return 6;  // vBROWN
+	case kColorWall:      return 0;  // vBLACK
+	case kColorRainbow1:  return 4;  // vRED
+	case kColorRainbow2:  return 14; // vYELLOW
+	default:              return 0;  // vBLACK
+	}
+}
+
 // DOS object geometry constants
 static const int kScreenPts[8][3] = {
 	{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
 	{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
 };
 static const int kScreenSurf[4][8] = {
-	{1, 4, 0, 3, 7, 4, 0, 0}, {1, 4, 3, 2, 6, 7, 0, 0},
-	{1, 4, 2, 1, 5, 6, 0, 0}, {1, 4, 7, 6, 5, 4, 0, 0}
+	{kColorBlack, 4, 0, 3, 7, 4, 0, 0}, {kColorBlack, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBlack, 4, 2, 1, 5, 6, 0, 0}, {kColorBlack, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kTableTopPts[4][3] = {
 	{-64, 64, 100}, {64, 64, 100}, {64, -64, 100}, {-64, -64, 100}
 };
-static const int kTableTopSurf[1][8] = {{6, 4, 3, 2, 1, 0, 0, 0}};
+static const int kTableTopSurf[1][8] = {{kColorTable, 4, 3, 2, 1, 0, 0, 0}};
 static const int kTableBasePts[8][3] = {
 	{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0},
 	{-20, 20, 100}, {20, 20, 100}, {20, -20, 100}, {-20, -20, 100}
 };
 static const int kTableBaseSurf[4][8] = {
-	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
-	{6, 4, 1, 0, 4, 5, 0, 0}, {6, 4, 2, 1, 5, 6, 0, 0}
+	{kColorTableBase, 4, 0, 3, 7, 4, 0, 0}, {kColorTableBase, 4, 3, 2, 6, 7, 0, 0},
+	{kColorTableBase, 4, 1, 0, 4, 5, 0, 0}, {kColorTableBase, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kBedPostPts[4][3] = {
 	{-80, 180, 0}, {80, 180, 0}, {80, 180, 100}, {-80, 180, 100}
 };
-static const int kBedPostSurf[1][8] = {{6, 4, 3, 2, 1, 0, 0, 0}};
+static const int kBedPostSurf[1][8] = {{kColorBed, 4, 3, 2, 1, 0, 0, 0}};
 static const int kBedBlanketPts[8][3] = {
 	{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
 	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
 };
 static const int kBlanketSurf[4][8] = {
-	{2, 4, 0, 3, 7, 4, 0, 0}, {2, 4, 3, 2, 6, 7, 0, 0},
-	{2, 4, 2, 1, 5, 6, 0, 0}, {2, 4, 7, 6, 5, 4, 0, 0}
+	{kColorBlanket, 4, 0, 3, 7, 4, 0, 0}, {kColorBlanket, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBlanket, 4, 2, 1, 5, 6, 0, 0}, {kColorBlanket, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kBedSheetPts[8][3] = {
 	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60},
 	{-80, 70, 80}, {80, 70, 80}, {80, -175, 80}, {-80, -175, 80}
 };
 static const int kSheetSurf[3][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
+	{kColorSheet, 4, 0, 3, 7, 4, 0, 0}, {kColorSheet, 4, 2, 1, 5, 6, 0, 0},
+	{kColorSheet, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kBBedBlanketPts[8][3] = {
 	{-120, 96, 0}, {120, 96, 0}, {120, -96, 0}, {-120, -96, 0},
@@ -91,7 +132,7 @@ static const int kBBedPostPts[4][3] = {
 static const int kDeskTopPts[4][3] = {
 	{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
 };
-static const int kDeskTopSurf[1][8] = {{6, 4, 3, 2, 1, 0, 0, 0}};
+static const int kDeskTopSurf[1][8] = {{kColorDeskTop, 4, 3, 2, 1, 0, 0, 0}};
 static const int kDeskLeftPts[8][3] = {
 	{-135, 95, 0}, {-55, 95, 0}, {-55, -95, 0}, {-135, -95, 0},
 	{-135, 95, 100}, {-55, 95, 100}, {-55, -95, 100}, {-135, -95, 100}
@@ -101,13 +142,13 @@ static const int kDeskRightPts[8][3] = {
 	{55, 95, 100}, {135, 95, 100}, {135, -95, 100}, {55, -95, 100}
 };
 static const int kDeskCabSurf[4][8] = {
-	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
-	{6, 4, 1, 0, 4, 5, 0, 0}, {6, 4, 2, 1, 5, 6, 0, 0}
+	{kColorDesk, 4, 0, 3, 7, 4, 0, 0}, {kColorDesk, 4, 3, 2, 6, 7, 0, 0},
+	{kColorDesk, 4, 1, 0, 4, 5, 0, 0}, {kColorDesk, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kSeatPts[4][3] = {
 	{-40, 210, 60}, {40, 210, 60}, {40, 115, 60}, {-40, 115, 60}
 };
-static const int kSeatSurf[1][8] = {{1, 4, 3, 2, 1, 0, 0, 0}};
+static const int kSeatSurf[1][8] = {{kColorDeskChair, 4, 3, 2, 1, 0, 0, 0}};
 static const int kArmLeftPts[4][3] = {
 	{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
 };
@@ -115,13 +156,13 @@ static const int kArmRightPts[4][3] = {
 	{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
 };
 static const int kArmSurf[2][8] = {
-	{1, 4, 3, 2, 1, 0, 0, 0}, {1, 4, 0, 1, 2, 3, 0, 0}
+	{kColorClear, 4, 3, 2, 1, 0, 0, 0}, {kColorClear, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kBackPts[4][3] = {
 	{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
 };
 static const int kBackSurf[2][8] = {
-	{1, 4, 3, 2, 1, 0, 0, 0}, {1, 4, 0, 1, 2, 3, 0, 0}
+	{kColorDeskChair, 4, 3, 2, 1, 0, 0, 0}, {kColorDeskChair, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kComputerPts[8][3] = {
 	{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
@@ -132,53 +173,53 @@ static const int kMonitorPts[8][3] = {
 	{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
 };
 static const int kComputerSurf[5][8] = {
-	{6, 4, 7, 6, 5, 4, 0, 0}, {6, 4, 0, 3, 7, 4, 0, 0},
-	{6, 4, 3, 2, 6, 7, 0, 0}, {6, 4, 1, 0, 4, 5, 0, 0},
-	{6, 4, 2, 1, 5, 6, 0, 0}
+	{kColorMac, 4, 7, 6, 5, 4, 0, 0}, {kColorMac, 4, 0, 3, 7, 4, 0, 0},
+	{kColorMac, 4, 3, 2, 6, 7, 0, 0}, {kColorMac, 4, 1, 0, 4, 5, 0, 0},
+	{kColorMac, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kDeskScreenPts[4][3] = {
 	{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
 };
-static const int kDeskScreenSurf[1][8] = {{1, 4, 3, 2, 1, 0, 0, 0}};
+static const int kDeskScreenSurf[1][8] = {{kColorMacScreen, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCSeatPts[4][3] = {
 	{-40, 40, 60}, {40, 40, 60}, {40, -40, 60}, {-40, -40, 60}
 };
-static const int kCSeatSurf[1][8] = {{1, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCSeatSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCArmLeftPts[4][3] = {
 	{-50, 40, 90}, {-40, 40, 60}, {-40, -40, 60}, {-50, -40, 90}
 };
-static const int kCArmLeftSurf[1][8] = {{1, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCArmLeftSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCArmRightPts[4][3] = {
 	{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
 };
-static const int kCArmRightSurf[1][8] = {{1, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCArmRightSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
 static const int kCBackPts[4][3] = {
 	{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
 };
 static const int kCBackSurf[2][8] = {
-	{1, 4, 3, 2, 1, 0, 0, 0}, {1, 4, 0, 1, 2, 3, 0, 0}
+	{kColorChair, 4, 3, 2, 1, 0, 0, 0}, {kColorChair, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kCBasePts[8][3] = {
 	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
 	{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
 };
 static const int kCBaseSurf[4][8] = {
-	{1, 4, 0, 3, 7, 4, 0, 0}, {1, 4, 3, 2, 6, 7, 0, 0},
-	{1, 4, 1, 0, 4, 5, 0, 0}, {1, 4, 2, 1, 5, 6, 0, 0}
+	{kColorChairBase, 4, 0, 3, 7, 4, 0, 0}, {kColorChairBase, 4, 3, 2, 6, 7, 0, 0},
+	{kColorChairBase, 4, 1, 0, 4, 5, 0, 0}, {kColorChairBase, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kConsolePts[8][3] = {
 	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
 	{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
 };
 static const int kConsoleSurf[5][8] = {
-	{4, 4, 4, 0, 3, 7, 0, 0}, {4, 4, 7, 3, 2, 6, 0, 0},
-	{4, 4, 5, 1, 0, 4, 0, 0}, {4, 4, 6, 2, 1, 5, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
+	{kColorConsole, 4, 4, 0, 3, 7, 0, 0}, {kColorConsole, 4, 7, 3, 2, 6, 0, 0},
+	{kColorConsole, 4, 5, 1, 0, 4, 0, 0}, {kColorConsole, 4, 6, 2, 1, 5, 0, 0},
+	{kColorBlack, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kCouchSurf[5][8] = {
-	{4, 4, 0, 3, 7, 4, 0, 0}, {4, 4, 3, 2, 6, 7, 0, 0},
-	{4, 4, 1, 0, 4, 5, 0, 0}, {4, 4, 2, 1, 5, 6, 0, 0},
-	{4, 4, 7, 6, 5, 4, 0, 0}
+	{kColorCouch, 4, 0, 3, 7, 4, 0, 0}, {kColorCouch, 4, 3, 2, 6, 7, 0, 0},
+	{kColorCouch, 4, 1, 0, 4, 5, 0, 0}, {kColorCouch, 4, 2, 1, 5, 6, 0, 0},
+	{kColorCouch, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kACouchPts[8][3] = {
 	{-50, 150, 0}, {50, 150, 0}, {50, -150, 0}, {-50, -150, 0},
@@ -217,27 +258,27 @@ static const int kTVBodyPts[8][3] = {
 	{-30, 60, 120}, {30, 60, 120}, {30, -60, 120}, {-30, -60, 120}
 };
 static const int kTVBodySurf[5][8] = {
-	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
-	{6, 4, 1, 0, 4, 5, 0, 0}, {6, 4, 2, 1, 5, 6, 0, 0},
-	{6, 4, 7, 6, 5, 4, 0, 0}
+	{kColorTV, 4, 0, 3, 7, 4, 0, 0}, {kColorTV, 4, 3, 2, 6, 7, 0, 0},
+	{kColorTV, 4, 1, 0, 4, 5, 0, 0}, {kColorTV, 4, 2, 1, 5, 6, 0, 0},
+	{kColorTV, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kTVScreenPts[4][3] = {
 	{30, 50, 10}, {30, -50, 10}, {30, 50, 110}, {30, -50, 110}
 };
-static const int kTVScreenSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
+static const int kTVScreenSurf[1][8] = {{kColorTVScreen, 4, 1, 0, 2, 3, 0, 0}};
 static const int kDrawerPts[8][3] = {
 	{-80, 70, 0}, {0, 70, 0}, {0, -70, 0}, {-80, -70, 0},
 	{-80, 70, 100}, {0, 70, 100}, {0, -70, 100}, {-80, -70, 100}
 };
 static const int kDrawerSurf[5][8] = {
-	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
-	{6, 4, 1, 0, 4, 5, 0, 0}, {6, 4, 2, 1, 5, 6, 0, 0},
-	{6, 4, 7, 6, 5, 4, 0, 0}
+	{kColorDrawer, 4, 0, 3, 7, 4, 0, 0}, {kColorDrawer, 4, 3, 2, 6, 7, 0, 0},
+	{kColorDrawer, 4, 1, 0, 4, 5, 0, 0}, {kColorDrawer, 4, 2, 1, 5, 6, 0, 0},
+	{kColorDrawer, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kMirrorPts[4][3] = {
 	{-80, 65, 100}, {-80, -65, 100}, {-80, 65, 210}, {-80, -65, 210}
 };
-static const int kMirrorSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
+static const int kMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
 
 // Bathtub geometry
 static const int kTubPts[8][3] = {
@@ -245,14 +286,14 @@ static const int kTubPts[8][3] = {
 	{-127, 127, 70}, {   0, 127, 70}, {   0,-127, 70}, {-127,-127, 70}
 };
 static const int kTubSurf[5][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
+	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
+	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kDTubPts[6][3] = {
 	{-16, 112, 70}, {-8, 0, 70}, {-16, -112, 70}, {-112, -112, 70}, {-120, 0, 70}, {-112, 112, 70}
 };
-static const int kDTubSurf[1][8] = {{1, 6, 5, 4, 3, 2, 1, 0}};
+static const int kDTubSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
 
 // Toilet geometry
 static const int kAToiletPts[8][3] = {
@@ -260,31 +301,31 @@ static const int kAToiletPts[8][3] = {
 	{-127, 45, 100}, {-100, 45, 100}, {-100, -45, 100}, {-127, -45, 100}
 };
 static const int kAToiletSurf[5][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
+	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
+	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kBToiletPts[12][3] = {
 	{-100, 20, 50}, {-60, 40, 50}, {-20, 20, 50}, {-20, -20, 50}, {-60, -40, 50}, {-100, -20, 50},
 	{-80, 10,  0}, {-60, 20,  0}, {-40, 10,  0}, {-40, -10,  0}, {-60, -20,  0}, {-80, -10,  0}
 };
 static const int kBToiletSurf[7][8] = {
-	{0, 4, 0, 1, 7, 6, 0, 0}, {0, 4, 1, 2, 8, 7, 0, 0}, {0, 4, 2, 3, 9, 8, 0, 0},
-	{0, 4, 3, 4, 10, 9, 0, 0}, {0, 4, 4, 5, 11, 10, 0, 0}, {0, 4, 5, 0, 6, 11, 0, 0},
-	{0, 6, 5, 4, 3, 2, 1, 0}
+	{kColorBath, 4, 0, 1, 7, 6, 0, 0}, {kColorBath, 4, 1, 2, 8, 7, 0, 0}, {kColorBath, 4, 2, 3, 9, 8, 0, 0},
+	{kColorBath, 4, 3, 4, 10, 9, 0, 0}, {kColorBath, 4, 4, 5, 11, 10, 0, 0}, {kColorBath, 4, 5, 0, 6, 11, 0, 0},
+	{kColorBath, 6, 5, 4, 3, 2, 1, 0}
 };
 static const int kCToiletPts[6][3] = {
 	{-95, 15, 50}, {-60, 35, 50}, {-25, 15, 50}, {-25, -15, 50}, {-60, -35, 50}, {-95, -15, 50}
 };
-static const int kCToiletSurf[1][8] = {{1, 6, 5, 4, 3, 2, 1, 0}};
+static const int kCToiletSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
 static const int kDToiletPts[6][3] = {
 	{-100, 20, 50}, {-100, 40, 90}, {-100, 20, 130}, {-100, -20, 130}, {-100, -40, 90}, {-100, -20, 50}
 };
-static const int kDToiletSurf[1][8] = {{10, 6, 5, 4, 3, 2, 1, 0}};
+static const int kDToiletSurf[1][8] = {{kColorLtGreen, 6, 5, 4, 3, 2, 1, 0}};
 static const int kEToiletPts[4][3] = {
 	{-127,-127, 20}, {-127,-127, 200}, { 127,-127, 200}, { 127,-127, 20}
 };
-static const int kEToiletSurf[1][8] = {{0, 4, 0, 1, 2, 3, 0, 0}};
+static const int kEToiletSurf[1][8] = {{kColorDkGray, 4, 0, 1, 2, 3, 0, 0}};
 
 // Sink geometry
 static const int kSinkPts[8][3] = {
@@ -292,18 +333,18 @@ static const int kSinkPts[8][3] = {
 	{-127, 50, 110}, {-50, 50, 110}, {-50,-50, 110}, {-127,-50, 110}
 };
 static const int kSinkSurf[5][8] = {
-	{0, 4, 0, 3, 7, 4, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0},
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0},
-	{0, 4, 7, 6, 5, 4, 0, 0}
+	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
+	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kDSinkPts[6][3] = {
 	{-55, 0, 110}, {-60, -45, 110}, {-118, -45, 110}, {-123, 0, 110}, {-118, 45, 110}, {-60, 45, 110}
 };
-static const int kDSinkSurf[1][8] = {{1, 6, 5, 4, 3, 2, 1, 0}};
+static const int kDSinkSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
 static const int kSinkMirrorPts[4][3] = {
 	{-127, 65, 130}, {-127, -65, 130}, {-127, 65, 250}, {-127, -65, 250}
 };
-static const int kSinkMirrorSurf[1][8] = {{0, 4, 1, 0, 2, 3, 0, 0}};
+static const int kSinkMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
 
 static const Colony::ColonyEngine::PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
 static const Colony::ColonyEngine::PrismPartDef kTableParts[2] = {
@@ -388,7 +429,7 @@ static const int kCWallPts[8][3] = {
 	{-128, 128, 320},  {0, 112, 320},  {112, 0, 320},  {128, -128, 320}
 };
 static const int kCWallSurf[3][8] = {
-	{0, 4, 1, 0, 4, 5, 0, 0}, {0, 4, 2, 1, 5, 6, 0, 0}, {0, 4, 3, 2, 6, 7, 0, 0}
+	{kColorWall, 4, 1, 0, 4, 5, 0, 0}, {kColorWall, 4, 2, 1, 5, 6, 0, 0}, {kColorWall, 4, 3, 2, 6, 7, 0, 0}
 };
 static const Colony::ColonyEngine::PrismPartDef kCWallParts[1] = {{8, kCWallPts, 3, kCWallSurf}};
 
@@ -397,13 +438,13 @@ static const int kPlantPotPts[12][3] = {
 	{8, 12, 0}, {15, 0, 0}, {8, -12, 0}, {-8, -12, 0}, {-15, 0, 0}, {-8, 12, 0}
 };
 static const int kPlantPotSurf[6][8] = {
-	{6, 4, 0, 1, 7, 6, 0, 0}, {6, 4, 1, 2, 8, 7, 0, 0}, {6, 4, 2, 3, 9, 8, 0, 0},
-	{6, 4, 3, 4, 10, 9, 0, 0}, {6, 4, 4, 5, 11, 10, 0, 0}, {6, 4, 5, 0, 6, 11, 0, 0}
+	{kColorPot, 4, 0, 1, 7, 6, 0, 0}, {kColorPot, 4, 1, 2, 8, 7, 0, 0}, {kColorPot, 4, 2, 3, 9, 8, 0, 0},
+	{kColorPot, 4, 3, 4, 10, 9, 0, 0}, {kColorPot, 4, 4, 5, 11, 10, 0, 0}, {kColorPot, 4, 5, 0, 6, 11, 0, 0}
 };
 static const int kPlantTopPotPts[6][3] = {
 	{10, 17, 40}, {20, 0, 40}, {10, -17, 40}, {-10, -17, 40}, {-20, 0, 40}, {-10, 17, 40}
 };
-static const int kPlantTopPotSurf[1][8] = {{0, 6, 5, 4, 3, 2, 1, 0}};
+static const int kPlantTopPotSurf[1][8] = {{kColorDkGray, 6, 5, 4, 3, 2, 1, 0}};
 
 static const int kPlantLeaf0Pts[3][3] = {{0, 0, 0}, {20, 20, 150}, {70, 70, 100}};
 static const int kPlantLeaf1Pts[3][3] = {{0, 0, 0}, {-20, 30, 100}, {-60, 50, 50}};
@@ -412,7 +453,7 @@ static const int kPlantLeaf3Pts[3][3] = {{0, 0, 0}, {20, -10, 50}, {90, -70, 70}
 static const int kPlantLeaf4Pts[3][3] = {{0, 0, 0}, {20, -30, 190}, {40, -60, 150}};
 static const int kPlantLeaf5Pts[3][3] = {{0, 0, 0}, {20, -10, 130}, {50, -80, 200}};
 
-static const int kPlantLeafSurf[2][8] = {{2, 3, 0, 1, 2, 0, 0, 0}, {2, 3, 2, 1, 0, 0, 0, 0}};
+static const int kPlantLeafSurf[2][8] = {{kColorPlant, 3, 0, 1, 2, 0, 0, 0}, {kColorPlant, 3, 2, 1, 0, 0, 0, 0}};
 
 static const Colony::ColonyEngine::PrismPartDef kPlantParts[8] = {
 	{12, kPlantPotPts, 6, kPlantPotSurf},
@@ -430,8 +471,8 @@ static const int kBox1Pts[8][3] = {
 	{-50, 50, 100}, {50, 50, 100}, {50, -50, 100}, {-50, -50, 100}
 };
 static const int kBox1Surf[5][8] = {
-	{6, 4, 0, 3, 7, 4, 0, 0}, {6, 4, 3, 2, 6, 7, 0, 0},
-	{6, 4, 1, 0, 4, 5, 0, 0}, {6, 4, 2, 1, 5, 6, 0, 0}, {6, 4, 7, 6, 5, 4, 0, 0}
+	{kColorBox, 4, 0, 3, 7, 4, 0, 0}, {kColorBox, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBox, 4, 1, 0, 4, 5, 0, 0}, {kColorBox, 4, 2, 1, 5, 6, 0, 0}, {kColorBox, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kBox2Pts[8][3] = {
 	{-50, 50, 100}, {50, 50, 100}, {50, -50, 100}, {-50, -50, 100},
@@ -443,17 +484,17 @@ static const int kReactorCorePts[12][3] = {
 	{32, 55, 168}, {64, 0, 168}, {32, -55, 168}, {-32, -55, 168}, {-64, 0, 168}, {-32, 55, 168}
 };
 static const int kReactorCoreSurf[7][8] = {
-	{0, 4, 0, 1, 7, 6, 0, 0}, {0, 4, 1, 2, 8, 7, 0, 0}, {0, 4, 2, 3, 9, 8, 0, 0},
-	{0, 4, 3, 4, 10, 9, 0, 0}, {0, 4, 4, 5, 11, 10, 0, 0}, {0, 4, 5, 0, 6, 11, 0, 0},
-	{0, 6, 5, 4, 3, 2, 1, 0}
+	{kColorReactor, 4, 0, 1, 7, 6, 0, 0}, {kColorReactor, 4, 1, 2, 8, 7, 0, 0}, {kColorReactor, 4, 2, 3, 9, 8, 0, 0},
+	{kColorReactor, 4, 3, 4, 10, 9, 0, 0}, {kColorReactor, 4, 4, 5, 11, 10, 0, 0}, {kColorReactor, 4, 5, 0, 6, 11, 0, 0},
+	{kColorReactor, 6, 5, 4, 3, 2, 1, 0}
 };
 static const int kReactorBasePts[8][3] = {
 	{-127, 127, 0}, {127, 127, 0}, {127, -127, 0}, {-127, -127, 0},
 	{-127, 127, 120}, {127, 127, 120}, {127, -127, 120}, {-127, -127, 120}
 };
 static const int kReactorBaseSurf[6][8] = {
-	{4, 4, 0, 3, 7, 4, 0, 0}, {4, 4, 3, 2, 6, 7, 0, 0}, {4, 4, 1, 0, 4, 5, 0, 0},
-	{4, 4, 2, 1, 5, 6, 0, 0}, {4, 4, 7, 6, 5, 4, 0, 0}, {4, 4, 0, 1, 2, 3, 0, 0}
+	{kColorRainbow1, 4, 0, 3, 7, 4, 0, 0}, {kColorRainbow1, 4, 3, 2, 6, 7, 0, 0}, {kColorRainbow1, 4, 1, 0, 4, 5, 0, 0},
+	{kColorRainbow1, 4, 2, 1, 5, 6, 0, 0}, {kColorRainbow1, 4, 7, 6, 5, 4, 0, 0}, {kColorRainbow1, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kReactorTopPts[8][3] = {
 	{-127, 127, 168}, {127, 127, 168}, {127, -127, 168}, {-127, -127, 168},
@@ -525,36 +566,16 @@ const uint8 *ColonyEngine::mapFeatureAt(int x, int y, int direction) const {
 	return _mapData[x][y][direction];
 }
 
-uint32 ColonyEngine::objectColor(int type) const {
-	switch (type) {
-	case 21: // DESK
-		return 220;
-	case 22: // PLANT
-		return 100;
-	case 24: // BED
-	case 42: // BBED
-		return 180;
-	case 29: // SCREEN
-	case 30: // CONSOLE
-		return 240;
-	case 31: // POWERSUIT
-	case 46: // REACTOR
-		return 255;
-	case 36: // TELEPORT
-		return 140;
-	default:
-		return 160 + ((uint32)(type * 7) & 0x3F);
-	}
-}
-
-void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, uint32 color) {
+void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook) {
 	// +32 compensates for the original sine table's 45° phase offset.
 	// Object angles from game data were stored assuming that offset.
 	const uint8 ang = (useLook ? obj.where.look : obj.where.ang) + 32;
 	const long rotCos = _cost[ang];
 	const long rotSin = _sint[ang];
- 
+	const bool lit = (_corePower[_coreIndex] > 0);
+
 	for (int i = 0; i < def.surfaceCount; i++) {
+		const int colorIdx = def.surfaces[i][0];
 		const int n = def.surfaces[i][1];
 		if (n < 2) continue;
 
@@ -574,7 +595,7 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 			// World relative rotation
 			long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
 			long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
-			
+
 			px[count] = (float)(rx + obj.where.xloc);
 			py[count] = (float)(ry + obj.where.yloc);
 			pz[count] = (float)(oz - 160); // Shift from floor-relative (z=0) to world (z=-160)
@@ -582,15 +603,10 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 		}
 
 		if (count >= 3) {
-			uint32 finalColor;
-			if (_corePower[_coreIndex] > 0) {
-				// Lit mode: use surface-specific colors defined in geometry
-				finalColor = (uint32)def.surfaces[i][0];
-			} else {
-				// Dark mode: everything is white wireframe.
-				finalColor = 15; // vINTWHITE
-			}
-			_gfx->draw3DPolygon(px, py, pz, count, finalColor);
+			// polyfill=False mode: wireframe fill stays as wall background,
+			// only outline color changes per surface (LINECOLOR from lsColor)
+			uint32 lineColor = lit ? (uint32)lookupLineColor(colorIdx) : 15;
+			_gfx->draw3DPolygon(px, py, pz, count, lineColor);
 		}
 	}
 }
@@ -600,8 +616,7 @@ void ColonyEngine::drawStaticObjects() {
 		const Thing &obj = _objects[i];
 		if (!obj.alive)
 			continue;
-		uint32 color = objectColor(obj.type);
-		drawStaticObjectPrisms3D(obj, color);
+		drawStaticObjectPrisms3D(obj);
 	}
 }
 
@@ -779,77 +794,75 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 
 	switch (map[0]) {
 	case kWallFeatureDoor: {
+		// DOS wireframe: all doors drawn with PenColor(realcolor[vDKGRAY]) = 8
+		const uint32 doorColor = 8; // vDKGRAY
 		bool shipLevel = (_level == 1 || _level == 5 || _level == 6);
-		
+
 		if (shipLevel) {
-			// Octagonal ship door (Weighted 7-point grid from source)
-			// Horizontal indices: 2, 1, 1, 2, 4, 5, 5, 4 (0.25..0.75 width)
-			// Vertical indices:   0, 1, 5, 6, 6, 5, 1, 0 (0.125..0.875 height)
+			// Octagonal ship door (SS door)
 			static const float u_ss[8] = { 0.375f, 0.250f, 0.250f, 0.375f, 0.625f, 0.750f, 0.750f, 0.625f };
 			static const float v_ss[8] = { 0.125f, 0.250f, 0.750f, 0.875f, 0.875f, 0.750f, 0.250f, 0.125f };
-			
+
 			if (map[1] == 0) {
-				wallPolygon(corners, u_ss, v_ss, 8, 0);
+				// Open: dark gray octagonal outline
+				for (int i = 0; i < 8; i++)
+					wallLine(corners, u_ss[i], v_ss[i], u_ss[(i + 1) % 8], v_ss[(i + 1) % 8], doorColor);
 			} else {
-				wallPolygon(corners, u_ss, v_ss, 8, 7);
+				// Closed: dark gray octagon + inner panel
 				for (int i = 0; i < 8; i++)
-					wallLine(corners, u_ss[i], v_ss[i], u_ss[(i + 1) % 8], v_ss[(i + 1) % 8], 0);
-				// Ship door inner panel
-				wallLine(corners, 0.375f, 0.25f, 0.375f, 0.75f, 0);
-				wallLine(corners, 0.625f, 0.25f, 0.625f, 0.75f, 0);
-				wallLine(corners, 0.375f, 0.25f, 0.625f, 0.25f, 0);
-				wallLine(corners, 0.375f, 0.75f, 0.625f, 0.75f, 0);
+					wallLine(corners, u_ss[i], v_ss[i], u_ss[(i + 1) % 8], v_ss[(i + 1) % 8], doorColor);
+				wallLine(corners, 0.375f, 0.25f, 0.375f, 0.75f, doorColor);
+				wallLine(corners, 0.625f, 0.25f, 0.625f, 0.75f, doorColor);
+				wallLine(corners, 0.375f, 0.25f, 0.625f, 0.25f, doorColor);
+				wallLine(corners, 0.375f, 0.75f, 0.625f, 0.75f, doorColor);
 			}
 		} else {
-			// Standard rectangular door (Lab levels) - Smaller look
+			// Standard rectangular door (Lab levels)
 			float xl = 0.25f, xr = 0.75f;
-			float yb = 0.125f, yt = 0.875f; // Suspended 1/8th from both ends
-			float f_u[4] = {xl, xr, xr, xl};
-			float f_v[4] = {yb, yb, yt, yt};
-			
+			float yb = 0.125f, yt = 0.875f;
+
 			if (map[1] == 0) {
-				wallPolygon(corners, f_u, f_v, 4, 0);
-				float leaf_u[4] = {xl, xl + 0.1f, xl + 0.1f, xl};
-				float leaf_v[4] = {yb, yb + 0.05f, yt - 0.05f, yt};
-				wallPolygon(corners, leaf_u, leaf_v, 4, 15);
+				// Open: dark gray door outline + floor perspective quad
+				wallLine(corners, xl, yb, xl, yt, doorColor);
+				wallLine(corners, xl, yt, xr, yt, doorColor);
+				wallLine(corners, xr, yt, xr, yb, doorColor);
+				wallLine(corners, xr, yb, xl, yb, doorColor);
 			} else {
-				uint32 fill = (_level >= 1 && _level <= 3) ? 12 : 7;
-				wallPolygon(corners, f_u, f_v, 4, fill);
-				wallLine(corners, xl, yb, xl, yt, 0);
-				wallLine(corners, xl, yt, xr, yt, 0);
-				wallLine(corners, xr, yt, xr, yb, 0);
-				wallLine(corners, xr, yb, xl, yb, 0);
-				// Diagonal Handle (Centered in the door)
-				wallLine(corners, xr - 0.15f, 0.45f, xr - 0.05f, 0.55f, 0);
+				// Closed: dark gray door outline + handle
+				wallLine(corners, xl, yb, xl, yt, doorColor);
+				wallLine(corners, xl, yt, xr, yt, doorColor);
+				wallLine(corners, xr, yt, xr, yb, doorColor);
+				wallLine(corners, xr, yb, xl, yb, doorColor);
+				// Handle
+				wallLine(corners, xr - 0.15f, 0.45f, xr - 0.05f, 0.55f, doorColor);
 			}
 		}
 		break;
 	}
 	case kWallFeatureWindow: {
-		// Window: Uses average midpoint shifts. 1/8th suspended.
+		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
+		const uint32 winColor = 8; // vDKGRAY
 		float xl = 0.3125f, xr = 0.6875f;
-		float yb = 0.3125f, yt = 0.6875f; 
-		float u[4] = {xl, xr, xr, xl};
-		float v[4] = {yb, yb, yt, yt};
-		wallPolygon(corners, u, v, 4, 7); 
-		wallLine(corners, xl, yb, xr, yb, 0);
-		wallLine(corners, xr, yb, xr, yt, 0);
-		wallLine(corners, xr, yt, xl, yt, 0);
-		wallLine(corners, xl, yt, xl, yb, 0);
-		wallLine(corners, 0.5f, yb, 0.5f, yt, 0);
-		wallLine(corners, xl, 0.5f, xr, 0.5f, 0);
+		float yb = 0.3125f, yt = 0.6875f;
+		wallLine(corners, xl, yb, xl, yt, winColor);
+		wallLine(corners, xl, yt, xr, yt, winColor);
+		wallLine(corners, xr, yt, xr, yb, winColor);
+		wallLine(corners, xr, yb, xl, yb, winColor);
+		wallLine(corners, xl, 0.5f, xr, 0.5f, winColor);
 		break;
 	}
 	case kWallFeatureShelves: {
+		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
+		const uint32 shelfColor = 8; // vDKGRAY
 		float xl = 0.15f, xr = 0.85f;
 		float yb = 0.1f, yt = 0.9f;
-		wallLine(corners, xl, yb, xr, yb, 0);
-		wallLine(corners, xr, yb, xr, yt, 0);
-		wallLine(corners, xr, yt, xl, yt, 0);
-		wallLine(corners, xl, yt, xl, yb, 0);
+		wallLine(corners, xl, yb, xr, yb, shelfColor);
+		wallLine(corners, xr, yb, xr, yt, shelfColor);
+		wallLine(corners, xr, yt, xl, yt, shelfColor);
+		wallLine(corners, xl, yt, xl, yb, shelfColor);
 		for (int i = 1; i <= 6; i++) {
 			float t = yb + (yt - yb) * (float)i / 7.0f;
-			wallLine(corners, xl, t, xr, t, 0);
+			wallLine(corners, xl, t, xr, t, shelfColor);
 		}
 		break;
 	}
@@ -881,20 +894,24 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		wallChar(corners, map[1]);
 		break;
 	case kWallFeatureGlyph: {
+		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
+		const uint32 glyphColor = 8; // vDKGRAY
 		for (int i = 0; i < 7; i++) {
 			float v = 0.2f + i * 0.1f;
-			wallLine(corners, 0.2f, v, 0.8f, v, 0);
+			wallLine(corners, 0.2f, v, 0.8f, v, glyphColor);
 		}
 		break;
 	}
 	case kWallFeatureElevator: {
+		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
+		const uint32 elevColor = 8; // vDKGRAY
 		float xl = 0.2f, xr = 0.8f;
 		float yb = 0.1f, yt = 0.9f;
-		wallLine(corners, xl, yb, xl, yt, 0);
-		wallLine(corners, xl, yt, xr, yt, 0);
-		wallLine(corners, xr, yt, xr, yb, 0);
-		wallLine(corners, xr, yb, xl, yb, 0);
-		wallLine(corners, 0.5f, yb, 0.5f, yt, 0);
+		wallLine(corners, xl, yb, xl, yt, elevColor);
+		wallLine(corners, xl, yt, xr, yt, elevColor);
+		wallLine(corners, xr, yt, xr, yb, elevColor);
+		wallLine(corners, xr, yb, xl, yb, elevColor);
+		wallLine(corners, 0.5f, yb, 0.5f, yt, elevColor);
 		break;
 	}
 	case kWallFeatureTunnel: {
@@ -905,6 +922,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	}
 	case kWallFeatureAirlock: {
+		// DOS wireframe: open=vBLACK(0), closed=vDKGRAY(8)
 		float pts[][2] = {{0.0f, 0.5f}, {0.15f, 0.85f}, {0.5f, 1.0f}, {0.85f, 0.85f},
 		                  {1.0f, 0.5f}, {0.85f, 0.15f}, {0.5f, 0.0f}, {0.15f, 0.15f}};
 		float u[8], v[8];
@@ -913,15 +931,17 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 			v[i] = 0.1f + pts[i][1] * 0.8f;
 		}
 		if (map[1] == 0) {
+			// Open: black fill (passable opening)
 			wallPolygon(corners, u, v, 8, 0);
 		} else {
-			wallPolygon(corners, u, v, 8, 7);
+			// Closed: dark gray wireframe outline + cross
+			const uint32 airlockColor = 8; // vDKGRAY
 			for (int i = 0; i < 8; i++) {
 				int n = (i + 1) % 8;
-				wallLine(corners, u[i], v[i], u[n], v[n], 0);
+				wallLine(corners, u[i], v[i], u[n], v[n], airlockColor);
 			}
-			wallLine(corners, 0.1f, 0.5f, 0.9f, 0.5f, 0);
-			wallLine(corners, 0.5f, 0.1f, 0.5f, 0.9f, 0);
+			wallLine(corners, 0.1f, 0.5f, 0.9f, 0.5f, airlockColor);
+			wallLine(corners, 0.5f, 0.1f, 0.5f, 0.9f, airlockColor);
 		}
 		break;
 	}
@@ -1009,102 +1029,96 @@ void ColonyEngine::renderCorridor3D() {
 	
 	drawWallFeatures3D();
 	drawStaticObjects();
-		
+
 	_gfx->end3D();
 	_gfx->setWireframe(oldWire);
 }
 
-bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj, uint32 baseColor) {
-	auto tint = [](uint32 base, int delta) -> uint32 {
-		return (uint32)CLIP<int>((int)base + delta, 0, 255);
-	};
-
+bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 	switch (obj.type) {
 	case kObjConsole:
-		draw3DPrism(obj, kConsolePart, false, tint(baseColor, 0));
+		draw3DPrism(obj, kConsolePart, false);
 		break;
 	case kObjCChair:
 		for (int i = 0; i < 5; i++)
-			draw3DPrism(obj, kCChairParts[i], false, tint(baseColor, 0));
+			draw3DPrism(obj, kCChairParts[i], false);
 		break;
 	case kObjPlant:
 		for (int i = 0; i < 8; i++)
-			draw3DPrism(obj, kPlantParts[i], false, tint(baseColor, 0));
+			draw3DPrism(obj, kPlantParts[i], false);
 		break;
 	case kObjCouch:
 	case kObjChair: {
 		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
 		for (int i = 0; i < 4; i++)
-			draw3DPrism(obj, parts[i], false, tint(baseColor, 0));
+			draw3DPrism(obj, parts[i], false);
 		break;
 	}
 	case kObjTV:
-		draw3DPrism(obj, kTVParts[0], false, tint(baseColor, 0));
-		draw3DPrism(obj, kTVParts[1], false, tint(baseColor, 35));
+		for (int i = 0; i < 2; i++)
+			draw3DPrism(obj, kTVParts[i], false);
 		break;
 	case kObjDrawer:
-		draw3DPrism(obj, kDrawerParts[0], false, tint(baseColor, 0));
-		draw3DPrism(obj, kDrawerParts[1], false, tint(baseColor, 30));
+		for (int i = 0; i < 2; i++)
+			draw3DPrism(obj, kDrawerParts[i], false);
 		break;
 	case kObjFWall:
 	case kObjCWall:
-		draw3DPrism(obj, kCWallParts[0], false, tint(baseColor, -5));
+		draw3DPrism(obj, kCWallParts[0], false);
 		break;
 	case kObjScreen:
-		draw3DPrism(obj, kScreenPart, false, tint(baseColor, 0));
+		draw3DPrism(obj, kScreenPart, false);
 		break;
 	case kObjTable:
-		draw3DPrism(obj, kTableParts[0], false, tint(baseColor, 20));
-		draw3DPrism(obj, kTableParts[1], false, tint(baseColor, -10));
+		for (int i = 0; i < 2; i++)
+			draw3DPrism(obj, kTableParts[i], false);
 		break;
 	case kObjBed:
 	case kObjBBed: {
 		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
-		draw3DPrism(obj, parts[0], false, tint(baseColor, 15));
-		draw3DPrism(obj, parts[1], false, tint(baseColor, -10));
-		draw3DPrism(obj, parts[2], false, tint(baseColor, 5));
+		for (int i = 0; i < 3; i++)
+			draw3DPrism(obj, parts[i], false);
 		break;
 	}
 	case kObjDesk:
 		for (int i = 0; i < 10; i++)
-			draw3DPrism(obj, kDeskParts[i], false, tint(baseColor, 0));
+			draw3DPrism(obj, kDeskParts[i], false);
 		break;
 	case kObjBox1:
-		draw3DPrism(obj, kBox1Part, false, tint(baseColor, 0));
+		draw3DPrism(obj, kBox1Part, false);
 		break;
 	case kObjBox2:
-		draw3DPrism(obj, kBox2Parts[0], false, tint(baseColor, 0));
-		draw3DPrism(obj, kBox2Parts[1], false, tint(baseColor, 0));
+		for (int i = 0; i < 2; i++)
+			draw3DPrism(obj, kBox2Parts[i], false);
 		break;
 	case kObjReactor:
 		for (int i = 0; i < 3; i++)
-			draw3DPrism(obj, kReactorParts[i], false, tint(baseColor, 0));
+			draw3DPrism(obj, kReactorParts[i], false);
 		break;
 	case kObjPowerSuit:
-		draw3DPrism(obj, kConsolePart, false, tint(baseColor, 0)); // Placeholder
+		draw3DPrism(obj, kConsolePart, false); // Placeholder
 		break;
 	case kObjTeleport:
-		draw3DPrism(obj, kCWallParts[0], false, tint(baseColor, 50)); // Placeholder
+		draw3DPrism(obj, kCWallParts[0], false); // Placeholder
 		break;
 	case kObjTub:
 		for (int i = 0; i < 2; i++)
-			draw3DPrism(obj, kTubParts[i], false, tint(baseColor, 0));
+			draw3DPrism(obj, kTubParts[i], false);
 		break;
 	case kObjSink:
 		for (int i = 0; i < 3; i++)
-			draw3DPrism(obj, kSinkParts[i], false, tint(baseColor, 0));
+			draw3DPrism(obj, kSinkParts[i], false);
 		break;
 	case kObjToilet:
 		for (int i = 0; i < 4; i++)
-			draw3DPrism(obj, kToiletParts[i], false, tint(baseColor, 0));
+			draw3DPrism(obj, kToiletParts[i], false);
 		break;
 	case kObjPToilet:
 		for (int i = 0; i < 5; i++)
-			draw3DPrism(obj, kPToiletParts[i], false, tint(baseColor, 0));
+			draw3DPrism(obj, kPToiletParts[i], false);
 		break;
-
 	case kObjForkLift:
-		draw3DPrism(obj, kBox1Part, false, tint(baseColor, 0)); // Placeholder
+		draw3DPrism(obj, kBox1Part, false); // Placeholder
 		break;
 	default:
 		return false;


Commit: ddded293ca65f900d6e25f12d0b8589bef3a0d9b
    https://github.com/scummvm/scummvm/commit/ddded293ca65f900d6e25f12d0b8589bef3a0d9b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:17+02:00

Commit Message:
COLONY: allow movement between levels

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index fdda2ce611e..a49f52b7857 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -311,7 +311,7 @@ private:
 	void interactWithObject(int objNum);
 	bool setDoorState(int x, int y, int direction, int state);
 	int openAdjacentDoors(int x, int y);
-	bool tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject);
+	int tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject);
 	void updateViewportLayout();
 	void drawDashboardStep1();
 	void drawCrosshair();
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 8e600258bda..b762fb59e3d 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -867,27 +867,111 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	}
 	case kWallFeatureUpStairs: {
-		float xl = 0.15f, xr = 0.85f;
-		for (int i = 0; i < 6; i++) {
-			float u = xl + (xr - xl) * (float)i / 6.0f;
-			float u2 = xl + (xr - xl) * (float)(i + 1) / 6.0f;
-			float v = 0.1f + 0.8f * (float)i / 6.0f;
-			float v2 = 0.1f + 0.8f * (float)(i + 1) / 6.0f;
-			wallLine(corners, u, v, u2, v2, 0);
+		// DOS: draw_up_stairs — staircase ascending into the wall with perspective
+		// Uses split7 subdivision (7 depth levels at fractions 1/8..7/8)
+		// Passage narrows toward vanishing point (0.5, 0.5)
+		const uint32 col = 0; // vBLACK
+
+		// Perspective convergence: back of passage at ~1/3 width (1 cell deep)
+		float ul[7], ur[7], vf[7], vc[7];
+		for (int i = 0; i < 7; i++) {
+			float f = (i + 1) / 8.0f;
+			float inset = f * (1.0f / 3.0f);
+			ul[i] = inset;
+			ur[i] = 1.0f - inset;
+			vf[i] = inset;        // floor rises toward center
+			vc[i] = 1.0f - inset; // ceiling drops toward center
+		}
+		// Step height: at depth d, step s is at fraction (s+1)/8 from floor to ceiling
+		auto vh = [&](int d, int s) -> float {
+			return vf[d] + (s + 1) / 8.0f * (vc[d] - vf[d]);
+		};
+		// Back of passage (full depth)
+		float bi = 1.0f / 3.0f; // back inset
+		float bu = bi, bur = 1.0f - bi, bvc = 1.0f - bi;
+
+		// 1. Side wall verticals at back of passage
+		wallLine(corners, bu, bvc, bu, 0.5f, col);
+		wallLine(corners, bur, 0.5f, bur, bvc, col);
+
+		// 2. Back wall landing (depth 6 to full depth)
+		wallLine(corners, ul[6], vh(6, 6), bu, bvc, col);
+		wallLine(corners, bu, bvc, bur, bvc, col);
+		wallLine(corners, bur, bvc, ur[6], vh(6, 6), col);
+		wallLine(corners, ur[6], vh(6, 6), ul[6], vh(6, 6), col);
+
+		// 3. First step tread (floor from wall face to depth 0)
+		wallLine(corners, 0.0f, 0.0f, ul[0], vf[0], col);
+		wallLine(corners, ul[0], vf[0], ur[0], vf[0], col);
+		wallLine(corners, ur[0], vf[0], 1.0f, 0.0f, col);
+		wallLine(corners, 1.0f, 0.0f, 0.0f, 0.0f, col);
+
+		// 4. First step riser (at depth 0)
+		wallLine(corners, ul[0], vh(0, 0), ul[0], vf[0], col);
+		wallLine(corners, ur[0], vf[0], ur[0], vh(0, 0), col);
+		wallLine(corners, ur[0], vh(0, 0), ul[0], vh(0, 0), col);
+
+		// 5. Step treads (i=3..0: depth i to depth i+1)
+		for (int i = 3; i >= 0; i--) {
+			wallLine(corners, ul[i], vh(i, i), ul[i + 1], vh(i + 1, i), col);
+			wallLine(corners, ul[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i), col);
+			wallLine(corners, ur[i + 1], vh(i + 1, i), ur[i], vh(i, i), col);
+			wallLine(corners, ur[i], vh(i, i), ul[i], vh(i, i), col);
+		}
+
+		// 6. Step risers (i=5..0: vertical face at depth i+1)
+		for (int i = 5; i >= 0; i--) {
+			wallLine(corners, ul[i + 1], vh(i + 1, i + 1), ul[i + 1], vh(i + 1, i), col);
+			wallLine(corners, ul[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i), col);
+			wallLine(corners, ur[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i + 1), col);
+			wallLine(corners, ur[i + 1], vh(i + 1, i + 1), ul[i + 1], vh(i + 1, i + 1), col);
 		}
-		wallLine(corners, xl, 0.1f, xr, 0.9f, 0);
+
+		// 7. Handrails: from center of wall edges up to near-ceiling at mid-depth
+		wallLine(corners, 0.0f, 0.5f, ul[3], vc[0], col);
+		wallLine(corners, 1.0f, 0.5f, ur[3], vc[0], col);
 		break;
 	}
 	case kWallFeatureDnStairs: {
-		float xl = 0.15f, xr = 0.85f;
-		for (int i = 0; i < 6; i++) {
-			float u = xl + (xr - xl) * (float)i / 6.0f;
-			float u2 = xl + (xr - xl) * (float)(i + 1) / 6.0f;
-			float v = 0.9f - 0.8f * (float)i / 6.0f;
-			float v2 = 0.9f - 0.8f * (float)(i + 1) / 6.0f;
-			wallLine(corners, u, v, u2, v2, 0);
+		// DOS: draw_dn_stairs — staircase descending into the wall with perspective
+		// Simpler than up stairs: ceiling slopes down, side walls, first step, handrails
+		const uint32 col = 0; // vBLACK
+
+		float ul[7], ur[7], vf[7], vc[7];
+		for (int i = 0; i < 7; i++) {
+			float f = (i + 1) / 8.0f;
+			float inset = f * (1.0f / 3.0f);
+			ul[i] = inset;
+			ur[i] = 1.0f - inset;
+			vf[i] = inset;
+			vc[i] = 1.0f - inset;
 		}
-		wallLine(corners, xl, 0.9f, xr, 0.1f, 0);
+		float bi = 1.0f / 3.0f;
+		float bu = bi, bur = 1.0f - bi;
+
+		// 1. Ceiling: front ceiling slopes down to mid-depth
+		wallLine(corners, 0.0f, 1.0f, ul[3], vc[3], col);
+		wallLine(corners, ul[3], vc[3], ur[3], vc[3], col);
+		wallLine(corners, ur[3], vc[3], 1.0f, 1.0f, col);
+
+		// 2. Slant: from mid-depth ceiling down to center at back
+		wallLine(corners, ul[3], vc[3], bu, 0.5f, col);
+		wallLine(corners, bu, 0.5f, bur, 0.5f, col);
+		wallLine(corners, bur, 0.5f, ur[3], vc[3], col);
+
+		// 3. Side wall verticals: from center at back down to floor level
+		wallLine(corners, bu, 0.5f, bu, vf[0], col);
+		wallLine(corners, bur, 0.5f, bur, vf[0], col);
+
+		// 4. First step (floor from wall face to depth 0)
+		wallLine(corners, 0.0f, 0.0f, ul[0], vf[0], col);
+		wallLine(corners, ul[0], vf[0], ur[0], vf[0], col);
+		wallLine(corners, ur[0], vf[0], 1.0f, 0.0f, col);
+		wallLine(corners, 1.0f, 0.0f, 0.0f, 0.0f, col);
+
+		// 5. Handrails: from center of wall edges down to floor at mid-depth
+		wallLine(corners, 0.0f, 0.5f, ul[3], vf[3], col);
+		wallLine(corners, 1.0f, 0.5f, ur[3], vf[3], col);
 		break;
 	}
 	case kWallFeatureChar:
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index c09f77f120e..819d565e092 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -303,6 +303,16 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		return 0;
 	};
 
+	// tryPassThroughFeature returns: 0=blocked, 1=pass through, 2=teleported (position already set)
+	auto tryFeature = [&](int dir) -> int {
+		int r = tryPassThroughFeature(pobject->xindex, pobject->yindex, dir, pobject);
+		if (r == 2)
+			return 0; // teleported — position already updated by the feature
+		if (r == 1)
+			return moveTo();
+		return -2; // blocked, caller handles
+	};
+
 	if (xind2 == pobject->xindex) {
 		if (yind2 == pobject->yindex) {
 			pobject->dx = xnew - pobject->xloc;
@@ -315,8 +325,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		if (yind2 > pobject->yindex) {
 			if (!(_wall[pobject->xindex][yind2] & 1))
 				return moveTo();
-			if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirNorth, pobject))
-				return moveTo();
+			{ int r = tryFeature(kDirNorth); if (r != -2) return r; }
 			debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
 			_sound->play(Sound::kBang);
 			return -1;
@@ -325,8 +334,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 1))
 			return moveTo();
-		if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirSouth, pobject))
-			return moveTo();
+		{ int r = tryFeature(kDirSouth); if (r != -2) return r; }
 		debug("Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
 		_sound->play(Sound::kBang);
 		return -1;
@@ -337,8 +345,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		if (xind2 > pobject->xindex) {
 			if (!(_wall[xind2][pobject->yindex] & 2))
 				return moveTo();
-			if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirEast, pobject))
-				return moveTo();
+			{ int r = tryFeature(kDirEast); if (r != -2) return r; }
 			debug("Collision East at x=%d y=%d", xind2, pobject->yindex);
 			_sound->play(Sound::kBang);
 			return -1;
@@ -347,8 +354,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 2))
 			return moveTo();
-		if (tryPassThroughFeature(pobject->xindex, pobject->yindex, kDirWest, pobject))
-			return moveTo();
+		{ int r = tryFeature(kDirWest); if (r != -2) return r; }
 		debug("Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
 		_sound->play(Sound::kBang);
 		return -1;
@@ -444,38 +450,136 @@ int ColonyEngine::openAdjacentDoors(int x, int y) {
 	return opened;
 }
 
-bool ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject) {
+// Returns: 0 = blocked, 1 = can pass through normally, 2 = teleported (position already set)
+int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject) {
 	const uint8 *map = mapFeatureAt(fromX, fromY, direction);
 	if (!map || map[0] == kWallFeatureNone)
-		return false;
+		return 0;
 
 	switch (map[0]) {
 	case kWallFeatureDoor:
 		if (map[1] == 0)
-			return true;
+			return 1;
 		if (pobject != &_me)
-			return false;
+			return 0;
 		if (_hasKeycard || _unlocked) {
 			setDoorState(fromX, fromY, direction, 0);
-			return true;
+			return 1;
 		}
-		doText(75, 0); // "Door is locked" or similar from T.DAT
-		return false;
+		doText(75, 0);
+		return 0;
 	case kWallFeatureAirlock:
 		if (map[1] == 0)
-			return true;
+			return 1;
 		if (pobject == &_me) {
 			if (_unlocked) {
 				_sound->play(Sound::kAirlock);
 				setDoorState(fromX, fromY, direction, 0);
-				return true;
+				return 1;
 			}
-
 			inform("AIRLOCK IS SEALED.", true);
 		}
-		return false;
+		return 0;
+
+	case kWallFeatureUpStairs:
+	case kWallFeatureDnStairs:
+	case kWallFeatureTunnel: {
+		if (pobject != &_me)
+			return 0; // robots don't use stairs/tunnels
+
+		// DOS GoTo(): mapdata[2]=level, [3]=xcell, [4]=ycell
+		// Must copy values BEFORE loadMap, which overwrites _mapData
+		const int targetMap = map[2];
+		const int targetX = map[3];
+		const int targetY = map[4];
+
+		if (targetMap == 0 && targetX == 0 && targetY == 0)
+			return 1; // no destination data — just pass through
+
+		// Special cases from DOS
+		if (targetMap == 100) {
+			doText(78, 0); // Dimension error
+			return 0;
+		}
+
+		// Play appropriate sound
+		if (map[0] == kWallFeatureDnStairs)
+			_sound->play(Sound::kClatter);
+
+		// Move player to target position (preserve sub-cell offset like DOS)
+		if (targetX > 0 && targetX < 31 && targetY > 0 && targetY < 31) {
+			const int xmod = pobject->xloc - (pobject->xindex << 8);
+			const int ymod = pobject->yloc - (pobject->yindex << 8);
+			_robotArray[pobject->xindex][pobject->yindex] = 0;
+			pobject->xloc = (targetX << 8) + xmod;
+			pobject->xindex = targetX;
+			pobject->yloc = (targetY << 8) + ymod;
+			pobject->yindex = targetY;
+		}
+
+		// Load new map if target level specified
+		if (targetMap > 0 && targetMap != _level) {
+			loadMap(targetMap);
+			// DOS: coreindex depends on level
+			_coreIndex = (targetMap == 1) ? 0 : 1;
+		}
+
+		if (pobject->xindex >= 0 && pobject->xindex < 32 &&
+		    pobject->yindex >= 0 && pobject->yindex < 32)
+			_robotArray[pobject->xindex][pobject->yindex] = MENUM;
+
+		debug("Level change via %s: level=%d pos=(%d,%d)",
+		      map[0] == kWallFeatureUpStairs ? "upstairs" :
+		      map[0] == kWallFeatureDnStairs ? "downstairs" : "tunnel",
+		      _level, pobject->xindex, pobject->yindex);
+		return 2; // teleported
+	}
+
+	case kWallFeatureElevator: {
+		if (pobject != &_me)
+			return 0;
+		if (_corePower[1] == 0) {
+			inform("ELEVATOR HAS NO POWER.", true);
+			return 0;
+		}
+
+		// The elevator is an interactive animation in DOS (the "elev" animation).
+		// For now, directly use the map data destination if available.
+		const int targetMap = map[2];
+		const int targetX = map[3];
+		const int targetY = map[4];
+
+		if (targetMap == 0 && targetX == 0 && targetY == 0)
+			return 1; // no destination — just pass through
+
+		_sound->play(Sound::kElevator);
+
+		if (targetX > 0 && targetX < 31 && targetY > 0 && targetY < 31) {
+			_robotArray[pobject->xindex][pobject->yindex] = 0;
+			pobject->xloc = (targetX << 8) + 128;
+			pobject->xindex = targetX;
+			pobject->yloc = (targetY << 8) + 128;
+			pobject->yindex = targetY;
+			// DOS: player turns around when exiting elevator
+			pobject->ang += 128;
+			pobject->look = pobject->ang;
+		}
+
+		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)
+			_robotArray[pobject->xindex][pobject->yindex] = MENUM;
+
+		debug("Elevator: level=%d pos=(%d,%d)", _level, pobject->xindex, pobject->yindex);
+		return 2; // teleported
+	}
+
 	default:
-		return false;
+		return 0;
 	}
 }
 


Commit: b9e2b05eefbca0c339e827b88f1531c397b24265
    https://github.com/scummvm/scummvm/commit/b9e2b05eefbca0c339e827b88f1531c397b24265
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:17+02:00

Commit Message:
COLONY: compute visible cells

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index a49f52b7857..a25b28dbf43 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -287,6 +287,8 @@ private:
 
 	uint8 wallAt(int x, int y) const;
 	const uint8 *mapFeatureAt(int x, int y, int direction) const;
+	bool _visibleCell[32][32];
+	void computeVisibleCells();
 	void drawStaticObjects();
 
 public:
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index b762fb59e3d..2a375f75f2b 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -611,11 +611,76 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 	}
 }
 
+void ColonyEngine::computeVisibleCells() {
+	memset(_visibleCell, 0, sizeof(_visibleCell));
+
+	int px = _me.xindex;
+	int py = _me.yindex;
+	if (px < 0 || px >= 32 || py < 0 || py >= 32)
+		return;
+
+	// BFS from player cell, stopping at walls.
+	// Doors are on walls, so they act as opaque barriers.
+	_visibleCell[px][py] = true;
+	int queueX[1024], queueY[1024];
+	int head = 0, tail = 0;
+	queueX[tail] = px;
+	queueY[tail] = py;
+	tail++;
+
+	while (head < tail) {
+		int cx = queueX[head];
+		int cy = queueY[head];
+		head++;
+
+		// North: (cx, cy+1) — wall check: _wall[cx][cy+1] & 0x01
+		if (cy + 1 < 32 && !_visibleCell[cx][cy + 1]) {
+			if (!(wallAt(cx, cy + 1) & 0x01)) {
+				_visibleCell[cx][cy + 1] = true;
+				queueX[tail] = cx;
+				queueY[tail] = cy + 1;
+				tail++;
+			}
+		}
+		// South: (cx, cy-1) — wall check: _wall[cx][cy] & 0x01
+		if (cy - 1 >= 0 && !_visibleCell[cx][cy - 1]) {
+			if (!(wallAt(cx, cy) & 0x01)) {
+				_visibleCell[cx][cy - 1] = true;
+				queueX[tail] = cx;
+				queueY[tail] = cy - 1;
+				tail++;
+			}
+		}
+		// East: (cx+1, cy) — wall check: _wall[cx+1][cy] & 0x02
+		if (cx + 1 < 32 && !_visibleCell[cx + 1][cy]) {
+			if (!(wallAt(cx + 1, cy) & 0x02)) {
+				_visibleCell[cx + 1][cy] = true;
+				queueX[tail] = cx + 1;
+				queueY[tail] = cy;
+				tail++;
+			}
+		}
+		// West: (cx-1, cy) — wall check: _wall[cx][cy] & 0x02
+		if (cx - 1 >= 0 && !_visibleCell[cx - 1][cy]) {
+			if (!(wallAt(cx, cy) & 0x02)) {
+				_visibleCell[cx - 1][cy] = true;
+				queueX[tail] = cx - 1;
+				queueY[tail] = cy;
+				tail++;
+			}
+		}
+	}
+}
+
 void ColonyEngine::drawStaticObjects() {
 	for (uint i = 0; i < _objects.size(); i++) {
 		const Thing &obj = _objects[i];
 		if (!obj.alive)
 			continue;
+		int ox = obj.where.xindex;
+		int oy = obj.where.yindex;
+		if (ox < 0 || ox >= 32 || oy < 0 || oy >= 32 || !_visibleCell[ox][oy])
+			continue;
 		drawStaticObjectPrisms3D(obj);
 	}
 }
@@ -1050,6 +1115,8 @@ void ColonyEngine::drawWallFeatures3D() {
 
 	for (int y = 0; y < 31; y++) {
 		for (int x = 0; x < 31; x++) {
+			if (!_visibleCell[x][y])
+				continue;
 			drawCellFeature3D(x, y);
 			for (int dir = 0; dir < 4; dir++) {
 				const uint8 *map = mapFeatureAt(x, y, dir);
@@ -1063,10 +1130,12 @@ void ColonyEngine::drawWallFeatures3D() {
 
 
 void ColonyEngine::renderCorridor3D() {
+	computeVisibleCells();
+
 	bool lit = (_corePower[_coreIndex] > 0);
 	bool oldWire = _wireframe;
 
-	// Authentic look: Always wireframe for walls. 
+	// Authentic look: Always wireframe for walls.
 	// Power ON = White background (fill), Black lines.
 	// Power OFF = Black background (fill), White lines.
 	_gfx->setWireframe(true, lit ? 7 : 0);
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 819d565e092..f854d86b906 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -287,6 +287,43 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 	const int xind2 = xnew >> 8;
 	const int yind2 = ynew >> 8;
 
+	// Clamp position to maintain minimum distance from walls within a cell.
+	// This prevents the camera from clipping through thin walls.
+	// Skip clamping for walls that have a passable feature (open door, stairs, etc.)
+	static const int kWallPad = 40;
+	auto isPassable = [this](int cx, int cy, int dir) -> bool {
+		if (cx < 0 || cx >= 31 || cy < 0 || cy >= 31)
+			return false;
+		uint8 type = _mapData[cx][cy][dir][0];
+		if (type == kWallFeatureDoor || type == kWallFeatureAirlock)
+			return _mapData[cx][cy][dir][1] == 0; // open
+		if (type == kWallFeatureUpStairs || type == kWallFeatureDnStairs ||
+			type == kWallFeatureTunnel || type == kWallFeatureElevator)
+			return true;
+		return false;
+	};
+	auto clampToWalls = [this, &isPassable](Locate *p) {
+		int cx = p->xindex;
+		int cy = p->yindex;
+		int cellMinX = cx << 8;
+		int cellMinY = cy << 8;
+		int cellMaxX = cellMinX + 255;
+		int cellMaxY = cellMinY + 255;
+
+		// South wall of this cell (at cellMinY boundary)
+		if ((wallAt(cx, cy) & 0x01) && !isPassable(cx, cy, kDirSouth))
+			p->yloc = MAX(p->yloc, cellMinY + kWallPad);
+		// North wall (at cellMaxY+1 boundary = south wall of cell above)
+		if ((wallAt(cx, cy + 1) & 0x01) && !isPassable(cx, cy, kDirNorth))
+			p->yloc = MIN(p->yloc, cellMaxY - kWallPad);
+		// West wall of this cell (at cellMinX boundary)
+		if ((wallAt(cx, cy) & 0x02) && !isPassable(cx, cy, kDirWest))
+			p->xloc = MAX(p->xloc, cellMinX + kWallPad);
+		// East wall (at cellMaxX+1 boundary = west wall of cell to right)
+		if ((wallAt(cx + 1, cy) & 0x02) && !isPassable(cx, cy, kDirEast))
+			p->xloc = MIN(p->xloc, cellMaxX - kWallPad);
+	};
+
 	auto occupied = [&]() -> int {
 		return occupiedObjectAt(xind2, yind2, pobject);
 	};
@@ -300,6 +337,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		pobject->dy = ynew - pobject->yloc;
 		pobject->xloc = xnew;
 		pobject->yloc = ynew;
+		clampToWalls(pobject);
 		return 0;
 	};
 
@@ -319,6 +357,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 			pobject->dy = ynew - pobject->yloc;
 			pobject->xloc = xnew;
 			pobject->yloc = ynew;
+			clampToWalls(pobject);
 			return 0;
 		}
 


Commit: cbe8e2aacc961aa6f984da3f51f7f4399bcd73c3
    https://github.com/scummvm/scummvm/commit/cbe8e2aacc961aa6f984da3f51f7f4399bcd73c3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:18+02:00

Commit Message:
COLONY: implement door and airlock open/close animation handlers

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 25bb44331de..8f43bd23722 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -88,6 +88,9 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_backgroundActive = false;
 	_divideBG = 0;
 	_animationRunning = false;
+	_animationResult = 0;
+	_doorOpen = false;
+	_elevatorFloor = 0;
 
 	for (int i = 0; i < 4; i++) {
 		_decode1[i] = _decode2[i] = _decode3[i] = 0;
@@ -547,6 +550,9 @@ void ColonyEngine::playAnimation() {
 				if (item > 0) {
 					handleAnimationClick(item);
 				}
+			} else if (event.type == Common::EVENT_RBUTTONDOWN) {
+				// DOS: right-click exits animation (AnimControl returns FALSE on button-up)
+				_animationRunning = false;
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
 				debug(5, "Animation Mouse: %d, %d", event.mouse.x, event.mouse.y);
 			} else if (event.type == Common::EVENT_KEYDOWN) {
@@ -943,6 +949,118 @@ void ColonyEngine::handleAnimationClick(int item) {
 				_gfx->copyToScreen();
 			}
 		}
+	} else if (_animationName == "door" || _animationName == "bulkhead") {
+		// DOS DoDoor: item==3 toggles door open/close, item==1 or (item==101 && door open) exits
+		if (item == 3) {
+			_sound->play(Sound::kDoor);
+			if (_doorOpen) {
+				for (int i = 3; i >= 1; i--) {
+					_doorOpen = !_doorOpen;
+					SetObjectState(2, i);
+					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+				}
+			} else {
+				for (int i = 1; i < 4; i++) {
+					_doorOpen = !_doorOpen;
+					SetObjectState(2, i);
+					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+				}
+			}
+		}
+		if (item == 1 || (item == 101 && ObjectState(2) == 3)) {
+			_animationResult = 1;
+			_animationRunning = false;
+		}
+	} else if (_animationName == "airlock") {
+		// DOS DoAirLock: item==1 toggles airlock if power on && unlocked
+		// item==2 or (item==101 && airlock open) exits with pass-through
+		if ((item == 2 || item == 101) && _doorOpen) {
+			_animationResult = 1;
+			_animationRunning = false;
+		} else if (item == 1 && _corePower[_coreIndex] && _unlocked) {
+			_sound->play(Sound::kAirlock);
+			if (_doorOpen) {
+				for (int i = 3; i >= 1; i--) {
+					SetObjectState(2, i);
+					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+				}
+			} else {
+				for (int i = 1; i < 4; i++) {
+					SetObjectState(2, i);
+					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+				}
+			}
+			_doorOpen = !_doorOpen;
+		} else if (item == 101 && !_doorOpen) {
+			// Exit without opening
+			_animationRunning = false;
+		}
+	} else if (_animationName == "elev") {
+		// DOS DoElevator: two phases
+		// _doorOpen=false: Phase 1 (outside) - item==5 toggles doors
+		// _doorOpen=true: Phase 2 (inside) - items 6-10 select floor
+		// _animationResult tracks: 0=outside, 1=doors open, 2=inside
+		if (_animationResult < 2) {
+			// Phase 1: outside the elevator
+			if (item == 5) {
+				_sound->play(Sound::kElevator);
+				if (!_doorOpen) {
+					for (int i = 1; i < 4; i++) {
+						SetObjectState(3, i);
+						SetObjectState(4, i);
+						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+					}
+					_doorOpen = true;
+				} else {
+					for (int i = 3; i >= 1; i--) {
+						SetObjectState(4, i);
+						SetObjectState(3, i);
+						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+					}
+					_doorOpen = false;
+				}
+			} else if (item == 2 || (item == 101 && _doorOpen)) {
+				// Enter the elevator (transition to phase 2)
+				_animationResult = 2;
+				SetObjectOnOff(6, true);
+				SetObjectOnOff(7, true);
+				SetObjectOnOff(8, true);
+				SetObjectOnOff(9, true);
+				SetObjectOnOff(10, true);
+				SetObjectOnOff(2, false);
+				SetObjectOnOff(5, false);
+				drawAnimation(); _gfx->copyToScreen();
+			} else if (item == 101 && !_doorOpen) {
+				// Exit without entering
+				_animationResult = 0;
+				_animationRunning = false;
+			}
+		} else {
+			// Phase 2: inside — floor selection
+			if (item >= 6 && item <= 10) {
+				int fl = item - 5;
+				if (fl == _elevatorFloor) {
+					SetObjectState(item, 1); // already on this floor
+				} else {
+					_sound->play(Sound::kElevator);
+					for (int i = 3; i >= 1; i--) {
+						SetObjectState(4, i);
+						SetObjectState(3, i);
+						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+					}
+					_elevatorFloor = fl;
+					for (int i = 1; i <= 3; i++) {
+						SetObjectState(4, i);
+						SetObjectState(3, i);
+						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+					}
+					SetObjectState(item, 1);
+				}
+			} else if (item == 1 || item == 101) {
+				// Exit elevator
+				_animationRunning = false;
+			}
+		}
 	} else if (_animationName == "controls") {
 		switch (item) {
 		case 4: // Accelerator
@@ -1080,6 +1198,13 @@ void ColonyEngine::SetObjectState(int num, int state) {
 		_lSprites[num]->current = state - 1;
 }
 
+int ColonyEngine::ObjectState(int num) const {
+	num--;
+	if (num >= 0 && num < (int)_lSprites.size())
+		return _lSprites[num]->current + 1;
+	return 0;
+}
+
 void ColonyEngine::SetObjectOnOff(int num, bool on) {
 	num--;
 	if (num >= 0 && num < (int)_lSprites.size())
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index a25b28dbf43..033021f32ba 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -341,6 +341,9 @@ private:
 	int16 _divideBG;
 	Common::String _animationName;
 	bool _animationRunning;
+	int _animationResult;
+	bool _doorOpen;
+	int _elevatorFloor;
 
 	bool loadAnimation(const Common::String &name);
 	void deleteAnimation();
@@ -356,6 +359,7 @@ private:
 	void handleAnimationClick(int item);
 	void dolSprite(int index);
 	void SetObjectState(int num, int state);
+	int ObjectState(int num) const;
 	void SetObjectOnOff(int num, bool on);
 	void refreshAnimationDisplay();
 	void crypt(uint8 sarray[6], int i, int j, int k, int l);
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index f854d86b906..defb22e9da4 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -289,20 +289,20 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 
 	// Clamp position to maintain minimum distance from walls within a cell.
 	// This prevents the camera from clipping through thin walls.
-	// Skip clamping for walls that have a passable feature (open door, stairs, etc.)
+	// Skip clamping for walls that have any interactive feature (door, airlock,
+	// stairs, etc.) so the player can reach the boundary and trigger the interaction.
+	// The wall itself still blocks via checkwall; clamping only prevents camera clipping
+	// on plain walls with no features.
 	static const int kWallPad = 40;
-	auto isPassable = [this](int cx, int cy, int dir) -> bool {
+	auto hasFeature = [this](int cx, int cy, int dir) -> bool {
 		if (cx < 0 || cx >= 31 || cy < 0 || cy >= 31)
 			return false;
 		uint8 type = _mapData[cx][cy][dir][0];
-		if (type == kWallFeatureDoor || type == kWallFeatureAirlock)
-			return _mapData[cx][cy][dir][1] == 0; // open
-		if (type == kWallFeatureUpStairs || type == kWallFeatureDnStairs ||
-			type == kWallFeatureTunnel || type == kWallFeatureElevator)
-			return true;
-		return false;
+		return type == kWallFeatureDoor || type == kWallFeatureAirlock ||
+			type == kWallFeatureUpStairs || type == kWallFeatureDnStairs ||
+			type == kWallFeatureTunnel || type == kWallFeatureElevator;
 	};
-	auto clampToWalls = [this, &isPassable](Locate *p) {
+	auto clampToWalls = [this, &hasFeature](Locate *p) {
 		int cx = p->xindex;
 		int cy = p->yindex;
 		int cellMinX = cx << 8;
@@ -311,16 +311,16 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		int cellMaxY = cellMinY + 255;
 
 		// South wall of this cell (at cellMinY boundary)
-		if ((wallAt(cx, cy) & 0x01) && !isPassable(cx, cy, kDirSouth))
+		if ((wallAt(cx, cy) & 0x01) && !hasFeature(cx, cy, kDirSouth))
 			p->yloc = MAX(p->yloc, cellMinY + kWallPad);
 		// North wall (at cellMaxY+1 boundary = south wall of cell above)
-		if ((wallAt(cx, cy + 1) & 0x01) && !isPassable(cx, cy, kDirNorth))
+		if ((wallAt(cx, cy + 1) & 0x01) && !hasFeature(cx, cy, kDirNorth))
 			p->yloc = MIN(p->yloc, cellMaxY - kWallPad);
 		// West wall of this cell (at cellMinX boundary)
-		if ((wallAt(cx, cy) & 0x02) && !isPassable(cx, cy, kDirWest))
+		if ((wallAt(cx, cy) & 0x02) && !hasFeature(cx, cy, kDirWest))
 			p->xloc = MAX(p->xloc, cellMinX + kWallPad);
 		// East wall (at cellMaxX+1 boundary = west wall of cell to right)
-		if ((wallAt(cx + 1, cy) & 0x02) && !isPassable(cx, cy, kDirEast))
+		if ((wallAt(cx + 1, cy) & 0x02) && !hasFeature(cx, cy, kDirEast))
 			p->xloc = MIN(p->xloc, cellMaxX - kWallPad);
 	};
 
@@ -498,27 +498,46 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 	switch (map[0]) {
 	case kWallFeatureDoor:
 		if (map[1] == 0)
-			return 1;
+			return 1; // already open — pass through
 		if (pobject != &_me)
-			return 0;
-		if (_hasKeycard || _unlocked) {
-			setDoorState(fromX, fromY, direction, 0);
-			return 1;
+			return 0; // robots can't open doors
+		// DOS DoDoor: play door animation, player clicks handle to open
+		if (_corePower[_coreIndex] == 0)
+			return 0; // no power
+		{
+			const char *animName = (_level == 1 || _level == 5 || _level == 6) ? "bulkhead" : "door";
+			if (!loadAnimation(animName))
+				return 0;
+			_animationResult = 0;
+			_doorOpen = false;
+			SetObjectState(2, 1); // door starts closed
+			playAnimation();
+			if (_animationResult) {
+				setDoorState(fromX, fromY, direction, 0);
+				return 1; // pass through
+			}
+			return 0; // player didn't open the door
 		}
-		doText(75, 0);
-		return 0;
 	case kWallFeatureAirlock:
 		if (map[1] == 0)
-			return 1;
-		if (pobject == &_me) {
-			if (_unlocked) {
-				_sound->play(Sound::kAirlock);
+			return 1; // already open — pass through
+		if (pobject != &_me)
+			return 0;
+		// DOS DoAirLock: play airlock animation
+		{
+			if (!loadAnimation("airlock"))
+				return 0;
+			_animationResult = 0;
+			_doorOpen = false;
+			SetObjectState(2, 1); // airlock starts closed
+			SetObjectState(1, 1);
+			playAnimation();
+			if (_animationResult) {
 				setDoorState(fromX, fromY, direction, 0);
 				return 1;
 			}
-			inform("AIRLOCK IS SEALED.", true);
+			return 0;
 		}
-		return 0;
 
 	case kWallFeatureUpStairs:
 	case kWallFeatureDnStairs:
@@ -582,39 +601,54 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 			return 0;
 		}
 
-		// The elevator is an interactive animation in DOS (the "elev" animation).
-		// For now, directly use the map data destination if available.
-		const int targetMap = map[2];
-		const int targetX = map[3];
-		const int targetY = map[4];
+		// DOS DoElevator: play elevator animation with floor selection
+		if (!loadAnimation("elev"))
+			return 0;
+		_animationResult = 0;
+		_doorOpen = false;
+		_elevatorFloor = _level - 1; // DOS: fl = level-1
+		SetObjectOnOff(6, false);
+		SetObjectOnOff(7, false);
+		SetObjectOnOff(8, false);
+		SetObjectOnOff(9, false);
+		SetObjectOnOff(10, false);
+		playAnimation();
+
+		bool entered = (_animationResult >= 2);
+		if (entered && _elevatorFloor + 1 != _level) {
+			// Player selected a different floor
+			int targetMap = _elevatorFloor + 1;
+			const int targetX = map[3];
+			const int targetY = map[4];
+
+			if (targetX > 0 && targetX < 31 && targetY > 0 && targetY < 31) {
+				_robotArray[pobject->xindex][pobject->yindex] = 0;
+				pobject->xloc = (targetX << 8) + 128;
+				pobject->xindex = targetX;
+				pobject->yloc = (targetY << 8) + 128;
+				pobject->yindex = targetY;
+				pobject->ang += 128;
+				pobject->look = pobject->ang;
+			}
 
-		if (targetMap == 0 && targetX == 0 && targetY == 0)
-			return 1; // no destination — just pass through
+			if (targetMap > 0 && targetMap != _level) {
+				loadMap(targetMap);
+				_coreIndex = (targetMap == 1) ? 0 : 1;
+			}
 
-		_sound->play(Sound::kElevator);
+			if (pobject->xindex >= 0 && pobject->xindex < 32 &&
+			    pobject->yindex >= 0 && pobject->yindex < 32)
+				_robotArray[pobject->xindex][pobject->yindex] = MENUM;
 
-		if (targetX > 0 && targetX < 31 && targetY > 0 && targetY < 31) {
-			_robotArray[pobject->xindex][pobject->yindex] = 0;
-			pobject->xloc = (targetX << 8) + 128;
-			pobject->xindex = targetX;
-			pobject->yloc = (targetY << 8) + 128;
-			pobject->yindex = targetY;
-			// DOS: player turns around when exiting elevator
+			debug("Elevator: level=%d pos=(%d,%d)", _level, pobject->xindex, pobject->yindex);
+			return 2; // teleported
+		}
+		// Player entered but stayed on same floor — turn around
+		if (entered) {
 			pobject->ang += 128;
 			pobject->look = pobject->ang;
 		}
-
-		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)
-			_robotArray[pobject->xindex][pobject->yindex] = MENUM;
-
-		debug("Elevator: level=%d pos=(%d,%d)", _level, pobject->xindex, pobject->yindex);
-		return 2; // teleported
+		return 0;
 	}
 
 	default:


Commit: 9dbf80331136d8072d36d35f92177b955e986c40
    https://github.com/scummvm/scummvm/commit/9dbf80331136d8072d36d35f92177b955e986c40
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:18+02:00

Commit Message:
COLONY: implement vanity/mirror animation with suit and inventory display

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 8f43bd23722..c611227bd91 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -484,7 +484,7 @@ void ColonyEngine::playAnimation() {
 		} else {
 			uint8 *decode = (_action0 == 11) ? _decode2 : _decode3;
 			for (int i = 0; i < 4; i++) {
-				if (decode[i] == (_action0 == 11 ? _decode2[i] : _decode3[i])) // This check is weird in original but effectively sets state
+				if (decode[i])
 					SetObjectState(i + 2, decode[i]);
 				else
 					SetObjectState(i + 2, 1);
@@ -536,6 +536,37 @@ void ColonyEngine::playAnimation() {
 			SetObjectOnOff(9, false);
 			break;
 		}
+	} else if (_animationName == "vanity") {
+		// DOS DoVanity: set suit state on mirror display (object 1)
+		if (_weapons && _armor)
+			SetObjectState(1, 3);
+		else if (_weapons)
+			SetObjectState(1, 2);
+		else if (_armor)
+			SetObjectState(1, 1);
+		else
+			SetObjectState(1, 4);
+		// Badge only visible on level 1
+		if (_level != 1)
+			SetObjectOnOff(14, false);
+		// Hide items based on action0 (num parameter in DOS)
+		if (_action0 < 90) { // coffee cup only
+			SetObjectOnOff(4, false);
+			SetObjectOnOff(7, false);
+			SetObjectOnOff(13, false);
+		} else if (_action0 < 100) { // paper
+			SetObjectOnOff(12, false);
+			SetObjectOnOff(4, false);
+			SetObjectOnOff(7, false);
+		} else if (_action0 < 110) { // diary
+			SetObjectOnOff(12, false);
+			SetObjectOnOff(13, false);
+			SetObjectOnOff(7, false);
+		} else if (_action0 < 120) { // book
+			SetObjectOnOff(12, false);
+			SetObjectOnOff(13, false);
+			SetObjectOnOff(4, false);
+		}
 	}
 
 	while (_animationRunning && !shouldQuit()) {
@@ -818,7 +849,17 @@ void ColonyEngine::handleAnimationClick(int item) {
 			doText(_action1, 0);
 		}
 	} else if (_animationName == "vanity") {
-		if (item == 13) { // Paper
+		if (item == 12) { // Coffee cup - spill animation
+			if (!_doorOpen) { // reuse _doorOpen as "spilled" flag
+				for (int i = 1; i < 6; i++) {
+					SetObjectState(12, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(50);
+				}
+				_doorOpen = true;
+			}
+		} else if (item == 13) { // Paper
 			doText(_action0, 0);
 		} else if (item == 14) { // Badge
 			doText(80, 0);
@@ -1143,21 +1184,45 @@ void ColonyEngine::dolSprite(int index) {
 		return;
 
 	ComplexSprite *ls = _lSprites[index];
+	int maxFrames = (int)ls->objects.size();
+
 	switch (ls->type) {
 	case 1: // Key and control
 		if (ls->frozen) {
-			// Control logic
+			// Container: can open or close if not locked, or if slightly open
 			if (!ls->locked || ls->current) {
-				if (ls->current > 0) {
+				if (ls->current > 1) {
+					// Close: animate from current down to 0
 					while (ls->current > 0) {
 						ls->current--;
+						// Sync linked sprites via key/lock
+						if (ls->key) {
+							for (int i = 0; i < (int)_lSprites.size(); i++) {
+								if (i != index && _lSprites[i]->lock == ls->key) {
+									_lSprites[i]->current = ls->current;
+									if (_lSprites[i]->current >= (int)_lSprites[i]->objects.size())
+										_lSprites[i]->current = (int)_lSprites[i]->objects.size() - 1;
+								}
+							}
+						}
 						drawAnimation();
 						_gfx->copyToScreen();
 						_system->delayMillis(50);
 					}
 				} else {
-					while (ls->current < (int)ls->objects.size() - 1) {
+					// Open: animate from current up to max
+					while (ls->current < maxFrames - 1) {
 						ls->current++;
+						// Sync linked sprites via key/lock
+						if (ls->key) {
+							for (int i = 0; i < (int)_lSprites.size(); i++) {
+								if (i != index && _lSprites[i]->lock == ls->key) {
+									_lSprites[i]->current = ls->current;
+									if (_lSprites[i]->current >= (int)_lSprites[i]->objects.size())
+										_lSprites[i]->current = (int)_lSprites[i]->objects.size() - 1;
+								}
+							}
+						}
 						drawAnimation();
 						_gfx->copyToScreen();
 						_system->delayMillis(50);
@@ -1169,6 +1234,7 @@ void ColonyEngine::dolSprite(int index) {
 	case 2: // Container and object
 		if (ls->frozen) {
 			if (!ls->locked) {
+				// Unlocked container: toggle open/close
 				if (ls->current > 0) {
 					while (ls->current > 0) {
 						ls->current--;
@@ -1177,7 +1243,24 @@ void ColonyEngine::dolSprite(int index) {
 						_system->delayMillis(50);
 					}
 				} else {
-					while (ls->current < (int)ls->objects.size() - 1) {
+					while (ls->current < maxFrames - 1) {
+						ls->current++;
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				}
+			} else {
+				// Locked container: current>1 closes, current==1 opens further
+				if (ls->current > 1) {
+					while (ls->current > 0) {
+						ls->current--;
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				} else if (ls->current == 1) {
+					while (ls->current < maxFrames - 1) {
 						ls->current++;
 						drawAnimation();
 						_gfx->copyToScreen();


Commit: 5e1d8a4727eb6246f311009e7063164cdfb18c1b
    https://github.com/scummvm/scummvm/commit/5e1d8a4727eb6246f311009e7063164cdfb18c1b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:18+02:00

Commit Message:
COLONY: add pixel-perfect sprite hit-testing for animation clicks

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c611227bd91..1125d02d729 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -763,26 +763,45 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 
 	for (int i = _lSprites.size() - 1; i >= 0; i--) {
 		ComplexSprite *ls = _lSprites[i];
-		if (ls->onoff) {
-			int cnum = ls->current;
-			if (cnum < 0 || cnum >= (int)ls->objects.size()) continue;
-
-			int spriteIdx = ls->objects[cnum].spritenum;
-			if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size()) continue;
-
-			Sprite *s = _cSprites[spriteIdx];
-			int xloc = ls->xloc + ls->objects[cnum].xloc;
-			int yloc = ls->yloc + ls->objects[cnum].yloc;
-
-			Common::Rect r = s->clip;
-			r.translate(xloc, yloc);
-
-			if (r.contains(pt)) {
-				debug(1, "Sprite %d hit. Frame %d, Base Sprite %d. Box: (%d, %d, %d, %d)", i + 1,
-					cnum, spriteIdx, r.left, r.top, r.right, r.bottom);
-				return i + 1;
+		if (!ls->onoff) continue;
+
+		int cnum = ls->current;
+		if (cnum < 0 || cnum >= (int)ls->objects.size()) continue;
+
+		int spriteIdx = ls->objects[cnum].spritenum;
+		if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size()) continue;
+
+		Sprite *s = _cSprites[spriteIdx];
+		int xloc = ls->xloc + ls->objects[cnum].xloc;
+		int yloc = ls->yloc + ls->objects[cnum].yloc;
+
+		Common::Rect r = s->clip;
+		r.translate(xloc, yloc);
+
+		if (!r.contains(pt)) continue;
+
+		// Pixel-perfect mask test (matches DOS WhichlSprite)
+		Image *mask = s->mask;
+		if (mask && mask->data) {
+			int row = pt.y - r.top;
+			int col = pt.x - r.left;
+			int bitCol = (col + mask->align) * mask->bits;
+			int maskIndex = row * mask->rowBytes + (bitCol / 8);
+			int shift = bitCol % 8;
+
+			if (maskIndex >= 0 && maskIndex < mask->rowBytes * mask->height) {
+				byte maskByte = mask->data[maskIndex];
+				if (mask->planes == 2)
+					maskByte |= mask->data[mask->rowBytes * mask->height + maskIndex];
+				maskByte = maskByte >> shift;
+				if (!(maskByte & ((1 << mask->bits) - 1)))
+					continue; // Transparent pixel, skip this sprite
 			}
 		}
+
+		debug(1, "Sprite %d hit. Frame %d, Base Sprite %d. Box: (%d, %d, %d, %d)", i + 1,
+			cnum, spriteIdx, r.left, r.top, r.right, r.bottom);
+		return i + 1;
 	}
 
 	// Dump accurately calculated bounds if debug is high enough


Commit: 375a7fcb1c619d90ffd858605339b7af5adf15f4
    https://github.com/scummvm/scummvm/commit/375a7fcb1c619d90ffd858605339b7af5adf15f4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:19+02:00

Commit Message:
COLONY: implement drag-to-move object interaction with link groups

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 1125d02d729..116c800f27d 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -537,6 +537,12 @@ void ColonyEngine::playAnimation() {
 			break;
 		}
 	} else if (_animationName == "vanity") {
+		debug(0, "Vanity init: action0=%d action1=%d level=%d weapons=%d armor=%d", _action0, _action1, _level, _weapons, _armor);
+		for (int i = 0; i < (int)_lSprites.size(); i++) {
+			ComplexSprite *ls = _lSprites[i];
+			debug(0, "  Vanity sprite %d: type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d frames=%d",
+				i + 1, ls->type, ls->frozen, ls->locked, ls->current, (int)ls->onoff, ls->key, ls->lock, (int)ls->objects.size());
+		}
 		// DOS DoVanity: set suit state on mirror display (object 1)
 		if (_weapons && _armor)
 			SetObjectState(1, 3);
@@ -794,13 +800,20 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 				if (mask->planes == 2)
 					maskByte |= mask->data[mask->rowBytes * mask->height + maskIndex];
 				maskByte = maskByte >> shift;
-				if (!(maskByte & ((1 << mask->bits) - 1)))
+				if (!(maskByte & ((1 << mask->bits) - 1))) {
+					debug(0, "  Sprite %d (type=%d frz=%d): bbox hit but mask transparent at row=%d col=%d bits=%d align=%d",
+						i + 1, ls->type, ls->frozen, row, col, mask->bits, mask->align);
 					continue; // Transparent pixel, skip this sprite
+				}
+			} else {
+				debug(0, "  Sprite %d: mask index %d out of bounds (max %d)", i + 1, maskIndex, mask->rowBytes * mask->height);
 			}
+		} else {
+			debug(0, "  Sprite %d: no mask data, using bbox", i + 1);
 		}
 
-		debug(1, "Sprite %d hit. Frame %d, Base Sprite %d. Box: (%d, %d, %d, %d)", i + 1,
-			cnum, spriteIdx, r.left, r.top, r.right, r.bottom);
+		debug(0, "Sprite %d HIT. type=%d frozen=%d Frame %d, Sprite %d. Box: (%d,%d,%d,%d)",
+			i + 1, ls->type, ls->frozen, cnum, spriteIdx, r.left, r.top, r.right, r.bottom);
 		return i + 1;
 	}
 
@@ -868,6 +881,16 @@ void ColonyEngine::handleAnimationClick(int item) {
 			doText(_action1, 0);
 		}
 	} else if (_animationName == "vanity") {
+		debug(0, "Vanity item %d clicked. Sprite type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d size=%d",
+			item,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->type : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->frozen : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->locked : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->current : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? (int)_lSprites[item-1]->onoff : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->key : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->lock : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? (int)_lSprites[item-1]->objects.size() : -1);
 		if (item == 12) { // Coffee cup - spill animation
 			if (!_doorOpen) { // reuse _doorOpen as "spilled" flag
 				for (int i = 1; i < 6; i++) {
@@ -886,6 +909,8 @@ void ColonyEngine::handleAnimationClick(int item) {
 			doText(_action0, 0);
 		} else if (item == 7) { // Book
 			doText(_action0, 0);
+		} else {
+			debug(0, "Vanity: unhandled item %d", item);
 		}
 	} else if (_animationName == "slides") {
 		if (item == 2) { // Speaker
@@ -1198,6 +1223,83 @@ void ColonyEngine::terminateGame(bool blowup) {
 	_system->quit();
 }
 
+void ColonyEngine::moveObject(int index) {
+	if (index < 0 || index >= (int)_lSprites.size())
+		return;
+
+	ComplexSprite *ls = _lSprites[index];
+
+	// Build link group
+	Common::Array<int> linked;
+	if (ls->link) {
+		for (int i = 0; i < (int)_lSprites.size(); i++)
+			if (_lSprites[i]->link == ls->link)
+				linked.push_back(i);
+	} else {
+		linked.push_back(index);
+	}
+
+	// Get initial mouse position and animation origin
+	Common::Point old = _system->getEventManager()->getMousePos();
+	int ox = _screenR.left + (_screenR.width() - 416) / 2;
+	ox = (ox / 8) * 8;
+	int oy = _screenR.top + (_screenR.height() - 264) / 2;
+
+	// Drag loop: track mouse while left button held.
+	// NOTE: The original DOS hides dragged sprites during drag (SetObjectOnOff FALSE)
+	// and redraws them separately on top. We improve on this by keeping them visible
+	// throughout, and drawing an extra copy on top so they render above drawers.
+	while (!shouldQuit()) {
+		Common::Event event;
+		bool buttonDown = true;
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_LBUTTONUP) {
+				buttonDown = false;
+				break;
+			}
+		}
+		if (!buttonDown)
+			break;
+
+		Common::Point cur = _system->getEventManager()->getMousePos();
+		int dx = cur.x - old.x;
+		int dy = cur.y - old.y;
+
+		if (dx != 0 || dy != 0) {
+			// Cycle frame for non-type-2 sprites
+			if (ls->type != 2 && (int)ls->objects.size() > 1) {
+				ls->current++;
+				if (ls->current >= (int)ls->objects.size())
+					ls->current = 0;
+			}
+
+			// Move all linked sprites
+			for (uint i = 0; i < linked.size(); i++) {
+				_lSprites[linked[i]]->xloc += dx;
+				_lSprites[linked[i]]->yloc += dy;
+			}
+
+			old = cur;
+		}
+
+		// Draw all sprites normally, then draw dragged sprites again on top
+		// so they appear above drawers and other overlapping sprites
+		drawAnimation();
+		for (uint i = 0; i < linked.size(); i++)
+			drawComplexSprite(linked[i], ox, oy);
+		_gfx->copyToScreen();
+
+		_system->delayMillis(20);
+	}
+
+	// Reset frame for non-type-2
+	if (ls->type != 2)
+		ls->current = 0;
+
+	drawAnimation();
+	_gfx->copyToScreen();
+}
+
 void ColonyEngine::dolSprite(int index) {
 	if (index < 0 || index >= (int)_lSprites.size())
 		return;
@@ -1206,6 +1308,10 @@ void ColonyEngine::dolSprite(int index) {
 	int maxFrames = (int)ls->objects.size();
 
 	switch (ls->type) {
+	case 0: // Display
+		if (!ls->frozen)
+			moveObject(index);
+		break;
 	case 1: // Key and control
 		if (ls->frozen) {
 			// Container: can open or close if not locked, or if slightly open
@@ -1248,6 +1354,8 @@ void ColonyEngine::dolSprite(int index) {
 					}
 				}
 			}
+		} else {
+			moveObject(index);
 		}
 		break;
 	case 2: // Container and object
@@ -1287,6 +1395,8 @@ void ColonyEngine::dolSprite(int index) {
 					}
 				}
 			}
+		} else {
+			moveObject(index);
 		}
 		break;
 	default:
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 033021f32ba..a47beceb7ea 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -358,6 +358,7 @@ private:
 	int whichSprite(const Common::Point &p);
 	void handleAnimationClick(int item);
 	void dolSprite(int index);
+	void moveObject(int index);
 	void SetObjectState(int num, int state);
 	int ObjectState(int num) const;
 	void SetObjectOnOff(int num, bool on);
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index b9f5d2c4e84..b8ffb834c66 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -205,8 +205,8 @@ void OpenGLRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY
 	// regardless of viewport dimensions (e.g. when dashboard narrows it).
 	float aspectRatio = (float)viewport.width() / (float)viewport.height();
 	float fov = 75.0f; // vertical FOV in degrees
-	float nearClip = 10.0f;
-	float farClip = 20000.0f;
+	float nearClip = 1.0f;
+	float farClip = 10000.0f;
 	float ymax = nearClip * tanf(fov * M_PI / 360.0f);
 	float xmax = ymax * aspectRatio;
 	glFrustum(-xmax, xmax, -ymax, ymax, nearClip, farClip);


Commit: 5f4ab536cff90660799bf83b13e7bc24c0dc9311
    https://github.com/scummvm/scummvm/commit/5f4ab536cff90660799bf83b13e7bc24c0dc9311
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:19+02:00

Commit Message:
COLONY: add DOS movement speed/rotation controls

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 116c800f27d..20f8e9b9ec5 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -59,6 +59,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_weapons = 0;
 	_wireframe = false;
 	_widescreen = ConfMan.getBool("widescreen_mod");
+	_speedShift = 2; // DOS default: speedshift=1, but 2 feels better with our frame rate
 	
 	memset(_wall, 0, sizeof(_wall));
 	memset(_mapData, 0, sizeof(_mapData));
@@ -232,8 +233,7 @@ Common::Error ColonyEngine::run() {
 		pal[i * 3 + 2] = i;
 	}
 	_gfx->setPalette(pal, 0, 256);
-	_gfx->setWireframe(_wireframe);
-	
+
 	scrollInfo();
 
 	loadMap(1); // Try to load the first map
@@ -248,44 +248,98 @@ Common::Error ColonyEngine::run() {
 				if (event.type == Common::EVENT_KEYDOWN) {
 					debug("Key down: %d", event.kbd.keycode);
 					const bool allowInteraction = (event.kbd.flags & Common::KBD_CTRL) == 0;
+					// DOS movement: xai[ang] << speedshift, where xai[i] = cost[i] >> 4
+					const int moveX = (_cost[_me.look] * (1 << _speedShift)) >> 4;
+					const int moveY = (_sint[_me.look] * (1 << _speedShift)) >> 4;
+					const int rotSpeed = 1 << (_speedShift - 1); // DOS: speed = 1 << (speedshift-1)
 					switch (event.kbd.keycode) {
+					// Move forward (DOS: w / 8 / up)
 					case Common::KEYCODE_UP:
 					case Common::KEYCODE_w:
-					{
-						int xnew = _me.xloc + (_cost[_me.look] >> 2);
-						int ynew = _me.yloc + (_sint[_me.look] >> 2);
-						cCommand(xnew, ynew, allowInteraction);
+						cCommand(_me.xloc + moveX, _me.yloc + moveY, allowInteraction);
 						break;
-					}
+					// Move backward (DOS: s / 5 / down)
 					case Common::KEYCODE_DOWN:
 					case Common::KEYCODE_s:
-					{
-						int xnew = _me.xloc - (_cost[_me.look] >> 2);
-						int ynew = _me.yloc - (_sint[_me.look] >> 2);
-						cCommand(xnew, ynew, allowInteraction);
+						cCommand(_me.xloc - moveX, _me.yloc - moveY, allowInteraction);
 						break;
-					}
+					// Strafe left (DOS: a / 4 / left)
 					case Common::KEYCODE_LEFT:
 					case Common::KEYCODE_a:
 					{
 						uint8 strafeAngle = (uint8)((int)_me.look + 64);
-						int xnew = _me.xloc + (_cost[strafeAngle] >> 2);
-						int ynew = _me.yloc + (_sint[strafeAngle] >> 2);
-						cCommand(xnew, ynew, allowInteraction);
+						int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
+						int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+						cCommand(_me.xloc + sx, _me.yloc + sy, allowInteraction);
 						break;
 					}
+					// Strafe right (DOS: d / 6 / right)
 					case Common::KEYCODE_RIGHT:
 					case Common::KEYCODE_d:
 					{
 						uint8 strafeAngle = (uint8)((int)_me.look - 64);
-						int xnew = _me.xloc + (_cost[strafeAngle] >> 2);
-						int ynew = _me.yloc + (_sint[strafeAngle] >> 2);
-						cCommand(xnew, ynew, allowInteraction);
+						int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
+						int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+						cCommand(_me.xloc + sx, _me.yloc + sy, allowInteraction);
 						break;
 					}
+					// Rotate left (DOS: q / 7)
+					case Common::KEYCODE_q:
+						_me.ang += rotSpeed;
+						_me.look += rotSpeed;
+						break;
+					// Rotate right (DOS: e / 9)
+					case Common::KEYCODE_e:
+						_me.ang -= rotSpeed;
+						_me.look -= rotSpeed;
+						break;
+					// Look left (DOS: z / 1)
+					case Common::KEYCODE_z:
+						_me.look = _me.ang + 64;
+						break;
+					// Look right (DOS: c / 3)
+					case Common::KEYCODE_c:
+						_me.look = _me.ang - 64;
+						break;
+					// Look behind (DOS: x / 2)
+					case Common::KEYCODE_x:
+						_me.look = _me.ang + 128;
+						break;
+					// Speed 1-5 (DOS: keys 1-5)
+					case Common::KEYCODE_1:
+					case Common::KEYCODE_2:
+					case Common::KEYCODE_3:
+					case Common::KEYCODE_4:
+					case Common::KEYCODE_5:
+						_speedShift = event.kbd.keycode - Common::KEYCODE_1 + 1;
+						debug("Speed: %d", _speedShift);
+						break;
+					// F7: toggle dashboard (DOS: doFunctionKey case 7)
 					case Common::KEYCODE_F7:
+						_showDashBoard = !_showDashBoard;
+						break;
+					// F8: toggle polyfill (DOS: doFunctionKey case 8)
+					case Common::KEYCODE_F8:
 						_wireframe = !_wireframe;
-						_gfx->setWireframe(_wireframe);
+						debug("Polyfill: %s", _wireframe ? "off (wireframe)" : "on (filled)");
+						break;
+					// F10: ScummVM menu (replaces DOS F10 pause)
+					case Common::KEYCODE_F10:
+						_system->lockMouse(false);
+						openMainMenuDialog();
+						_gfx->computeScreenViewport();
+						_system->lockMouse(true);
+						_system->warpMouse(_centerX, _centerY);
+						_system->getEventManager()->purgeMouseEvents();
+						break;
+					// Escape: also opens ScummVM menu
+					case Common::KEYCODE_ESCAPE:
+						_system->lockMouse(false);
+						openMainMenuDialog();
+						_gfx->computeScreenViewport();
+						_system->lockMouse(true);
+						_system->warpMouse(_centerX, _centerY);
+						_system->getEventManager()->purgeMouseEvents();
 						break;
 					default:
 						break;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index a47beceb7ea..20353210589 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -253,6 +253,7 @@ private:
 	int _weapons;
 	bool _wireframe;
 	bool _widescreen;
+	int _speedShift; // 1-5, movement speed = 1 << (_speedShift - 1)
 
 	Common::RandomSource _randomSource;
 	uint8 _decode1[4];
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 2a375f75f2b..b1249eca256 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -780,8 +780,11 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 			u[i] = 0.2f + (data[1 + i*2] / 6.0f) * 0.6f;
 			v[i] = 0.2f + (data[2 + i*2] / 6.0f) * 0.6f;
 		}
-		// Characters are usually drawn in black or level-specific color. Color 0 is safe.
-		wallPolygon(corners, u, v, count, 0); 
+		// Draw arrow as outline only (wireframe mode)
+		for (int i = 0; i < count; i++) {
+			int n = (i + 1) % count;
+			wallLine(corners, u[i], v[i], u[n], v[n], 0); // vBLACK
+		}
 	}
 }
 
@@ -815,7 +818,7 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 	{
 		float u[4] = {0.375f, 0.625f, 0.625f, 0.375f};
 		float v[4] = {0.375f, 0.375f, 0.625f, 0.625f};
-		wallPolygon(corners, u, v, 4, 7); // LTGRAY
+		wallPolygon(corners, u, v, 4, 8); // vDKGRAY outline
 		break;
 	}
 	case 2: // LGHOLEFLR
@@ -823,14 +826,14 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 	{
 		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-		wallPolygon(corners, u, v, 4, 7); // LTGRAY
+		wallPolygon(corners, u, v, 4, 8); // vDKGRAY outline
 		break;
 	}
 	case 5: // HOTFOOT
 	{
 		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-		wallPolygon(corners, u, v, 4, 4); // RED
+		wallPolygon(corners, u, v, 4, 4); // vRED outline
 		break;
 	}
 	default:
@@ -1067,7 +1070,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)
 		static const float u_t[6] = { 0.0f,    0.0f,    1/6.0f,  5/6.0f,  1.0f,    1.0f };
 		static const float v_t[6] = { 0.0f,    0.750f,  0.875f,  0.875f,  0.750f,  0.0f };
-		wallPolygon(corners, u_t, v_t, 6, 0); // Black tunnel fill
+		wallPolygon(corners, u_t, v_t, 6, 0); // vBLACK outline
 		break;
 	}
 	case kWallFeatureAirlock: {
@@ -1081,7 +1084,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		}
 		if (map[1] == 0) {
 			// Open: black fill (passable opening)
-			wallPolygon(corners, u, v, 8, 0);
+			wallPolygon(corners, u, v, 8, 0); // vBLACK outline
 		} else {
 			// Closed: dark gray wireframe outline + cross
 			const uint32 airlockColor = 8; // vDKGRAY
@@ -1133,18 +1136,15 @@ void ColonyEngine::renderCorridor3D() {
 	computeVisibleCells();
 
 	bool lit = (_corePower[_coreIndex] > 0);
-	bool oldWire = _wireframe;
 
-	// Authentic look: Always wireframe for walls.
-	// Power ON = White background (fill), Black lines.
-	// Power OFF = Black background (fill), White lines.
+	// Walls always use wireframe with fill (opaque walls).
 	_gfx->setWireframe(true, lit ? 7 : 0);
 
 	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
 	_gfx->clear(lit ? 7 : 0);
- 
-	uint32 wallColor = lit ? 0 : 7; 
-	uint32 floorColor = lit ? 0 : 7; 
+
+	uint32 wallColor = lit ? 0 : 7;
+	uint32 floorColor = lit ? 0 : 7;
 
 	// Draw large floor and ceiling quads
 	// These will be filled with the background color in the occlusion pass
@@ -1181,10 +1181,19 @@ void ColonyEngine::renderCorridor3D() {
 	}
 	
 	drawWallFeatures3D();
+
+	// F8 toggles polyfill for objects only (DOS doFunctionKey case 8).
+	// Non-fill: objects are outline-only (see-through). Walls stay filled.
+	if (_wireframe) {
+		_gfx->setWireframe(true); // No fill = outline-only objects
+	}
 	drawStaticObjects();
+	if (_wireframe) {
+		_gfx->setWireframe(true, lit ? 7 : 0); // Restore wall fill
+	}
 
 	_gfx->end3D();
-	_gfx->setWireframe(oldWire);
+	_gfx->setWireframe(false);
 }
 
 bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index b8ffb834c66..01b0a832210 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -59,9 +59,9 @@ public:
 	void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) override;
 	void drawPolygon(const int *x, const int *y, int count, uint32 color) override;
 	void copyToScreen() override;
-	void setWireframe(bool enable, int fillColor = -1) override { 
-		_wireframe = enable; 
-		if (fillColor != -1) _wireframeFillColor = (uint32)fillColor;
+	void setWireframe(bool enable, int fillColor = -1) override {
+		_wireframe = enable;
+		_wireframeFillColor = fillColor;
 	}
 	void computeScreenViewport() override;
 
@@ -73,7 +73,7 @@ private:
 	int _height;
 	byte _palette[256 * 3];
 	bool _wireframe;
-	uint32 _wireframeFillColor;
+	int _wireframeFillColor; // -1 = no fill (outline only)
 	Common::Rect _screenViewport;
 };
 
@@ -236,20 +236,21 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 	float fy2 = y2 * 256.0f;
 
 	if (_wireframe) {
-		// Pass 1: Draw background fill (pushed back)
-		glEnable(GL_POLYGON_OFFSET_FILL);
-		glPolygonOffset(1.1f, 4.0f);
-		useColor(_wireframeFillColor);
-		glBegin(GL_QUADS);
-		glVertex3f(fx1, fy1, -160.0f);
-		glVertex3f(fx2, fy2, -160.0f);
-		glVertex3f(fx2, fy2, 160.0f);
-		glVertex3f(fx1, fy1, 160.0f);
-		glEnd();
+		if (_wireframeFillColor >= 0) {
+			// Pass 1: Draw background fill (pushed back)
+			glEnable(GL_POLYGON_OFFSET_FILL);
+			glPolygonOffset(1.1f, 4.0f);
+			useColor((uint32)_wireframeFillColor);
+			glBegin(GL_QUADS);
+			glVertex3f(fx1, fy1, -160.0f);
+			glVertex3f(fx2, fy2, -160.0f);
+			glVertex3f(fx2, fy2, 160.0f);
+			glVertex3f(fx1, fy1, 160.0f);
+			glEnd();
+			glDisable(GL_POLYGON_OFFSET_FILL);
+		}
 
-		// Pass 2: Draw colored wireframe edges pulled forward relative to the fill
-		glEnable(GL_POLYGON_OFFSET_LINE);
-		glPolygonOffset(0.0f, -1.0f); // Pull lines forward from the fill
+		// Pass 2: Draw colored wireframe edges
 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 		useColor(color);
 		glBegin(GL_QUADS);
@@ -259,8 +260,6 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 		glVertex3f(fx1, fy1, 160.0f);
 		glEnd();
 		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-		glDisable(GL_POLYGON_OFFSET_LINE);
-		glDisable(GL_POLYGON_OFFSET_FILL);
 	} else {
 		// Normal mode: push the wall face back slightly.
 		// This ensures that wall features drawn later as lines at the same depth correctly win the depth test.
@@ -279,20 +278,21 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
  
 void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) {
 	if (_wireframe) {
-		// Pass 1: Draw background fill (pushed back)
-		glEnable(GL_POLYGON_OFFSET_FILL);
-		glPolygonOffset(1.1f, 4.0f);
-		useColor(_wireframeFillColor);
-		glBegin(GL_QUADS);
-		glVertex3f(x1, y1, z1);
-		glVertex3f(x2, y2, z2);
-		glVertex3f(x3, y3, z3);
-		glVertex3f(x4, y4, z4);
-		glEnd();
+		if (_wireframeFillColor >= 0) {
+			// Pass 1: Draw background fill (pushed back)
+			glEnable(GL_POLYGON_OFFSET_FILL);
+			glPolygonOffset(1.1f, 4.0f);
+			useColor((uint32)_wireframeFillColor);
+			glBegin(GL_QUADS);
+			glVertex3f(x1, y1, z1);
+			glVertex3f(x2, y2, z2);
+			glVertex3f(x3, y3, z3);
+			glVertex3f(x4, y4, z4);
+			glEnd();
+			glDisable(GL_POLYGON_OFFSET_FILL);
+		}
 
-		// Pass 2: Draw colored wireframe edges pulled forward
-		glEnable(GL_POLYGON_OFFSET_LINE);
-		glPolygonOffset(0.0f, -1.0f);
+		// Pass 2: Draw colored wireframe edges
 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 		useColor(color);
 		glBegin(GL_QUADS);
@@ -302,8 +302,6 @@ void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2
 		glVertex3f(x4, y4, z4);
 		glEnd();
 		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-		glDisable(GL_POLYGON_OFFSET_LINE);
-		glDisable(GL_POLYGON_OFFSET_FILL);
 	} else {
 		// Normal mode: push back to allow overlays (like wall features or floor decorations)
 		glEnable(GL_POLYGON_OFFSET_FILL);
@@ -323,18 +321,19 @@ void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *
 	if (count < 3) return;
 
 	if (_wireframe) {
-		// Pass 1: Draw background fill (pushed back)
-		glEnable(GL_POLYGON_OFFSET_FILL);
-		glPolygonOffset(1.1f, 4.0f);
-		useColor(_wireframeFillColor);
-		glBegin(GL_POLYGON);
-		for (int i = 0; i < count; i++)
-			glVertex3f(x[i], y[i], z[i]);
-		glEnd();
+		if (_wireframeFillColor >= 0) {
+			// Pass 1: Draw background fill (pushed back)
+			glEnable(GL_POLYGON_OFFSET_FILL);
+			glPolygonOffset(1.1f, 4.0f);
+			useColor((uint32)_wireframeFillColor);
+			glBegin(GL_POLYGON);
+			for (int i = 0; i < count; i++)
+				glVertex3f(x[i], y[i], z[i]);
+			glEnd();
+			glDisable(GL_POLYGON_OFFSET_FILL);
+		}
 
-		// Pass 2: Draw colored wireframe edges pulled forward
-		glEnable(GL_POLYGON_OFFSET_LINE);
-		glPolygonOffset(0.0f, -1.0f);
+		// Pass 2: Draw colored wireframe edges
 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 		useColor(color);
 		glBegin(GL_POLYGON);
@@ -342,8 +341,6 @@ void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *
 			glVertex3f(x[i], y[i], z[i]);
 		glEnd();
 		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-		glDisable(GL_POLYGON_OFFSET_LINE);
-		glDisable(GL_POLYGON_OFFSET_FILL);
 	} else {
 		// Normal mode: push back
 		glEnable(GL_POLYGON_OFFSET_FILL);


Commit: b31166ae486231860037beee7c8c69808601a4a3
    https://github.com/scummvm/scummvm/commit/b31166ae486231860037beee7c8c69808601a4a3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:19+02:00

Commit Message:
COLONY: add 3D leaf rendering for plant objects

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 20353210589..df374704240 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -302,6 +302,7 @@ public:
 
 private:
 	void draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook);
+	void draw3DLeaf(const Thing &obj, const PrismPartDef &def);
 	bool drawStaticObjectPrisms3D(const Thing &obj);
 	void renderCorridor3D();
 	void drawWallFeatures3D();
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index b1249eca256..465999e1733 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -453,7 +453,7 @@ static const int kPlantLeaf3Pts[3][3] = {{0, 0, 0}, {20, -10, 50}, {90, -70, 70}
 static const int kPlantLeaf4Pts[3][3] = {{0, 0, 0}, {20, -30, 190}, {40, -60, 150}};
 static const int kPlantLeaf5Pts[3][3] = {{0, 0, 0}, {20, -10, 130}, {50, -80, 200}};
 
-static const int kPlantLeafSurf[2][8] = {{kColorPlant, 3, 0, 1, 2, 0, 0, 0}, {kColorPlant, 3, 2, 1, 0, 0, 0, 0}};
+static const int kPlantLeafSurf[2][8] = {{kColorClear, 3, 0, 1, 2, 0, 0, 0}, {kColorClear, 3, 2, 1, 0, 0, 0, 0}};
 
 static const Colony::ColonyEngine::PrismPartDef kPlantParts[8] = {
 	{12, kPlantPotPts, 6, kPlantPotSurf},
@@ -611,6 +611,42 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 	}
 }
 
+void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
+	// DOS DrawLeaf: draws leaf surfaces as lines (MoveTo/LineTo), not filled polygons.
+	// PenColor is set to vGREEN by the caller (MakePlant).
+	const uint8 ang = obj.where.ang + 32;
+	const long rotCos = _cost[ang];
+	const long rotSin = _sint[ang];
+	const bool lit = (_corePower[_coreIndex] > 0);
+	const uint32 color = lit ? 2 : 15; // vGREEN when lit, white when dark
+
+	for (int i = 0; i < def.surfaceCount; i++) {
+		const int n = def.surfaces[i][1];
+		if (n < 2) continue;
+
+		float px[8], py[8], pz[8];
+		int count = 0;
+
+		for (int j = 0; j < n; j++) {
+			const int cur = def.surfaces[i][j + 2];
+			if (cur < 0 || cur >= def.pointCount) continue;
+			int ox = def.points[cur][0];
+			int oy = def.points[cur][1];
+			int oz = def.points[cur][2];
+			long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
+			long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+			px[count] = (float)(rx + obj.where.xloc);
+			py[count] = (float)(ry + obj.where.yloc);
+			pz[count] = (float)(oz - 160);
+			count++;
+		}
+
+		// Draw as connected line segments (MoveTo first point, LineTo the rest)
+		for (int j = 0; j < count - 1; j++)
+			_gfx->draw3DLine(px[j], py[j], pz[j], px[j + 1], py[j + 1], pz[j + 1], color);
+	}
+}
+
 void ColonyEngine::computeVisibleCells() {
 	memset(_visibleCell, 0, sizeof(_visibleCell));
 
@@ -908,29 +944,45 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	}
 	case kWallFeatureWindow: {
-		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
+		// DOS drawwind: window pane at 1/4..3/4 with vertical + horizontal center lines
 		const uint32 winColor = 8; // vDKGRAY
-		float xl = 0.3125f, xr = 0.6875f;
-		float yb = 0.3125f, yt = 0.6875f;
+		float xl = 0.25f, xr = 0.75f;
+		float yb = 0.25f, yt = 0.75f;
+		float xc = 0.5f, yc = 0.5f;
+		// Window frame
 		wallLine(corners, xl, yb, xl, yt, winColor);
 		wallLine(corners, xl, yt, xr, yt, winColor);
 		wallLine(corners, xr, yt, xr, yb, winColor);
 		wallLine(corners, xr, yb, xl, yb, winColor);
-		wallLine(corners, xl, 0.5f, xr, 0.5f, winColor);
+		// Mullion (vertical center) and transom (horizontal center)
+		wallLine(corners, xc, yb, xc, yt, winColor);
+		wallLine(corners, xl, yc, xr, yc, winColor);
 		break;
 	}
 	case kWallFeatureShelves: {
-		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
+		// DOS drawbooks: recessed bookcase with 3D depth.
+		// The depth effect uses actual back-wall coordinates in DOS, which we can't
+		// replicate with UV mapping on a single wall face. We draw:
+		// - Back face rectangle (inset)
+		// - 4 connecting lines (front corners to back corners)
+		// - 7 horizontal shelf lines across the front face (full width)
 		const uint32 shelfColor = 8; // vDKGRAY
-		float xl = 0.15f, xr = 0.85f;
-		float yb = 0.1f, yt = 0.9f;
-		wallLine(corners, xl, yb, xr, yb, shelfColor);
-		wallLine(corners, xr, yb, xr, yt, shelfColor);
-		wallLine(corners, xr, yt, xl, yt, shelfColor);
-		wallLine(corners, xl, yt, xl, yb, shelfColor);
-		for (int i = 1; i <= 6; i++) {
-			float t = yb + (yt - yb) * (float)i / 7.0f;
-			wallLine(corners, xl, t, xr, t, shelfColor);
+		float bx = 0.1875f, bxr = 0.8125f;
+		float by = 0.1875f, byt = 0.8125f;
+		// Back face rectangle
+		wallLine(corners, bx, by, bxr, by, shelfColor);
+		wallLine(corners, bxr, by, bxr, byt, shelfColor);
+		wallLine(corners, bxr, byt, bx, byt, shelfColor);
+		wallLine(corners, bx, byt, bx, by, shelfColor);
+		// Connecting lines (front corners to back corners)
+		wallLine(corners, 0.0f, 0.0f, bx, by, shelfColor);
+		wallLine(corners, 0.0f, 1.0f, bx, byt, shelfColor);
+		wallLine(corners, 1.0f, 0.0f, bxr, by, shelfColor);
+		wallLine(corners, 1.0f, 1.0f, bxr, byt, shelfColor);
+		// 7 shelf lines across front face (DOS split7 at 1/8..7/8 intervals)
+		for (int i = 1; i <= 7; i++) {
+			float v = (float)i / 8.0f;
+			wallLine(corners, 0.0f, v, 1.0f, v, shelfColor);
 		}
 		break;
 	}
@@ -1206,8 +1258,11 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 			draw3DPrism(obj, kCChairParts[i], false);
 		break;
 	case kObjPlant:
-		for (int i = 0; i < 8; i++)
-			draw3DPrism(obj, kPlantParts[i], false);
+		// DOS MakePlant draw order: top pot, then green leaf lines, then pot on top.
+		draw3DPrism(obj, kPlantParts[1], false); // top pot (soil)
+		for (int i = 2; i < 8; i++)
+			draw3DLeaf(obj, kPlantParts[i]); // leaves as lines
+		draw3DPrism(obj, kPlantParts[0], false); // pot (drawn last, on top)
 		break;
 	case kObjCouch:
 	case kObjChair: {


Commit: 5dc287f3456bea81aa9f3216443c5cd8b1d88c7b
    https://github.com/scummvm/scummvm/commit/5dc287f3456bea81aa9f3216443c5cd8b1d88c7b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:20+02:00

Commit Message:
COLONY: power suit

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index df374704240..b2eed96ebd2 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -115,6 +115,9 @@ enum ObjColor {
 	kColorTable = 61,
 	kColorTableBase = 62,
 	kColorWall = 77,
+	kColorPower = 54,
+	kColorPBase = 55,
+	kColorPSource = 56,
 	kColorRainbow1 = 80,
 	kColorRainbow2 = 81
 };
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 465999e1733..010d6110e76 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -68,6 +68,9 @@ static uint8 lookupLineColor(int colorIdx) {
 	case kColorMacScreen: return 8;  // vDKGRAY
 	case kColorPot:       return 6;  // vBROWN
 	case kColorPlant:     return 2;  // vGREEN (hardcoded in MakePlant via DrawLeaf)
+	case kColorPower:     return 1;  // vBLUE (lsColor[54] LINECOLOR)
+	case kColorPBase:     return 1;  // vBLUE (lsColor[55] LINECOLOR)
+	case kColorPSource:   return 4;  // vRED (lsColor[56] LINECOLOR)
 	case kColorTable:     return 6;  // vBROWN
 	case kColorTableBase: return 6;  // vBROWN
 	case kColorWall:      return 0;  // vBLACK
@@ -512,6 +515,45 @@ static const Colony::ColonyEngine::PrismPartDef kReactorParts[3] = {
 	{8, kReactorTopPts, 6, kReactorBaseSurf}
 };
 
+// Power Suit: triangular prism body + small rectangular pedestal + flat table + hexagonal power source
+// DOS INITOBJ.C: 5 prism parts. Floor=160, so 2*Floor=320.
+static const int kPowerTopPts[3][3] = {{-150, 120, 320}, {150, 120, 320}, {0, -150, 320}};
+static const int kPowerTopSurf[1][8] = {{kColorPower, 3, 0, 1, 2, 0, 0, 0}};
+
+static const int kPowerBottomPts[3][3] = {{-150, 120, 0}, {150, 120, 0}, {0, -150, 0}};
+static const int kPowerBottomSurf[1][8] = {{kColorPower, 3, 2, 1, 0, 0, 0, 0}};
+
+static const int kPowerBasePts[8][3] = {
+	{-5, 100, 0}, {5, 100, 0}, {5, 90, 0}, {-5, 90, 0},
+	{-5, 100, 100}, {5, 100, 100}, {5, 90, 100}, {-5, 90, 100}
+};
+static const int kPowerBaseSurf[4][8] = {
+	{kColorPBase, 4, 0, 3, 7, 4, 0, 0}, {kColorPBase, 4, 3, 2, 6, 7, 0, 0},
+	{kColorPBase, 4, 1, 0, 4, 5, 0, 0}, {kColorPBase, 4, 2, 1, 5, 6, 0, 0}
+};
+
+static const int kPowerTablePts[4][3] = {{-50, 135, 100}, {50, 135, 100}, {50, 55, 100}, {-50, 55, 100}};
+static const int kPowerTableSurf[1][8] = {{kColorBlack, 4, 3, 2, 1, 0, 0, 0}};
+
+static const int kPowerSourcePts[12][3] = {
+	{-75, 0, 290}, {-35, 60, 290}, {35, 60, 290}, {75, 0, 290}, {35, -60, 290}, {-35, -60, 290},
+	{-75, 0, 320}, {-35, 60, 320}, {35, 60, 320}, {75, 0, 320}, {35, -60, 320}, {-35, -60, 320}
+};
+static const int kPowerSourceSurf[7][8] = {
+	{kColorRainbow1, 6, 0, 1, 2, 3, 4, 5},
+	{kColorPSource, 4, 0, 6, 7, 1, 0, 0}, {kColorPSource, 4, 1, 7, 8, 2, 0, 0},
+	{kColorPSource, 4, 2, 8, 9, 3, 0, 0}, {kColorPSource, 4, 3, 9, 10, 4, 0, 0},
+	{kColorPSource, 4, 4, 10, 11, 5, 0, 0}, {kColorPSource, 4, 5, 11, 6, 0, 0, 0}
+};
+
+static const Colony::ColonyEngine::PrismPartDef kPowerSuitParts[5] = {
+	{3, kPowerTopPts, 1, kPowerTopSurf},
+	{3, kPowerBottomPts, 1, kPowerBottomSurf},
+	{8, kPowerBasePts, 4, kPowerBaseSurf},
+	{4, kPowerTablePts, 1, kPowerTableSurf},
+	{12, kPowerSourcePts, 7, kPowerSourceSurf}
+};
+
 
 void ColonyEngine::quadrant() {
 	int remain;
@@ -1313,7 +1355,8 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 			draw3DPrism(obj, kReactorParts[i], false);
 		break;
 	case kObjPowerSuit:
-		draw3DPrism(obj, kConsolePart, false); // Placeholder
+		for (int i = 0; i < 5; i++)
+			draw3DPrism(obj, kPowerSuitParts[i], false);
 		break;
 	case kObjTeleport:
 		draw3DPrism(obj, kCWallParts[0], false); // Placeholder


Commit: 96fb82caafc76c73a76d164fbd4e48d71e526767
    https://github.com/scummvm/scummvm/commit/96fb82caafc76c73a76d164fbd4e48d71e526767
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:20+02:00

Commit Message:
COLONY: add cryo chamber and forklift 3D object geometry

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index b2eed96ebd2..1e05126ce5a 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -110,14 +110,25 @@ enum ObjColor {
 	kColorDeskChair = 39,
 	kColorMac = 40,
 	kColorMacScreen = 41,
+	kColorCryo = 33,
+	kColorCryoGlass = 34,
+	kColorCryoBase = 35,
+	kColorForklift = 49,
+	kColorTread1 = 50,
+	kColorTread2 = 51,
 	kColorPot = 52,
 	kColorPlant = 53,
-	kColorTable = 61,
-	kColorTableBase = 62,
-	kColorWall = 77,
 	kColorPower = 54,
 	kColorPBase = 55,
 	kColorPSource = 56,
+	kColorTable = 61,
+	kColorTableBase = 62,
+	kColorPStand = 63,
+	kColorPLens = 64,
+	kColorProjector = 65,
+	kColorTele = 66,
+	kColorTeleDoor = 67,
+	kColorWall = 77,
 	kColorRainbow1 = 80,
 	kColorRainbow2 = 81
 };
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 010d6110e76..f0ad9b3f5db 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -49,9 +49,9 @@ static uint8 lookupLineColor(int colorIdx) {
 	case kColorBath:      return 0;  // vBLACK
 	case kColorWater:     return 1;  // vBLUE
 	case kColorSilver:    return 1;  // vBLUE
-	case kColorReactor:   return 7;  // vWHITE
+	case kColorReactor:   return 0;  // vBLACK (LINEFILLCOLOR for fill mode)
 	case kColorBlanket:   return 2;  // vGREEN
-	case kColorSheet:     return 15; // vINTWHITE
+	case kColorSheet:     return 0;  // vBLACK (LINEFILLCOLOR for fill mode)
 	case kColorBed:       return 6;  // vBROWN
 	case kColorBox:       return 6;  // vBROWN
 	case kColorChair:     return 1;  // vBLUE
@@ -66,13 +66,24 @@ static uint8 lookupLineColor(int colorIdx) {
 	case kColorDeskChair: return 2;  // vGREEN
 	case kColorMac:       return 0;  // vBLACK
 	case kColorMacScreen: return 8;  // vDKGRAY
+	case kColorCryo:      return 1;  // vBLUE
+	case kColorCryoGlass: return 1;  // vBLUE
+	case kColorCryoBase:  return 1;  // vBLUE
+	case kColorForklift:  return 14; // vYELLOW
+	case kColorTread1:    return 14; // vYELLOW
+	case kColorTread2:    return 14; // vYELLOW
 	case kColorPot:       return 6;  // vBROWN
-	case kColorPlant:     return 2;  // vGREEN (hardcoded in MakePlant via DrawLeaf)
-	case kColorPower:     return 1;  // vBLUE (lsColor[54] LINECOLOR)
-	case kColorPBase:     return 1;  // vBLUE (lsColor[55] LINECOLOR)
-	case kColorPSource:   return 4;  // vRED (lsColor[56] LINECOLOR)
+	case kColorPlant:     return 2;  // vGREEN
+	case kColorPower:     return 1;  // vBLUE
+	case kColorPBase:     return 1;  // vBLUE
+	case kColorPSource:   return 4;  // vRED
 	case kColorTable:     return 6;  // vBROWN
 	case kColorTableBase: return 6;  // vBROWN
+	case kColorPStand:    return 5;  // vMAGENTA
+	case kColorPLens:     return 0;  // vBLACK
+	case kColorProjector: return 3;  // vCYAN
+	case kColorTele:      return 4;  // vRED
+	case kColorTeleDoor:  return 1;  // vBLUE
 	case kColorWall:      return 0;  // vBLACK
 	case kColorRainbow1:  return 4;  // vRED
 	case kColorRainbow2:  return 14; // vYELLOW
@@ -87,22 +98,22 @@ static const int kScreenPts[8][3] = {
 };
 static const int kScreenSurf[4][8] = {
 	{kColorBlack, 4, 0, 3, 7, 4, 0, 0}, {kColorBlack, 4, 3, 2, 6, 7, 0, 0},
-	{kColorBlack, 4, 2, 1, 5, 6, 0, 0}, {kColorBlack, 4, 7, 6, 5, 4, 0, 0}
+	{kColorBlack, 4, 1, 0, 4, 5, 0, 0}, {kColorBlack, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kTableTopPts[4][3] = {
-	{-64, 64, 100}, {64, 64, 100}, {64, -64, 100}, {-64, -64, 100}
+	{-128, 128, 100}, {128, 128, 100}, {128, -128, 100}, {-128, -128, 100}
 };
 static const int kTableTopSurf[1][8] = {{kColorTable, 4, 3, 2, 1, 0, 0, 0}};
 static const int kTableBasePts[8][3] = {
-	{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0},
-	{-20, 20, 100}, {20, 20, 100}, {20, -20, 100}, {-20, -20, 100}
+	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+	{-5, 5, 100}, {5, 5, 100}, {5, -5, 100}, {-5, -5, 100}
 };
 static const int kTableBaseSurf[4][8] = {
 	{kColorTableBase, 4, 0, 3, 7, 4, 0, 0}, {kColorTableBase, 4, 3, 2, 6, 7, 0, 0},
 	{kColorTableBase, 4, 1, 0, 4, 5, 0, 0}, {kColorTableBase, 4, 2, 1, 5, 6, 0, 0}
 };
 static const int kBedPostPts[4][3] = {
-	{-80, 180, 0}, {80, 180, 0}, {80, 180, 100}, {-80, 180, 100}
+	{-82, 128, 100}, {82, 128, 100}, {82, 128, 0}, {-82, 128, 0}
 };
 static const int kBedPostSurf[1][8] = {{kColorBed, 4, 3, 2, 1, 0, 0, 0}};
 static const int kBedBlanketPts[8][3] = {
@@ -114,23 +125,23 @@ static const int kBlanketSurf[4][8] = {
 	{kColorBlanket, 4, 2, 1, 5, 6, 0, 0}, {kColorBlanket, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kBedSheetPts[8][3] = {
-	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60},
-	{-80, 70, 80}, {80, 70, 80}, {80, -175, 80}, {-80, -175, 80}
+	{-80, 128, 30}, {80, 128, 30}, {80, 70, 30}, {-80, 70, 30},
+	{-80, 128, 60}, {80, 128, 60}, {80, 70, 60}, {-80, 70, 60}
 };
 static const int kSheetSurf[3][8] = {
 	{kColorSheet, 4, 0, 3, 7, 4, 0, 0}, {kColorSheet, 4, 2, 1, 5, 6, 0, 0},
 	{kColorSheet, 4, 7, 6, 5, 4, 0, 0}
 };
 static const int kBBedBlanketPts[8][3] = {
-	{-120, 96, 0}, {120, 96, 0}, {120, -96, 0}, {-120, -96, 0},
-	{-120, 96, 60}, {120, 96, 60}, {120, -96, 60}, {-120, -96, 60}
+	{-128, 70, 0}, {128, 70, 0}, {128, -175, 0}, {-128, -175, 0},
+	{-128, 70, 60}, {128, 70, 60}, {128, -175, 60}, {-128, -175, 60}
 };
 static const int kBBedSheetPts[8][3] = {
-	{-120, 96, 60}, {120, 96, 60}, {120, -96, 60}, {-120, -96, 60},
-	{-120, 96, 80}, {120, 96, 80}, {120, -96, 80}, {-120, -96, 80}
+	{-128, 128, 30}, {128, 128, 30}, {128, 70, 30}, {-128, 70, 30},
+	{-128, 128, 60}, {128, 128, 60}, {128, 70, 60}, {-128, 70, 60}
 };
 static const int kBBedPostPts[4][3] = {
-	{-120, 96, 0}, {120, 96, 0}, {120, 96, 100}, {-120, 96, 100}
+	{-130, 128, 100}, {130, 128, 100}, {130, 128, 0}, {-130, 128, 0}
 };
 static const int kDeskTopPts[4][3] = {
 	{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
@@ -285,8 +296,8 @@ static const int kMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
 
 // Bathtub geometry
 static const int kTubPts[8][3] = {
-	{-127, 127,  0}, {   0, 127,  0}, {   0,-127,  0}, {-127,-127,  0},
-	{-127, 127, 70}, {   0, 127, 70}, {   0,-127, 70}, {-127,-127, 70}
+	{-128, 128,  0}, {   0, 128,  0}, {   0,-128,  0}, {-128,-128,  0},
+	{-128, 128, 70}, {   0, 128, 70}, {   0,-128, 70}, {-128,-128, 70}
 };
 static const int kTubSurf[5][8] = {
 	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
@@ -300,8 +311,8 @@ static const int kDTubSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
 
 // Toilet geometry
 static const int kAToiletPts[8][3] = {
-	{-127, 45, 30}, {-100, 45, 30}, {-100, -45, 30}, {-127, -45, 30},
-	{-127, 45, 100}, {-100, 45, 100}, {-100, -45, 100}, {-127, -45, 100}
+	{-128, 45, 30}, {-100, 45, 30}, {-100, -45, 30}, {-128, -45, 30},
+	{-128, 45, 100}, {-100, 45, 100}, {-100, -45, 100}, {-128, -45, 100}
 };
 static const int kAToiletSurf[5][8] = {
 	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
@@ -326,14 +337,14 @@ static const int kDToiletPts[6][3] = {
 };
 static const int kDToiletSurf[1][8] = {{kColorLtGreen, 6, 5, 4, 3, 2, 1, 0}};
 static const int kEToiletPts[4][3] = {
-	{-127,-127, 20}, {-127,-127, 200}, { 127,-127, 200}, { 127,-127, 20}
+	{-128,-128, 20}, {-128,-128, 200}, { 128,-128, 200}, { 128,-128, 20}
 };
 static const int kEToiletSurf[1][8] = {{kColorDkGray, 4, 0, 1, 2, 3, 0, 0}};
 
 // Sink geometry
 static const int kSinkPts[8][3] = {
-	{-127, 50, 70}, {-50, 50, 90}, {-50,-50, 90}, {-127,-50, 70},
-	{-127, 50, 110}, {-50, 50, 110}, {-50,-50, 110}, {-127,-50, 110}
+	{-128, 50, 70}, {-50, 50, 90}, {-50,-50, 90}, {-128,-50, 70},
+	{-128, 50, 110}, {-50, 50, 110}, {-50,-50, 110}, {-128,-50, 110}
 };
 static const int kSinkSurf[5][8] = {
 	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
@@ -345,7 +356,7 @@ static const int kDSinkPts[6][3] = {
 };
 static const int kDSinkSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
 static const int kSinkMirrorPts[4][3] = {
-	{-127, 65, 130}, {-127, -65, 130}, {-127, 65, 250}, {-127, -65, 250}
+	{-128, 65, 130}, {-128, -65, 130}, {-128, 65, 250}, {-128, -65, 250}
 };
 static const int kSinkMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
 
@@ -380,7 +391,7 @@ static const Colony::ColonyEngine::PrismPartDef kCChairParts[5] = {
 	{4, kCSeatPts, 1, kCSeatSurf},
 	{4, kCArmLeftPts, 1, kCArmLeftSurf},
 	{4, kCArmRightPts, 1, kCArmRightSurf},
-	{4, kCBackPts, 2, kCBackSurf},
+	{4, kCBackPts, 1, kCBackSurf},
 	{8, kCBasePts, 4, kCBaseSurf}
 };
 static const Colony::ColonyEngine::PrismPartDef kConsolePart = {8, kConsolePts, 5, kConsoleSurf};
@@ -427,6 +438,12 @@ static const Colony::ColonyEngine::PrismPartDef kPToiletParts[5] = {
 	{4, kEToiletPts, 1, kEToiletSurf}
 };
 
+static const int kFWallPts[4][3] = {
+	{-128, 128, 0}, {128, -128, 0}, {-128, 128, 320}, {128, -128, 320}
+};
+static const int kFWallSurf[1][8] = {{kColorWall, 4, 2, 3, 1, 0, 0, 0}};
+static const Colony::ColonyEngine::PrismPartDef kFWallPart = {4, kFWallPts, 1, kFWallSurf};
+
 static const int kCWallPts[8][3] = {
 	{-128, 128, 0}, {0, 112, 0}, {112, 0, 0}, {128, -128, 0},
 	{-128, 128, 320},  {0, 112, 320},  {112, 0, 320},  {128, -128, 320}
@@ -434,7 +451,7 @@ static const int kCWallPts[8][3] = {
 static const int kCWallSurf[3][8] = {
 	{kColorWall, 4, 1, 0, 4, 5, 0, 0}, {kColorWall, 4, 2, 1, 5, 6, 0, 0}, {kColorWall, 4, 3, 2, 6, 7, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kCWallParts[1] = {{8, kCWallPts, 3, kCWallSurf}};
+static const Colony::ColonyEngine::PrismPartDef kCWallPart = {8, kCWallPts, 3, kCWallSurf};
 
 static const int kPlantPotPts[12][3] = {
 	{10, 17, 40}, {20, 0, 40}, {10, -17, 40}, {-10, -17, 40}, {-20, 0, 40}, {-10, 17, 40},
@@ -483,8 +500,8 @@ static const int kBox2Pts[8][3] = {
 };
 
 static const int kReactorCorePts[12][3] = {
-	{32, 55, 120}, {64, 0, 120}, {32, -55, 120}, {-32, -55, 120}, {-64, 0, 120}, {-32, 55, 120},
-	{32, 55, 168}, {64, 0, 168}, {32, -55, 168}, {-32, -55, 168}, {-64, 0, 168}, {-32, 55, 168}
+	{-40, 20, 288}, {0, 40, 288}, {40, 20, 288}, {40, -20, 288}, {0, -40, 288}, {-40, -20, 288},
+	{-40, 20, 32}, {0, 40, 32}, {40, 20, 32}, {40, -20, 32}, {0, -40, 32}, {-40, -20, 32}
 };
 static const int kReactorCoreSurf[7][8] = {
 	{kColorReactor, 4, 0, 1, 7, 6, 0, 0}, {kColorReactor, 4, 1, 2, 8, 7, 0, 0}, {kColorReactor, 4, 2, 3, 9, 8, 0, 0},
@@ -492,16 +509,16 @@ static const int kReactorCoreSurf[7][8] = {
 	{kColorReactor, 6, 5, 4, 3, 2, 1, 0}
 };
 static const int kReactorBasePts[8][3] = {
-	{-127, 127, 0}, {127, 127, 0}, {127, -127, 0}, {-127, -127, 0},
-	{-127, 127, 120}, {127, 127, 120}, {127, -127, 120}, {-127, -127, 120}
+	{-128, 128, 0}, {128, 128, 0}, {128, -128, 0}, {-128, -128, 0},
+	{-128, 128, 32}, {128, 128, 32}, {128, -128, 32}, {-128, -128, 32}
 };
 static const int kReactorBaseSurf[6][8] = {
 	{kColorRainbow1, 4, 0, 3, 7, 4, 0, 0}, {kColorRainbow1, 4, 3, 2, 6, 7, 0, 0}, {kColorRainbow1, 4, 1, 0, 4, 5, 0, 0},
 	{kColorRainbow1, 4, 2, 1, 5, 6, 0, 0}, {kColorRainbow1, 4, 7, 6, 5, 4, 0, 0}, {kColorRainbow1, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kReactorTopPts[8][3] = {
-	{-127, 127, 168}, {127, 127, 168}, {127, -127, 168}, {-127, -127, 168},
-	{-127, 127, 288}, {127, 127, 288}, {127, -127, 288}, {-127, -127, 288}
+	{-128, 128, 288}, {128, 128, 288}, {128, -128, 288}, {-128, -128, 288},
+	{-128, 128, 320}, {128, 128, 320}, {128, -128, 320}, {-128, -128, 320}
 };
 
 static const Colony::ColonyEngine::PrismPartDef kBox1Part = {8, kBox1Pts, 5, kBox1Surf};
@@ -554,6 +571,164 @@ static const Colony::ColonyEngine::PrismPartDef kPowerSuitParts[5] = {
 	{12, kPowerSourcePts, 7, kPowerSourceSurf}
 };
 
+// Cryo tube: top (coffin-shaped lid) + base
+static const int kCryoTopPts[16][3] = {
+	{-130,  50,  80}, { 130,  50,  80}, { 130, -50,  80}, {-130, -50,  80},
+	{-130,  50, 140}, { 130,  50, 140}, { 130, -50, 140}, {-130, -50, 140},
+	{   0,  50, 140}, {   0, -50, 140},
+	{-140,  70, 110}, { 140,  70, 110}, { 140, -70, 110}, {-140, -70, 110},
+	{   0,  70, 110}, {   0, -70, 110}
+};
+static const int kCryoTopSurf[12][8] = {
+	{kColorCryo,      4, 7, 9, 8, 4, 0, 0},
+	{kColorCryoGlass, 4, 9, 6, 5, 8, 0, 0},
+	{kColorCryo,      4, 0, 10, 11, 1, 0, 0},
+	{kColorCryo,      4, 1, 11, 12, 2, 0, 0},
+	{kColorCryo,      4, 2, 12, 13, 3, 0, 0},
+	{kColorCryo,      4, 3, 13, 10, 0, 0, 0},
+	{kColorCryo,      4, 7, 13, 15, 9, 0, 0},
+	{kColorCryo,      4, 4, 10, 13, 7, 0, 0},
+	{kColorCryo,      4, 14, 10, 4, 8, 0, 0},
+	{kColorSilver,    4, 5, 11, 14, 8, 0, 0},
+	{kColorSilver,    4, 6, 12, 11, 5, 0, 0},
+	{kColorSilver,    4, 9, 15, 12, 6, 0, 0}
+};
+static const int kCryoBasePts[8][3] = {
+	{-130,  50,  0}, { 130,  50,  0}, { 130, -50,  0}, {-130, -50,  0},
+	{-130,  50, 80}, { 130,  50, 80}, { 130, -50, 80}, {-130, -50, 80}
+};
+static const int kCryoBaseSurf[5][8] = {
+	{kColorCryoBase, 4, 0, 3, 7, 4, 0, 0}, {kColorCryoBase, 4, 3, 2, 6, 7, 0, 0},
+	{kColorCryoBase, 4, 1, 0, 4, 5, 0, 0}, {kColorCryoBase, 4, 2, 1, 5, 6, 0, 0},
+	{kColorCryo,     4, 7, 6, 5, 4, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kCryoParts[2] = {
+	{16, kCryoTopPts, 12, kCryoTopSurf},
+	{8, kCryoBasePts, 5, kCryoBaseSurf}
+};
+
+// Forklift: cab + treads + upper-left arm + lower-left fork + upper-right arm + lower-right fork
+static const int kFLCabPts[14][3] = {
+	{-35, 60, 40}, {35, 60, 40}, {35, -60, 40}, {-35, -60, 40},
+	{-15, 60, 260}, {15, 60, 260}, {15, -60, 260}, {-15, -60, 260},
+	{25, 60, 140}, {25, -60, 140},
+	{70, 35, 120}, {70, -35, 120},
+	{-70, 40, 80}, {-70, -40, 80}
+};
+static const int kFLCabSurf[12][8] = {
+	{kColorForklift, 4, 0, 3, 13, 12, 0, 0}, {kColorForklift, 4, 12, 13, 7, 4, 0, 0},
+	{kColorForklift, 3, 0, 12, 4, 0, 0, 0},  {kColorForklift, 3, 3, 7, 13, 0, 0, 0},
+	{kColorForklift, 4, 3, 2, 6, 7, 0, 0},   {kColorForklift, 4, 1, 0, 4, 5, 0, 0},
+	{kColorForklift, 3, 1, 8, 10, 0, 0, 0},  {kColorForklift, 3, 2, 11, 9, 0, 0, 0},
+	{kColorForklift, 4, 1, 10, 11, 2, 0, 0},
+	{kColorSilver,   3, 8, 5, 10, 0, 0, 0},  {kColorSilver, 3, 11, 6, 9, 0, 0, 0},
+	{kColorSilver,   4, 10, 5, 6, 11, 0, 0}
+};
+static const int kFLTreadPts[12][3] = {
+	{-60, 60, 20}, {60, 60, 20}, {60, -60, 20}, {-60, -60, 20},
+	{-35, 60, 40}, {35, 60, 40}, {35, -60, 40}, {-35, -60, 40},
+	{-35, 60, 0}, {35, 60, 0}, {35, -60, 0}, {-35, -60, 0}
+};
+static const int kFLTreadSurf[6][8] = {
+	{kColorTread1, 4, 0, 3, 7, 4, 0, 0},
+	{kColorTread2, 6, 3, 11, 10, 2, 6, 7},
+	{kColorTread2, 6, 0, 4, 5, 1, 9, 8},
+	{kColorTread1, 4, 2, 1, 5, 6, 0, 0},
+	{kColorTread1, 4, 0, 8, 11, 3, 0, 0},
+	{kColorTread1, 4, 10, 9, 1, 2, 0, 0}
+};
+static const int kFLULPts[8][3] = {
+	{-15, 70, 120}, {15, 70, 120}, {15, 60, 120}, {-15, 60, 120},
+	{-25, 70, 230}, {25, 70, 230}, {25, 60, 230}, {-25, 60, 230}
+};
+static const int kFLArmSurf[4][8] = {
+	{kColorForklift, 4, 0, 3, 7, 4, 0, 0}, {kColorForklift, 4, 3, 2, 6, 7, 0, 0},
+	{kColorForklift, 4, 1, 0, 4, 5, 0, 0}, {kColorForklift, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kFLLLPts[8][3] = {
+	{-15, 80, 120}, {100, 80, 125}, {100, 70, 125}, {-15, 70, 120},
+	{-15, 80, 150}, {100, 80, 140}, {100, 70, 140}, {-15, 70, 150}
+};
+static const int kFLForkSurf[6][8] = {
+	{kColorForklift, 4, 0, 3, 7, 4, 0, 0}, {kColorForklift, 4, 3, 2, 6, 7, 0, 0},
+	{kColorForklift, 4, 1, 0, 4, 5, 0, 0}, {kColorBlack,    4, 2, 1, 5, 6, 0, 0},
+	{kColorForklift, 4, 7, 6, 5, 4, 0, 0}, {kColorForklift, 4, 0, 1, 2, 3, 0, 0}
+};
+static const int kFLURPts[8][3] = {
+	{-15, -60, 120}, {15, -60, 120}, {15, -70, 120}, {-15, -70, 120},
+	{-25, -60, 230}, {25, -60, 230}, {25, -70, 230}, {-25, -70, 230}
+};
+static const int kFLLRPts[8][3] = {
+	{-15, -70, 120}, {100, -70, 125}, {100, -80, 125}, {-15, -80, 120},
+	{-15, -70, 150}, {100, -70, 140}, {100, -80, 140}, {-15, -80, 150}
+};
+static const Colony::ColonyEngine::PrismPartDef kForkliftParts[6] = {
+	{14, kFLCabPts, 12, kFLCabSurf},
+	{12, kFLTreadPts, 6, kFLTreadSurf},
+	{8, kFLULPts, 4, kFLArmSurf},
+	{8, kFLLLPts, 6, kFLForkSurf},
+	{8, kFLURPts, 4, kFLArmSurf},
+	{8, kFLLRPts, 6, kFLForkSurf}
+};
+
+// Teleport: octagonal booth with flared middle
+static const int kTelePts[24][3] = {
+	// Ring 0: outer flared ring at z=140
+	{   0, 175, 140}, { 125, 125, 140}, { 175,   0, 140}, { 125,-125, 140},
+	{   0,-175, 140}, {-125,-125, 140}, {-175,   0, 140}, {-125, 125, 140},
+	// Ring 1: inner ring at z=0 (bottom)
+	{  0,  80, 0}, { 65,  65, 0}, { 80,   0, 0}, { 65, -65, 0},
+	{  0, -80, 0}, {-65, -65, 0}, {-80,   0, 0}, {-65,  65, 0},
+	// Ring 2: inner ring at z=280 (top)
+	{  0,  80, 280}, { 65,  65, 280}, { 80,   0, 280}, { 65, -65, 280},
+	{  0, -80, 280}, {-65, -65, 280}, {-80,   0, 280}, {-65,  65, 280}
+};
+static const int kTeleSurf[16][8] = {
+	// Bottom 8 panels (outer mid to inner bottom)
+	{kColorTeleDoor, 4, 0, 1, 9, 8, 0, 0},
+	{kColorTele,     4, 1, 2, 10, 9, 0, 0}, {kColorTele, 4, 2, 3, 11, 10, 0, 0},
+	{kColorTele,     4, 3, 4, 12, 11, 0, 0}, {kColorTele, 4, 4, 5, 13, 12, 0, 0},
+	{kColorTele,     4, 5, 6, 14, 13, 0, 0}, {kColorTele, 4, 6, 7, 15, 14, 0, 0},
+	{kColorTele,     4, 7, 0, 8, 15, 0, 0},
+	// Top 8 panels (outer mid to inner top)
+	{kColorSilver,   4, 1, 0, 16, 17, 0, 0},
+	{kColorTele,     4, 2, 1, 17, 18, 0, 0}, {kColorTele, 4, 3, 2, 18, 19, 0, 0},
+	{kColorTele,     4, 4, 3, 19, 20, 0, 0}, {kColorTele, 4, 5, 4, 20, 21, 0, 0},
+	{kColorTele,     4, 6, 5, 21, 22, 0, 0}, {kColorTele, 4, 7, 6, 22, 23, 0, 0},
+	{kColorTele,     4, 0, 7, 23, 16, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kTelePart = {24, kTelePts, 16, kTeleSurf};
+
+// Projector: body + stand + lens (sits on table)
+static const int kProjectorPts[8][3] = {
+	{-30, 30, 140}, {30, 30, 140}, {30, -30, 140}, {-30, -30, 140},
+	{-20, 30, 160}, {30, 30, 160}, {30, -30, 160}, {-20, -30, 160}
+};
+static const int kProjectorSurf[5][8] = {
+	{kColorProjector, 4, 0, 3, 7, 4, 0, 0}, {kColorProjector, 4, 3, 2, 6, 7, 0, 0},
+	{kColorProjector, 4, 1, 0, 4, 5, 0, 0}, {kColorProjector, 4, 2, 1, 5, 6, 0, 0},
+	{kColorProjector, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kPStandPts[4][3] = {
+	{-25, 50, 100}, {0, 10, 140}, {0, -10, 140}, {-25, -50, 100}
+};
+static const int kPStandSurf[1][8] = {{kColorPStand, 4, 0, 1, 2, 3, 0, 0}};
+static const int kPLensPts[12][3] = {
+	{30,  8, 154}, {30,  0, 158}, {30, -8, 154}, {30, -8, 146}, {30,  0, 142}, {30,  8, 146},
+	{55, 10, 155}, {55,  0, 160}, {55,-10, 155}, {55,-10, 145}, {55,  0, 140}, {55, 10, 145}
+};
+static const int kPLensSurf[7][8] = {
+	{kColorPLens, 4, 0, 1, 7, 6, 0, 0}, {kColorPLens, 4, 1, 2, 8, 7, 0, 0},
+	{kColorPLens, 4, 2, 3, 9, 8, 0, 0}, {kColorPLens, 4, 3, 4, 10, 9, 0, 0},
+	{kColorPLens, 4, 4, 5, 11, 10, 0, 0}, {kColorPLens, 4, 5, 0, 6, 11, 0, 0},
+	{kColorBlack, 6, 6, 7, 8, 9, 10, 11}
+};
+static const Colony::ColonyEngine::PrismPartDef kProjectorParts[3] = {
+	{8, kProjectorPts, 5, kProjectorSurf},
+	{4, kPStandPts, 1, kPStandSurf},
+	{12, kPLensPts, 7, kPLensSurf}
+};
+
 
 void ColonyEngine::quadrant() {
 	int remain;
@@ -1322,8 +1497,10 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 			draw3DPrism(obj, kDrawerParts[i], false);
 		break;
 	case kObjFWall:
+		draw3DPrism(obj, kFWallPart, false);
+		break;
 	case kObjCWall:
-		draw3DPrism(obj, kCWallParts[0], false);
+		draw3DPrism(obj, kCWallPart, false);
 		break;
 	case kObjScreen:
 		draw3DPrism(obj, kScreenPart, false);
@@ -1347,8 +1524,8 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		draw3DPrism(obj, kBox1Part, false);
 		break;
 	case kObjBox2:
-		for (int i = 0; i < 2; i++)
-			draw3DPrism(obj, kBox2Parts[i], false);
+		draw3DPrism(obj, kBox2Parts[1], false); // base first
+		draw3DPrism(obj, kBox2Parts[0], false); // top second
 		break;
 	case kObjReactor:
 		for (int i = 0; i < 3; i++)
@@ -1359,7 +1536,19 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 			draw3DPrism(obj, kPowerSuitParts[i], false);
 		break;
 	case kObjTeleport:
-		draw3DPrism(obj, kCWallParts[0], false); // Placeholder
+		draw3DPrism(obj, kTelePart, false);
+		break;
+	case kObjCryo:
+		draw3DPrism(obj, kCryoParts[1], false); // base first
+		draw3DPrism(obj, kCryoParts[0], false); // top second
+		break;
+	case kObjProjector:
+		// Projector sits on table — draw table first, then projector parts
+		for (int i = 0; i < 2; i++)
+			draw3DPrism(obj, kTableParts[i], false);
+		draw3DPrism(obj, kProjectorParts[1], false); // stand
+		draw3DPrism(obj, kProjectorParts[0], false); // body
+		draw3DPrism(obj, kProjectorParts[2], false); // lens
 		break;
 	case kObjTub:
 		for (int i = 0; i < 2; i++)
@@ -1378,7 +1567,13 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 			draw3DPrism(obj, kPToiletParts[i], false);
 		break;
 	case kObjForkLift:
-		draw3DPrism(obj, kBox1Part, false); // Placeholder
+		// Default draw order: forks, arms, treads, cab (back-to-front)
+		draw3DPrism(obj, kForkliftParts[3], false); // FLLL (left fork)
+		draw3DPrism(obj, kForkliftParts[2], false); // FLUL (left arm)
+		draw3DPrism(obj, kForkliftParts[5], false); // FLLR (right fork)
+		draw3DPrism(obj, kForkliftParts[4], false); // FLUR (right arm)
+		draw3DPrism(obj, kForkliftParts[1], false); // treads
+		draw3DPrism(obj, kForkliftParts[0], false); // cab
 		break;
 	default:
 		return false;


Commit: 191556528b0a9b63bf5a28a3f04ab8812b32db79
    https://github.com/scummvm/scummvm/commit/191556528b0a9b63bf5a28a3f04ab8812b32db79
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:20+02:00

Commit Message:
COLONY: frame limit

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 20f8e9b9ec5..b823e9df46f 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -46,6 +46,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_level = 0;
 	_robotNum = 0;
 	_gfx = nullptr;
+	_frameLimiter = nullptr;
 	_width = 640;
 	_height = 350;
 	_centerX = _width / 2;
@@ -112,6 +113,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 
 ColonyEngine::~ColonyEngine() {
 	deleteAnimation();
+	delete _frameLimiter;
 	delete _gfx;
 	delete _sound;
 }
@@ -234,6 +236,9 @@ Common::Error ColonyEngine::run() {
 	}
 	_gfx->setPalette(pal, 0, 256);
 
+	// Frame limiter: target 60fps, like Freescape engine
+	_frameLimiter = new Graphics::FrameLimiter(_system, 60);
+
 	scrollInfo();
 
 	loadMap(1); // Try to load the first map
@@ -243,6 +248,7 @@ Common::Error ColonyEngine::run() {
 	int mouseDX = 0, mouseDY = 0;
 	bool mouseMoved = false;
 	while (!shouldQuit()) {
+		_frameLimiter->startFrame();
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
 				if (event.type == Common::EVENT_KEYDOWN) {
@@ -374,9 +380,10 @@ Common::Error ColonyEngine::run() {
 		corridor();
 		drawDashboardStep1();
 		drawCrosshair();
+		checkCenter();
 		
+		_frameLimiter->delayBeforeSwap();
 		_gfx->copyToScreen();
-		_system->delayMillis(10);
 	}
 
 	return Common::kNoError;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 1e05126ce5a..637495a27e8 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -27,6 +27,7 @@
 #include "common/array.h"
 #include "common/random.h"
 #include "common/rect.h"
+#include "graphics/framelimiter.h"
 #include "colony/gfx.h"
 #include "colony/sound.h"
 
@@ -230,6 +231,8 @@ public:
 	int checkwall(int xnew, int ynew, Locate *pobject);
 	void cCommand(int xnew, int ynew, bool allowInteraction);
 	void scrollInfo();
+	void checkCenter();
+	void fallThroughHole();
 
 	void doText(int entry, int center);
 	void inform(const char *text, bool hold);
@@ -251,6 +254,7 @@ private:
 
 	Renderer *_gfx;
 	Sound *_sound;
+	Graphics::FrameLimiter *_frameLimiter;
 
 
 	int _tsin, _tcos;
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index f0ad9b3f5db..270a35c78eb 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -1065,28 +1065,35 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 	bool ceiling = (map[0] == 3 || map[0] == 4); // SMHOLECEIL, LGHOLECEIL
 	getCellFace3D(cellX, cellY, ceiling, corners);
 
+	// DOS uses color_wall (PenColor) for hole outlines.
+	// In our inverted 3D renderer: lit=black outlines on white fill, dark=white on black.
+	bool lit = (_corePower[_coreIndex] > 0);
+	uint32 holeColor = lit ? 0 : 7;
+
 	switch (map[0]) {
 	case 1: // SMHOLEFLR
 	case 3: // SMHOLECEIL
 	{
-		float u[4] = {0.375f, 0.625f, 0.625f, 0.375f};
-		float v[4] = {0.375f, 0.375f, 0.625f, 0.625f};
-		wallPolygon(corners, u, v, 4, 8); // vDKGRAY outline
+		// DOS floor1hole/ceil1hole: hole spans 25%-75% of cell in each dimension
+		// Matching the CCenter proximity trigger zone (64..192 of 256)
+		float u[4] = {0.25f, 0.75f, 0.75f, 0.25f};
+		float v[4] = {0.25f, 0.25f, 0.75f, 0.75f};
+		wallPolygon(corners, u, v, 4, holeColor);
 		break;
 	}
 	case 2: // LGHOLEFLR
 	case 4: // LGHOLECEIL
 	{
+		// DOS floor2hole/ceil2hole: full-cell hole
 		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-		wallPolygon(corners, u, v, 4, 8); // vDKGRAY outline
+		wallPolygon(corners, u, v, 4, holeColor);
 		break;
 	}
-	case 5: // HOTFOOT
+	case 5: // HOTFOOT — DOS draws X pattern (two diagonals), not a rectangle
 	{
-		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-		wallPolygon(corners, u, v, 4, 4); // vRED outline
+		wallLine(corners, 0.0f, 0.0f, 1.0f, 1.0f, holeColor); // front-left to back-right
+		wallLine(corners, 1.0f, 0.0f, 0.0f, 1.0f, holeColor); // front-right to back-left
 		break;
 	}
 	default:
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index defb22e9da4..96ce2f218a3 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -953,6 +953,122 @@ void ColonyEngine::doText(int entry, int center) {
 	free(page);
 }
 
+void ColonyEngine::fallThroughHole() {
+	// DOS tunnel(TRUE, mapdata) + GoTo(mapdata)
+	// Called when player falls through a floor hole (SMHOLEFLR or LGHOLEFLR)
+	const uint8 *mapdata = _mapData[_me.xindex][_me.yindex][4];
+	int targetMap = mapdata[2];
+	int targetX = mapdata[3];
+	int targetY = mapdata[4];
+
+	if (targetMap == 0 && targetX == 0 && targetY == 0) {
+		terminateGame(false); // you're dead
+		return;
+	}
+
+	// DOS tunnel(TRUE,...): power damage from falling
+	int damage = -(_level << 7);
+	for (int i = 0; i < 3; i++)
+		_corePower[i] += damage;
+
+	_sound->play(Sound::kClatter);
+
+	// DOS tunnel(pt=TRUE): falling animation — nested rectangles shrinking toward
+	// center, simulating falling down a shaft. White outlines on black background.
+	// DOS runs 10 steps × 2 frames = 20 display frames at ~15fps = ~1.3 seconds.
+	// At 60fps we use 80 frames for the same duration, paced by the frame limiter.
+	{
+		const int cx = (_screenR.left + _screenR.right) / 2;
+		const int cy = (_screenR.top + _screenR.bottom) / 2;
+		const int hw = _screenR.width() / 2;
+		const int hh = _screenR.height() / 2;
+		const int totalFrames = 80;
+		const int maxRings = 10;
+
+		for (int frame = 0; frame < totalFrames && !shouldQuit(); frame++) {
+			_frameLimiter->startFrame();
+
+			_gfx->fillRect(_screenR, 0); // black background
+
+			float progress = (float)frame / totalFrames;
+
+			// Draw nested rectangles — outer ring shrinks in, inner rings follow
+			// The number of visible rings decreases as we fall deeper
+			int visibleRings = maxRings - (int)(progress * (maxRings - 1));
+			for (int ring = 0; ring < visibleRings; ring++) {
+				// Each ring's depth combines the overall fall progress with per-ring spacing
+				float depth = progress * 0.6f + (float)ring / (maxRings + 2.0f);
+				if (depth >= 1.0f) break;
+				float scale = 1.0f - depth;
+				int rw = (int)(hw * scale);
+				int rh = (int)(hh * scale);
+				if (rw < 2 || rh < 2) break;
+				Common::Rect r(cx - rw, cy - rh, cx + rw, cy + rh);
+				_gfx->drawRect(r, 15); // white outline
+			}
+
+			_frameLimiter->delayBeforeSwap();
+			_gfx->copyToScreen();
+		}
+	}
+
+	// DOS GoTo(): preserve sub-cell offset, move to destination
+	if (targetX > 0 && targetY > 0) {
+		// Don't go if destination is occupied on same level (DOS: (!map) && robotarray check)
+		if (targetMap == 0 && _robotArray[targetX][targetY] != 0)
+			return;
+
+		_robotArray[_me.xindex][_me.yindex] = 0;
+
+		// Preserve sub-cell offset (DOS: xmod = xloc - (xindex<<8))
+		int xmod = _me.xloc - (_me.xindex << 8);
+		int ymod = _me.yloc - (_me.yindex << 8);
+		_me.xloc = (targetX << 8) + xmod;
+		_me.xindex = targetX;
+		_me.yloc = (targetY << 8) + ymod;
+		_me.yindex = targetY;
+
+		_robotArray[targetX][targetY] = MENUM;
+	}
+
+	// DOS: if(map) load_mapnum(map, TRUE) — always reload when map != 0
+	if (targetMap > 0)
+		loadMap(targetMap);
+
+	debug("Fell through hole: level=%d pos=(%d,%d)", _level, _me.xindex, _me.yindex);
+}
+
+void ColonyEngine::checkCenter() {
+	// DOS CCenter(): check if player is standing on a floor hole or hotfoot
+	if (_me.xindex < 0 || _me.xindex >= 31 || _me.yindex < 0 || _me.yindex >= 31)
+		return;
+
+	const uint8 cellType = _mapData[_me.xindex][_me.yindex][4][0];
+	if (cellType == 0)
+		return;
+
+	switch (cellType) {
+	case 1: { // SMHOLEFLR — small floor hole, must be near center
+		// DOS: xcheck=abs(xloc-(xindex<<8)); if(xcheck>64&&xcheck<192)
+		int xcheck = ABS(_me.xloc - (_me.xindex << 8));
+		int ycheck = ABS(_me.yloc - (_me.yindex << 8));
+		if (xcheck > 64 && xcheck < 192 && ycheck > 64 && ycheck < 192)
+			fallThroughHole();
+		break;
+	}
+	case 2: // LGHOLEFLR — large floor hole, full cell
+		fallThroughHole();
+		break;
+	case 5: // HOTFOOT — electric floor, damages power
+		// DOS: SetPower(-(5<<level),-(5<<level),-(5<<level))
+		for (int i = 0; i < 3; i++)
+			_corePower[i] -= (5 << _level);
+		break;
+	default:
+		break;
+	}
+}
+
 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;


Commit: b2a94f4482cd5af82985af6a2b731d16a17911c8
    https://github.com/scummvm/scummvm/commit/b2a94f4482cd5af82985af6a2b731d16a17911c8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:20+02:00

Commit Message:
COLONY: fix airlock door rendering with octagon grid and wireframe outline

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 270a35c78eb..747f5db0436 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -1128,41 +1128,38 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 
 		if (shipLevel) {
 			// Octagonal ship door (SS door)
+			// DOS split7x7 grid: lr[2,1,1,2,4,5,5,4] ud[0,1,5,6,6,5,1,0][same]
+			// split7 positions: 0=1/8, 1=2/8, 2=3/8, 3=4/8, 4=5/8, 5=6/8, 6=7/8
 			static const float u_ss[8] = { 0.375f, 0.250f, 0.250f, 0.375f, 0.625f, 0.750f, 0.750f, 0.625f };
 			static const float v_ss[8] = { 0.125f, 0.250f, 0.750f, 0.875f, 0.875f, 0.750f, 0.250f, 0.125f };
 
-			if (map[1] == 0) {
-				// Open: dark gray octagonal outline
-				for (int i = 0; i < 8; i++)
-					wallLine(corners, u_ss[i], v_ss[i], u_ss[(i + 1) % 8], v_ss[(i + 1) % 8], doorColor);
-			} else {
-				// Closed: dark gray octagon + inner panel
-				for (int i = 0; i < 8; i++)
-					wallLine(corners, u_ss[i], v_ss[i], u_ss[(i + 1) % 8], v_ss[(i + 1) % 8], doorColor);
+			// DOS wireframe: octagon outline in vDKGRAY (both open and closed)
+			for (int i = 0; i < 8; i++)
+				wallLine(corners, u_ss[i], v_ss[i], u_ss[(i + 1) % 8], v_ss[(i + 1) % 8], doorColor);
+
+			if (map[1] != 0) {
+				// Closed: add inner rectangle panel (lr[2]..lr[4] × ud[1]..ud[5])
 				wallLine(corners, 0.375f, 0.25f, 0.375f, 0.75f, doorColor);
-				wallLine(corners, 0.625f, 0.25f, 0.625f, 0.75f, doorColor);
-				wallLine(corners, 0.375f, 0.25f, 0.625f, 0.25f, doorColor);
 				wallLine(corners, 0.375f, 0.75f, 0.625f, 0.75f, doorColor);
+				wallLine(corners, 0.625f, 0.75f, 0.625f, 0.25f, doorColor);
+				wallLine(corners, 0.625f, 0.25f, 0.375f, 0.25f, doorColor);
 			}
 		} else {
 			// Standard rectangular door (Lab levels)
-			float xl = 0.25f, xr = 0.75f;
-			float yb = 0.125f, yt = 0.875f;
-
-			if (map[1] == 0) {
-				// Open: dark gray door outline + floor perspective quad
-				wallLine(corners, xl, yb, xl, yt, doorColor);
-				wallLine(corners, xl, yt, xr, yt, doorColor);
-				wallLine(corners, xr, yt, xr, yb, doorColor);
-				wallLine(corners, xr, yb, xl, yb, doorColor);
-			} else {
-				// Closed: dark gray door outline + handle
-				wallLine(corners, xl, yb, xl, yt, doorColor);
-				wallLine(corners, xl, yt, xr, yt, doorColor);
-				wallLine(corners, xr, yt, xr, yb, doorColor);
-				wallLine(corners, xr, yb, xl, yb, doorColor);
-				// Handle
-				wallLine(corners, xr - 0.15f, 0.45f, xr - 0.05f, 0.55f, doorColor);
+			// DOS: xx1 = mid(center,left) = 0.25, xx2 = mid(center,right) = 0.75
+			// ybl goes to floor (v=0), ytl = 7/8*ceil + 1/8*floor (v=0.875)
+			static const float xl = 0.25f, xr = 0.75f;
+			static const float yb = 0.0f, yt = 0.875f;
+
+			// DOS wireframe: door outline in vDKGRAY (both open and closed)
+			wallLine(corners, xl, yb, xl, yt, doorColor);
+			wallLine(corners, xl, yt, xr, yt, doorColor);
+			wallLine(corners, xr, yt, xr, yb, doorColor);
+			wallLine(corners, xr, yb, xl, yb, doorColor);
+
+			if (map[1] != 0) {
+				// Closed: add handle bar at midpoint of door
+				wallLine(corners, 0.3125f, 0.4375f, 0.6875f, 0.4375f, doorColor);
 			}
 		}
 		break;


Commit: 7b4726fe2d76bcc4c6d0fff0e147ea72c0c0f0b7
    https://github.com/scummvm/scummvm/commit/7b4726fe2d76bcc4c6d0fff0e147ea72c0c0f0b7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:21+02:00

Commit Message:
COLONY: mac render mode

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/detection.cpp
    engines/colony/render.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index b823e9df46f..7ad0d5147ff 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -60,6 +60,14 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_weapons = 0;
 	_wireframe = false;
 	_widescreen = ConfMan.getBool("widescreen_mod");
+
+	// Render mode: EGA (DOS wireframe default) or Macintosh (filled polygons)
+	if (!ConfMan.hasKey("render_mode") || ConfMan.get("render_mode").empty())
+		_renderMode = Common::kRenderEGA;
+	else
+		_renderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
+	if (_renderMode == Common::kRenderDefault)
+		_renderMode = Common::kRenderEGA;
 	_speedShift = 2; // DOS default: speedshift=1, but 2 feels better with our frame rate
 	
 	memset(_wall, 0, sizeof(_wall));
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 637495a27e8..5590cda5231 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -28,6 +28,7 @@
 #include "common/random.h"
 #include "common/rect.h"
 #include "graphics/framelimiter.h"
+#include "common/rendermode.h"
 #include "colony/gfx.h"
 #include "colony/sound.h"
 
@@ -255,6 +256,7 @@ private:
 	Renderer *_gfx;
 	Sound *_sound;
 	Graphics::FrameLimiter *_frameLimiter;
+	Common::RenderMode _renderMode;
 
 
 	int _tsin, _tcos;
diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
index be227bd58b3..92dfcb5fe42 100644
--- a/engines/colony/detection.cpp
+++ b/engines/colony/detection.cpp
@@ -37,7 +37,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUIO0()
+		GUIO2(GUIO_RENDEREGA, GUIO_RENDERMACINTOSH)
 	},
 	AD_TABLE_END_MARKER
 };
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 747f5db0436..76ba3c1de23 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -91,6 +91,90 @@ static uint8 lookupLineColor(int colorIdx) {
 	}
 }
 
+// Mac Classic dither patterns (from colorize.c cColor[].pattern).
+// Mac Classic was a 1-bit B&W display. QuickDraw used 8x8 dither patterns
+// to simulate grayscale: WHITE, LTGRAY, GRAY, DKGRAY, BLACK, CLEAR.
+// Rendered via GL_POLYGON_STIPPLE: two-pass fill (white bg + black fg with stipple mask).
+enum MacPattern {
+	kPatternWhite  = 0, // Solid white (all background)
+	kPatternLtGray = 1, // 25% foreground (sparse black dots on white)
+	kPatternGray   = 2, // 50% foreground (checkerboard)
+	kPatternDkGray = 3, // 75% foreground (dense black, sparse white)
+	kPatternBlack  = 4, // Solid black (all foreground)
+	kPatternClear  = 5  // Outline only (no fill)
+};
+
+// GL_POLYGON_STIPPLE patterns: 32x32 bit arrays (128 bytes), tiled from 8x8 Mac patterns.
+// Bit=1 → fragment drawn (black foreground), bit=0 → fragment discarded (white background shows).
+// Mac QuickDraw convention: bit=1 = foreground (BLACK), bit=0 = background (WHITE).
+static const byte kStippleLtGray[128] = {
+	// 0x88,0x22 alternating rows = 25% coverage (sparse dots)
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+};
+
+static const byte kStippleGray[128] = {
+	// 0xAA,0x55 alternating rows = 50% coverage (checkerboard)
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+};
+
+static const byte kStippleDkGray[128] = {
+	// 0x77,0xDD alternating rows = 75% coverage (dense dots)
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+};
+
+// Lookup: MacPattern enum → GL stipple data pointer (null for solid/clear patterns)
+static const byte *kMacStippleData[] = {
+	nullptr,        // kPatternWhite  - solid white, no stipple
+	kStippleLtGray, // kPatternLtGray - 25% black dots
+	kStippleGray,   // kPatternGray   - 50% checkerboard
+	kStippleDkGray, // kPatternDkGray - 75% black
+	nullptr,        // kPatternBlack  - solid black, no stipple
+	nullptr         // kPatternClear  - outline only
+};
+
+// Map ObjColor constant → Mac dither pattern.
+// From colorize.c cColor[] array: ~90% of objects use GRAY,
+// special cases: c_dwall=WHITE, c_lwall=LTGRAY, c_window=DKGRAY,
+// c_desktop=WHITE, c_shelves=LTGRAY.
+static int lookupMacPattern(int colorIdx) {
+	switch (colorIdx) {
+	case kColorClear:     return kPatternClear;
+	case kColorBlack:     return kPatternBlack;
+	case kColorWall:      return kPatternWhite;  // c_dwall = WHITE
+	case kColorDeskTop:   return kPatternWhite;  // c_desktop = WHITE
+	case kColorSheet:     return kPatternWhite;  // c_bedsheet = WHITE (bright surface)
+	case kColorBath:      return kPatternWhite;  // c_bath = WHITE (porcelain)
+	case kColorMac:       return kPatternWhite;  // c_computer = WHITE (bright casing)
+	case kColorSilver:    return kPatternLtGray; // c_mirror = LTGRAY
+	case kColorReactor:   return kPatternLtGray; // c_reactor = LTGRAY
+	case kColorTVScreen:  return kPatternDkGray; // c_tvscreen = DKGRAY
+	case kColorMacScreen: return kPatternDkGray; // c_screen = DKGRAY
+	case kColorWater:     return kPatternDkGray; // c_water = DKGRAY
+	default:              return kPatternGray;   // Most objects = GRAY
+	}
+}
+
 // DOS object geometry constants
 static const int kScreenPts[8][3] = {
 	{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
@@ -820,10 +904,28 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 		}
 
 		if (count >= 3) {
-			// polyfill=False mode: wireframe fill stays as wall background,
-			// only outline color changes per surface (LINECOLOR from lsColor)
-			uint32 lineColor = lit ? (uint32)lookupLineColor(colorIdx) : 15;
-			_gfx->draw3DPolygon(px, py, pz, count, lineColor);
+			if (colorIdx == kColorClear)
+				continue; // Transparent surface, skip
+
+			if (lit) {
+				if (_renderMode == Common::kRenderMacintosh) {
+					// Mac B&W: stipple dither pattern fill + black outline
+					int pattern = lookupMacPattern(colorIdx);
+					if (pattern == kPatternClear) continue;
+					if (!_wireframe) {
+						_gfx->setStippleData(kMacStippleData[pattern]);
+						_gfx->setWireframe(true, pattern == kPatternBlack ? 0 : 255);
+					}
+					_gfx->draw3DPolygon(px, py, pz, count, 0); // black outline
+					_gfx->setStippleData(nullptr);
+				} else {
+					// EGA: global wall fill, per-surface colored outline
+					_gfx->draw3DPolygon(px, py, pz, count, (uint32)lookupLineColor(colorIdx));
+				}
+			} else {
+				// Unlit: same for both modes — black fill, white outline
+				_gfx->draw3DPolygon(px, py, pz, count, 15);
+			}
 		}
 	}
 }
@@ -835,7 +937,8 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 	const long rotCos = _cost[ang];
 	const long rotSin = _sint[ang];
 	const bool lit = (_corePower[_coreIndex] > 0);
-	const uint32 color = lit ? 2 : 15; // vGREEN when lit, white when dark
+	// Mac B&W: black outlines; EGA: green outlines; unlit: white
+	const uint32 color = lit ? (_renderMode == Common::kRenderMacintosh ? 0 : 2) : 15;
 
 	for (int i = 0; i < def.surfaceCount; i++) {
 		const int n = def.surfaces[i][1];
@@ -1033,7 +1136,14 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 			u[i] = 0.2f + (data[1 + i*2] / 6.0f) * 0.6f;
 			v[i] = 0.2f + (data[2 + i*2] / 6.0f) * 0.6f;
 		}
-		// Draw arrow as outline only (wireframe mode)
+		// Mac: fill arrow polygon with BLACK.
+		// drawchar() sets cColor[cc].pattern=WHITE (all background) and
+		// background is {0,0,0}=BLACK, so SuperPoly fills solid black.
+		if (_renderMode == Common::kRenderMacintosh) {
+			_gfx->setWireframe(true, 0); // BLACK fill
+			wallPolygon(corners, u, v, count, 0);
+			_gfx->setWireframe(true, 255); // restore white wall fill
+		}
 		for (int i = 0; i < count; i++) {
 			int n = (i + 1) % count;
 			wallLine(corners, u[i], v[i], u[n], v[n], 0); // vBLACK
@@ -1122,72 +1232,106 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 
 	switch (map[0]) {
 	case kWallFeatureDoor: {
-		// DOS wireframe: all doors drawn with PenColor(realcolor[vDKGRAY]) = 8
-		const uint32 doorColor = 8; // vDKGRAY
+		// EGA: vDKGRAY outlines; Mac B&W: black outlines + gray fills
+		const bool macMode = (_renderMode == Common::kRenderMacintosh);
+		const uint32 doorColor = macMode ? 0 : 8; // Mac: black, EGA: vDKGRAY
 		bool shipLevel = (_level == 1 || _level == 5 || _level == 6);
 
 		if (shipLevel) {
-			// Octagonal ship door (SS door)
-			// DOS split7x7 grid: lr[2,1,1,2,4,5,5,4] ud[0,1,5,6,6,5,1,0][same]
-			// split7 positions: 0=1/8, 1=2/8, 2=3/8, 3=4/8, 4=5/8, 5=6/8, 6=7/8
 			static const float u_ss[8] = { 0.375f, 0.250f, 0.250f, 0.375f, 0.625f, 0.750f, 0.750f, 0.625f };
 			static const float v_ss[8] = { 0.125f, 0.250f, 0.750f, 0.875f, 0.875f, 0.750f, 0.250f, 0.125f };
 
-			// DOS wireframe: octagon outline in vDKGRAY (both open and closed)
+			if (macMode) {
+				if (map[1] != 0) {
+					// Closed: fill octagon (c_bulkhead = GRAY stipple)
+					_gfx->setStippleData(kStippleGray);
+					wallPolygon(corners, u_ss, v_ss, 8, 0);
+					_gfx->setStippleData(nullptr);
+				} else {
+					// Open: fill with BLACK (passable opening)
+					_gfx->setWireframe(true, 0);
+					wallPolygon(corners, u_ss, v_ss, 8, 0);
+					_gfx->setWireframe(true, 255);
+				}
+			}
+
 			for (int i = 0; i < 8; i++)
 				wallLine(corners, u_ss[i], v_ss[i], u_ss[(i + 1) % 8], v_ss[(i + 1) % 8], doorColor);
 
 			if (map[1] != 0) {
-				// Closed: add inner rectangle panel (lr[2]..lr[4] × ud[1]..ud[5])
 				wallLine(corners, 0.375f, 0.25f, 0.375f, 0.75f, doorColor);
 				wallLine(corners, 0.375f, 0.75f, 0.625f, 0.75f, doorColor);
 				wallLine(corners, 0.625f, 0.75f, 0.625f, 0.25f, doorColor);
 				wallLine(corners, 0.625f, 0.25f, 0.375f, 0.25f, doorColor);
 			}
 		} else {
-			// Standard rectangular door (Lab levels)
-			// DOS: xx1 = mid(center,left) = 0.25, xx2 = mid(center,right) = 0.75
-			// ybl goes to floor (v=0), ytl = 7/8*ceil + 1/8*floor (v=0.875)
 			static const float xl = 0.25f, xr = 0.75f;
 			static const float yb = 0.0f, yt = 0.875f;
 
-			// DOS wireframe: door outline in vDKGRAY (both open and closed)
+			if (macMode) {
+				float ud[4] = {xl, xr, xr, xl};
+				float vd[4] = {yb, yb, yt, yt};
+				if (map[1] != 0) {
+					// Closed: fill (c_door = GRAY stipple)
+					_gfx->setStippleData(kStippleGray);
+					wallPolygon(corners, ud, vd, 4, 0);
+					_gfx->setStippleData(nullptr);
+				} else {
+					// Open: fill with BLACK (passable opening)
+					_gfx->setWireframe(true, 0);
+					wallPolygon(corners, ud, vd, 4, 0);
+					_gfx->setWireframe(true, 255);
+				}
+			}
+
 			wallLine(corners, xl, yb, xl, yt, doorColor);
 			wallLine(corners, xl, yt, xr, yt, doorColor);
 			wallLine(corners, xr, yt, xr, yb, doorColor);
 			wallLine(corners, xr, yb, xl, yb, doorColor);
 
 			if (map[1] != 0) {
-				// Closed: add handle bar at midpoint of door
 				wallLine(corners, 0.3125f, 0.4375f, 0.6875f, 0.4375f, doorColor);
 			}
 		}
 		break;
 	}
 	case kWallFeatureWindow: {
-		// DOS drawwind: window pane at 1/4..3/4 with vertical + horizontal center lines
-		const uint32 winColor = 8; // vDKGRAY
+		const bool macMode = (_renderMode == Common::kRenderMacintosh);
+		const uint32 winColor = macMode ? 0 : 8; // Mac: black, EGA: vDKGRAY
 		float xl = 0.25f, xr = 0.75f;
 		float yb = 0.25f, yt = 0.75f;
 		float xc = 0.5f, yc = 0.5f;
-		// Window frame
+
+		// Mac: fill window pane (c_window = DKGRAY stipple)
+		if (macMode) {
+			_gfx->setStippleData(kStippleDkGray);
+			float uw[4] = {xl, xr, xr, xl};
+			float vw[4] = {yb, yb, yt, yt};
+			wallPolygon(corners, uw, vw, 4, 0);
+			_gfx->setStippleData(nullptr);
+		}
+
 		wallLine(corners, xl, yb, xl, yt, winColor);
 		wallLine(corners, xl, yt, xr, yt, winColor);
 		wallLine(corners, xr, yt, xr, yb, winColor);
 		wallLine(corners, xr, yb, xl, yb, winColor);
-		// Mullion (vertical center) and transom (horizontal center)
 		wallLine(corners, xc, yb, xc, yt, winColor);
 		wallLine(corners, xl, yc, xr, yc, winColor);
 		break;
 	}
 	case kWallFeatureShelves: {
 		// DOS drawbooks: recessed bookcase with 3D depth.
-		// The depth effect uses actual back-wall coordinates in DOS, which we can't
-		// replicate with UV mapping on a single wall face. We draw:
-		// - Back face rectangle (inset)
-		// - 4 connecting lines (front corners to back corners)
-		// - 7 horizontal shelf lines across the front face (full width)
-		const uint32 shelfColor = 8; // vDKGRAY
+		const bool macMode = (_renderMode == Common::kRenderMacintosh);
+		const uint32 shelfColor = macMode ? 0 : 8;
+
+		// Mac: fill shelves area (c_shelves = LTGRAY stipple)
+		if (macMode) {
+			_gfx->setStippleData(kStippleLtGray);
+			float us[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+			float vs[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+			wallPolygon(corners, us, vs, 4, 0);
+			_gfx->setStippleData(nullptr);
+		}
 		float bx = 0.1875f, bxr = 0.8125f;
 		float by = 0.1875f, byt = 0.8125f;
 		// Back face rectangle
@@ -1209,10 +1353,17 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	}
 	case kWallFeatureUpStairs: {
 		// DOS: draw_up_stairs — staircase ascending into the wall with perspective
-		// Uses split7 subdivision (7 depth levels at fractions 1/8..7/8)
-		// Passage narrows toward vanishing point (0.5, 0.5)
 		const uint32 col = 0; // vBLACK
 
+		// Mac: fill entire wall face (c_upstairs = GRAY stipple)
+		if (_renderMode == Common::kRenderMacintosh) {
+			_gfx->setStippleData(kStippleGray);
+			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+			float vf[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+			wallPolygon(corners, uf, vf, 4, 0);
+			_gfx->setStippleData(nullptr);
+		}
+
 		// Perspective convergence: back of passage at ~1/3 width (1 cell deep)
 		float ul[7], ur[7], vf[7], vc[7];
 		for (int i = 0; i < 7; i++) {
@@ -1275,9 +1426,17 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	}
 	case kWallFeatureDnStairs: {
 		// DOS: draw_dn_stairs — staircase descending into the wall with perspective
-		// Simpler than up stairs: ceiling slopes down, side walls, first step, handrails
 		const uint32 col = 0; // vBLACK
 
+		// Mac: fill entire wall face (c_dnstairs = GRAY stipple)
+		if (_renderMode == Common::kRenderMacintosh) {
+			_gfx->setStippleData(kStippleGray);
+			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+			float vf[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+			wallPolygon(corners, uf, vf, 4, 0);
+			_gfx->setStippleData(nullptr);
+		}
+
 		float ul[7], ur[7], vf[7], vc[7];
 		for (int i = 0; i < 7; i++) {
 			float f = (i + 1) / 8.0f;
@@ -1320,7 +1479,18 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	case kWallFeatureGlyph: {
 		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
-		const uint32 glyphColor = 8; // vDKGRAY
+		const bool macMode = (_renderMode == Common::kRenderMacintosh);
+		const uint32 glyphColor = macMode ? 0 : 8;
+
+		// Mac: fill glyph area (c_glyph = GRAY stipple)
+		if (macMode) {
+			_gfx->setStippleData(kStippleGray);
+			float ug[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+			float vg[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+			wallPolygon(corners, ug, vg, 4, 0);
+			_gfx->setStippleData(nullptr);
+		}
+
 		for (int i = 0; i < 7; i++) {
 			float v = 0.2f + i * 0.1f;
 			wallLine(corners, 0.2f, v, 0.8f, v, glyphColor);
@@ -1328,10 +1498,20 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	}
 	case kWallFeatureElevator: {
-		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
-		const uint32 elevColor = 8; // vDKGRAY
+		const bool macMode = (_renderMode == Common::kRenderMacintosh);
+		const uint32 elevColor = macMode ? 0 : 8; // Mac: black, EGA: vDKGRAY
 		float xl = 0.2f, xr = 0.8f;
 		float yb = 0.1f, yt = 0.9f;
+
+		// Mac: fill elevator door (c_elevator = GRAY stipple)
+		if (macMode) {
+			_gfx->setStippleData(kStippleGray);
+			float ue[4] = {xl, xr, xr, xl};
+			float ve[4] = {yb, yb, yt, yt};
+			wallPolygon(corners, ue, ve, 4, 0);
+			_gfx->setStippleData(nullptr);
+		}
+
 		wallLine(corners, xl, yb, xl, yt, elevColor);
 		wallLine(corners, xl, yt, xr, yt, elevColor);
 		wallLine(corners, xr, yt, xr, yb, elevColor);
@@ -1343,11 +1523,18 @@ 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)
 		static const float u_t[6] = { 0.0f,    0.0f,    1/6.0f,  5/6.0f,  1.0f,    1.0f };
 		static const float v_t[6] = { 0.0f,    0.750f,  0.875f,  0.875f,  0.750f,  0.0f };
-		wallPolygon(corners, u_t, v_t, 6, 0); // vBLACK outline
+		if (_renderMode == Common::kRenderMacintosh) {
+			// Mac: c_tunnel = GRAY stipple fill + black outline
+			_gfx->setStippleData(kStippleGray);
+			wallPolygon(corners, u_t, v_t, 6, 0);
+			_gfx->setStippleData(nullptr);
+		} else {
+			wallPolygon(corners, u_t, v_t, 6, 0); // vBLACK outline
+		}
 		break;
 	}
 	case kWallFeatureAirlock: {
-		// DOS wireframe: open=vBLACK(0), closed=vDKGRAY(8)
+		const bool macMode = (_renderMode == Common::kRenderMacintosh);
 		float pts[][2] = {{0.0f, 0.5f}, {0.15f, 0.85f}, {0.5f, 1.0f}, {0.85f, 0.85f},
 		                  {1.0f, 0.5f}, {0.85f, 0.15f}, {0.5f, 0.0f}, {0.15f, 0.15f}};
 		float u[8], v[8];
@@ -1357,16 +1544,33 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		}
 		if (map[1] == 0) {
 			// Open: black fill (passable opening)
-			wallPolygon(corners, u, v, 8, 0); // vBLACK outline
+			_gfx->setWireframe(true, 0);
+			wallPolygon(corners, u, v, 8, 0);
+			bool lit = (_corePower[_coreIndex] > 0);
+			_gfx->setWireframe(true, lit ? (macMode ? 255 : 7) : 0);
 		} else {
-			// Closed: dark gray wireframe outline + cross
-			const uint32 airlockColor = 8; // vDKGRAY
+			// Mac: fill airlock (c_airlock = GRAY stipple) when closed
+			if (macMode) {
+				_gfx->setStippleData(kStippleGray);
+				wallPolygon(corners, u, v, 8, 0);
+				_gfx->setStippleData(nullptr);
+			}
+
+			const uint32 airlockColor = macMode ? 0 : 8; // Mac: black, EGA: vDKGRAY
 			for (int i = 0; i < 8; i++) {
 				int n = (i + 1) % 8;
 				wallLine(corners, u[i], v[i], u[n], v[n], airlockColor);
 			}
-			wallLine(corners, 0.1f, 0.5f, 0.9f, 0.5f, airlockColor);
-			wallLine(corners, 0.5f, 0.1f, 0.5f, 0.9f, airlockColor);
+			if (macMode) {
+				// Mac: 8 radial lines from each vertex to center (drawALClosed)
+				float cu = 0.5f, cv = 0.5f;
+				for (int i = 0; i < 8; i++)
+					wallLine(corners, u[i], v[i], cu, cv, airlockColor);
+			} else {
+				// EGA: simple crosshairs
+				wallLine(corners, 0.1f, 0.5f, 0.9f, 0.5f, airlockColor);
+				wallLine(corners, 0.5f, 0.1f, 0.5f, 0.9f, airlockColor);
+			}
 		}
 		break;
 	}
@@ -1409,27 +1613,34 @@ void ColonyEngine::renderCorridor3D() {
 	computeVisibleCells();
 
 	bool lit = (_corePower[_coreIndex] > 0);
+	bool macMode = (_renderMode == Common::kRenderMacintosh);
+
+	// Mac B&W: walls are pure white (c_dwall=WHITE); EGA: light gray (7)
+	uint32 wallFill = lit ? (macMode ? 255 : 7) : 0;
+	uint32 wallLine = lit ? 0 : (macMode ? 255 : 7);
 
 	// Walls always use wireframe with fill (opaque walls).
-	_gfx->setWireframe(true, lit ? 7 : 0);
+	_gfx->setWireframe(true, wallFill);
 
 	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
-	_gfx->clear(lit ? 7 : 0);
+	_gfx->clear(wallFill);
 
-	uint32 wallColor = lit ? 0 : 7;
-	uint32 floorColor = lit ? 0 : 7;
+	uint32 wallColor = wallLine;
+	// Mac: floor = black (c_lwall foreground), ceiling = white (c_lwall background)
+	// EGA: both use wallLine color
+	uint32 floorColor = macMode ? (lit ? 0 : 255) : wallLine;
+	uint32 ceilColor  = macMode ? (lit ? 255 : 0) : wallLine;
 
 	// Draw large floor and ceiling quads
-	// These will be filled with the background color in the occlusion pass
-	_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.1f, 
-	                100000.0f, -100000.0f, -160.1f, 
-	                100000.0f, 100000.0f, -160.1f, 
+	_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.1f,
+	                100000.0f, -100000.0f, -160.1f,
+	                100000.0f, 100000.0f, -160.1f,
 	                -100000.0f, 100000.0f, -160.1f, floorColor);
- 
-	_gfx->draw3DQuad(-100000.0f, -100000.0f, 160.1f, 
-	                100000.0f, -100000.0f, 160.1f, 
-	                100000.0f, 100000.0f, 160.1f, 
-	                -100000.0f, 100000.0f, 160.1f, floorColor);
+
+	_gfx->draw3DQuad(-100000.0f, -100000.0f, 160.1f,
+	                100000.0f, -100000.0f, 160.1f,
+	                100000.0f, 100000.0f, 160.1f,
+	                -100000.0f, 100000.0f, 160.1f, ceilColor);
  
 	// Draw ceiling grid (Cuadricule) - Historically only on ceiling
 	for (int i = 0; i <= 32; i++) {
@@ -1455,15 +1666,16 @@ void ColonyEngine::renderCorridor3D() {
 	
 	drawWallFeatures3D();
 
-	// F8 toggles polyfill for objects only (DOS doFunctionKey case 8).
-	// Non-fill: objects are outline-only (see-through). Walls stay filled.
+	// F7 toggles object fill.
+	// EGA: default is filled (wall background); F7 = outline-only (see-through).
+	// Mac: default is per-surface fill; F7 = "Fast mode" (outline-only).
 	if (_wireframe) {
 		_gfx->setWireframe(true); // No fill = outline-only objects
 	}
 	drawStaticObjects();
-	if (_wireframe) {
-		_gfx->setWireframe(true, lit ? 7 : 0); // Restore wall fill
-	}
+	// Always restore wall fill after objects.
+	// Mac mode's draw3DPrism changes fill per surface; must reset for subsequent rendering.
+	_gfx->setWireframe(true, wallFill);
 
 	_gfx->end3D();
 	_gfx->setWireframe(false);
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index 9c857d4866c..6037c502291 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -60,6 +60,7 @@ public:
 	// Buffer management
 	virtual void copyToScreen() = 0;
 	virtual void setWireframe(bool enable, int fillColor = -1) = 0;
+	virtual void setStippleData(const byte *data) {}
 	virtual void computeScreenViewport() = 0;
 
 	// Convenience color accessors
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 01b0a832210..b62049f48c5 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -63,6 +63,9 @@ public:
 		_wireframe = enable;
 		_wireframeFillColor = fillColor;
 	}
+	void setStippleData(const byte *data) override {
+		_stippleData = data;
+	}
 	void computeScreenViewport() override;
 
 private:
@@ -74,12 +77,14 @@ private:
 	byte _palette[256 * 3];
 	bool _wireframe;
 	int _wireframeFillColor; // -1 = no fill (outline only)
+	const byte *_stippleData; // GL_POLYGON_STIPPLE pattern (128 bytes), null = disabled
 	Common::Rect _screenViewport;
 };
 
 OpenGLRenderer::OpenGLRenderer(OSystem *system, int width, int height) : _system(system), _width(width), _height(height) {
 	_wireframe = true;
 	_wireframeFillColor = 0;
+	_stippleData = nullptr;
 	memset(_palette, 0, sizeof(_palette));
 	
 	// Default to white for initial colors if setPalette isn't called yet
@@ -236,21 +241,36 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 	float fy2 = y2 * 256.0f;
 
 	if (_wireframe) {
-		if (_wireframeFillColor >= 0) {
-			// Pass 1: Draw background fill (pushed back)
+		if (_wireframeFillColor >= 0 || _stippleData) {
 			glEnable(GL_POLYGON_OFFSET_FILL);
 			glPolygonOffset(1.1f, 4.0f);
-			useColor((uint32)_wireframeFillColor);
-			glBegin(GL_QUADS);
-			glVertex3f(fx1, fy1, -160.0f);
-			glVertex3f(fx2, fy2, -160.0f);
-			glVertex3f(fx2, fy2, 160.0f);
-			glVertex3f(fx1, fy1, 160.0f);
-			glEnd();
+
+			if (_stippleData) {
+				// Two-pass stipple fill (Mac B&W dither pattern)
+				useColor(255); // White background
+				glBegin(GL_QUADS);
+				glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
+				glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
+				glEnd();
+				glEnable(GL_POLYGON_STIPPLE);
+				glPolygonStipple(_stippleData);
+				useColor(0); // Black foreground through stipple mask
+				glBegin(GL_QUADS);
+				glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
+				glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
+				glEnd();
+				glDisable(GL_POLYGON_STIPPLE);
+			} else {
+				useColor((uint32)_wireframeFillColor);
+				glBegin(GL_QUADS);
+				glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
+				glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
+				glEnd();
+			}
 			glDisable(GL_POLYGON_OFFSET_FILL);
 		}
 
-		// Pass 2: Draw colored wireframe edges
+		// Draw colored wireframe edges
 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 		useColor(color);
 		glBegin(GL_QUADS);
@@ -261,38 +281,63 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 		glEnd();
 		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 	} else {
-		// Normal mode: push the wall face back slightly.
-		// This ensures that wall features drawn later as lines at the same depth correctly win the depth test.
 		glEnable(GL_POLYGON_OFFSET_FILL);
-		glPolygonOffset(1.1f, 4.0f); // Positive pulls AWAY from camera
-		useColor(color);
-		glBegin(GL_QUADS);
-		glVertex3f(fx1, fy1, -160.0f);
-		glVertex3f(fx2, fy2, -160.0f);
-		glVertex3f(fx2, fy2, 160.0f);
-		glVertex3f(fx1, fy1, 160.0f);
-		glEnd();
+		glPolygonOffset(1.1f, 4.0f);
+		if (_stippleData) {
+			useColor(255);
+			glBegin(GL_QUADS);
+			glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
+			glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
+			glEnd();
+			glEnable(GL_POLYGON_STIPPLE);
+			glPolygonStipple(_stippleData);
+			useColor(0);
+			glBegin(GL_QUADS);
+			glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
+			glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
+			glEnd();
+			glDisable(GL_POLYGON_STIPPLE);
+		} else {
+			useColor(color);
+			glBegin(GL_QUADS);
+			glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
+			glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
+			glEnd();
+		}
 		glDisable(GL_POLYGON_OFFSET_FILL);
 	}
 }
  
 void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) {
 	if (_wireframe) {
-		if (_wireframeFillColor >= 0) {
-			// Pass 1: Draw background fill (pushed back)
+		if (_wireframeFillColor >= 0 || _stippleData) {
 			glEnable(GL_POLYGON_OFFSET_FILL);
 			glPolygonOffset(1.1f, 4.0f);
-			useColor((uint32)_wireframeFillColor);
-			glBegin(GL_QUADS);
-			glVertex3f(x1, y1, z1);
-			glVertex3f(x2, y2, z2);
-			glVertex3f(x3, y3, z3);
-			glVertex3f(x4, y4, z4);
-			glEnd();
+
+			if (_stippleData) {
+				useColor(255);
+				glBegin(GL_QUADS);
+				glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2);
+				glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4);
+				glEnd();
+				glEnable(GL_POLYGON_STIPPLE);
+				glPolygonStipple(_stippleData);
+				useColor(0);
+				glBegin(GL_QUADS);
+				glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2);
+				glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4);
+				glEnd();
+				glDisable(GL_POLYGON_STIPPLE);
+			} else {
+				useColor((uint32)_wireframeFillColor);
+				glBegin(GL_QUADS);
+				glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2);
+				glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4);
+				glEnd();
+			}
 			glDisable(GL_POLYGON_OFFSET_FILL);
 		}
 
-		// Pass 2: Draw colored wireframe edges
 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 		useColor(color);
 		glBegin(GL_QUADS);
@@ -303,16 +348,29 @@ void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2
 		glEnd();
 		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 	} else {
-		// Normal mode: push back to allow overlays (like wall features or floor decorations)
 		glEnable(GL_POLYGON_OFFSET_FILL);
 		glPolygonOffset(1.1f, 4.0f);
-		useColor(color);
-		glBegin(GL_QUADS);
-		glVertex3f(x1, y1, z1);
-		glVertex3f(x2, y2, z2);
-		glVertex3f(x3, y3, z3);
-		glVertex3f(x4, y4, z4);
-		glEnd();
+		if (_stippleData) {
+			useColor(255);
+			glBegin(GL_QUADS);
+			glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2);
+			glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4);
+			glEnd();
+			glEnable(GL_POLYGON_STIPPLE);
+			glPolygonStipple(_stippleData);
+			useColor(0);
+			glBegin(GL_QUADS);
+			glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2);
+			glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4);
+			glEnd();
+			glDisable(GL_POLYGON_STIPPLE);
+		} else {
+			useColor(color);
+			glBegin(GL_QUADS);
+			glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2);
+			glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4);
+			glEnd();
+		}
 		glDisable(GL_POLYGON_OFFSET_FILL);
 	}
 }
@@ -321,19 +379,31 @@ void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *
 	if (count < 3) return;
 
 	if (_wireframe) {
-		if (_wireframeFillColor >= 0) {
-			// Pass 1: Draw background fill (pushed back)
+		if (_wireframeFillColor >= 0 || _stippleData) {
 			glEnable(GL_POLYGON_OFFSET_FILL);
 			glPolygonOffset(1.1f, 4.0f);
-			useColor((uint32)_wireframeFillColor);
-			glBegin(GL_POLYGON);
-			for (int i = 0; i < count; i++)
-				glVertex3f(x[i], y[i], z[i]);
-			glEnd();
+
+			if (_stippleData) {
+				useColor(255);
+				glBegin(GL_POLYGON);
+				for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+				glEnd();
+				glEnable(GL_POLYGON_STIPPLE);
+				glPolygonStipple(_stippleData);
+				useColor(0);
+				glBegin(GL_POLYGON);
+				for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+				glEnd();
+				glDisable(GL_POLYGON_STIPPLE);
+			} else {
+				useColor((uint32)_wireframeFillColor);
+				glBegin(GL_POLYGON);
+				for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+				glEnd();
+			}
 			glDisable(GL_POLYGON_OFFSET_FILL);
 		}
 
-		// Pass 2: Draw colored wireframe edges
 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 		useColor(color);
 		glBegin(GL_POLYGON);
@@ -342,14 +412,26 @@ void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *
 		glEnd();
 		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 	} else {
-		// Normal mode: push back
 		glEnable(GL_POLYGON_OFFSET_FILL);
 		glPolygonOffset(1.1f, 4.0f);
-		useColor(color);
-		glBegin(GL_POLYGON);
-		for (int i = 0; i < count; i++)
-			glVertex3f(x[i], y[i], z[i]);
-		glEnd();
+		if (_stippleData) {
+			useColor(255);
+			glBegin(GL_POLYGON);
+			for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+			glEnd();
+			glEnable(GL_POLYGON_STIPPLE);
+			glPolygonStipple(_stippleData);
+			useColor(0);
+			glBegin(GL_POLYGON);
+			for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+			glEnd();
+			glDisable(GL_POLYGON_STIPPLE);
+		} else {
+			useColor(color);
+			glBegin(GL_POLYGON);
+			for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+			glEnd();
+		}
 		glDisable(GL_POLYGON_OFFSET_FILL);
 	}
 }


Commit: 9a7830e8c497fdd4dfa35976d8dc0d88cbd999ec
    https://github.com/scummvm/scummvm/commit/9a7830e8c497fdd4dfa35976d8dc0d88cbd999ec
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:21+02:00

Commit Message:
COLONY: add stipple pattern for wall holes and BFS room visibility culling

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 76ba3c1de23..b8aac3780d3 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -913,9 +913,9 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 					int pattern = lookupMacPattern(colorIdx);
 					if (pattern == kPatternClear) continue;
 					if (!_wireframe) {
-						_gfx->setStippleData(kMacStippleData[pattern]);
 						_gfx->setWireframe(true, pattern == kPatternBlack ? 0 : 255);
 					}
+					_gfx->setStippleData(kMacStippleData[pattern]);
 					_gfx->draw3DPolygon(px, py, pz, count, 0); // black outline
 					_gfx->setStippleData(nullptr);
 				} else {
@@ -975,8 +975,16 @@ void ColonyEngine::computeVisibleCells() {
 	if (px < 0 || px >= 32 || py < 0 || py >= 32)
 		return;
 
-	// BFS from player cell, stopping at walls.
-	// Doors are on walls, so they act as opaque barriers.
+	// Check if a wall feature exists on a cell boundary (either side).
+	// Features like doors define room boundaries and block visibility.
+	auto hasFeatureAt = [this](int x, int y, int dir) -> bool {
+		const uint8 *map = mapFeatureAt(x, y, dir);
+		return map && map[0] != kWallFeatureNone;
+	};
+
+	// BFS from player cell, stopping at walls AND wall features.
+	// This limits visibility to the current "room" — doors, shelves, etc.
+	// act as solid barriers even if the cells are connected via open paths.
 	_visibleCell[px][py] = true;
 	int queueX[1024], queueY[1024];
 	int head = 0, tail = 0;
@@ -989,36 +997,48 @@ void ColonyEngine::computeVisibleCells() {
 		int cy = queueY[head];
 		head++;
 
-		// North: (cx, cy+1) — wall check: _wall[cx][cy+1] & 0x01
+		// North: (cx, cy+1)
 		if (cy + 1 < 32 && !_visibleCell[cx][cy + 1]) {
-			if (!(wallAt(cx, cy + 1) & 0x01)) {
+			bool blocked = (wallAt(cx, cy + 1) & 0x01) != 0;
+			if (!blocked)
+				blocked = hasFeatureAt(cx, cy, kDirNorth) || hasFeatureAt(cx, cy + 1, kDirSouth);
+			if (!blocked) {
 				_visibleCell[cx][cy + 1] = true;
 				queueX[tail] = cx;
 				queueY[tail] = cy + 1;
 				tail++;
 			}
 		}
-		// South: (cx, cy-1) — wall check: _wall[cx][cy] & 0x01
+		// South: (cx, cy-1)
 		if (cy - 1 >= 0 && !_visibleCell[cx][cy - 1]) {
-			if (!(wallAt(cx, cy) & 0x01)) {
+			bool blocked = (wallAt(cx, cy) & 0x01) != 0;
+			if (!blocked)
+				blocked = hasFeatureAt(cx, cy, kDirSouth) || hasFeatureAt(cx, cy - 1, kDirNorth);
+			if (!blocked) {
 				_visibleCell[cx][cy - 1] = true;
 				queueX[tail] = cx;
 				queueY[tail] = cy - 1;
 				tail++;
 			}
 		}
-		// East: (cx+1, cy) — wall check: _wall[cx+1][cy] & 0x02
+		// East: (cx+1, cy)
 		if (cx + 1 < 32 && !_visibleCell[cx + 1][cy]) {
-			if (!(wallAt(cx + 1, cy) & 0x02)) {
+			bool blocked = (wallAt(cx + 1, cy) & 0x02) != 0;
+			if (!blocked)
+				blocked = hasFeatureAt(cx, cy, kDirEast) || hasFeatureAt(cx + 1, cy, kDirWest);
+			if (!blocked) {
 				_visibleCell[cx + 1][cy] = true;
 				queueX[tail] = cx + 1;
 				queueY[tail] = cy;
 				tail++;
 			}
 		}
-		// West: (cx-1, cy) — wall check: _wall[cx][cy] & 0x02
+		// West: (cx-1, cy)
 		if (cx - 1 >= 0 && !_visibleCell[cx - 1][cy]) {
-			if (!(wallAt(cx, cy) & 0x02)) {
+			bool blocked = (wallAt(cx, cy) & 0x02) != 0;
+			if (!blocked)
+				blocked = hasFeatureAt(cx, cy, kDirWest) || hasFeatureAt(cx - 1, cy, kDirEast);
+			if (!blocked) {
 				_visibleCell[cx - 1][cy] = true;
 				queueX[tail] = cx - 1;
 				queueY[tail] = cy;
@@ -1139,9 +1159,29 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 		// Mac: fill arrow polygon with BLACK.
 		// drawchar() sets cColor[cc].pattern=WHITE (all background) and
 		// background is {0,0,0}=BLACK, so SuperPoly fills solid black.
+		// Arrow shapes are concave — GL_POLYGON only handles convex polys.
+		// Decompose into convex parts: triangle head + rectangle shaft.
 		if (_renderMode == Common::kRenderMacintosh) {
 			_gfx->setWireframe(true, 0); // BLACK fill
-			wallPolygon(corners, u, v, count, 0);
+			if (cnum == 'b') {
+				// Right arrow head: (0,3)-(3,0)-(3,6)
+				float hu[3] = {u[0], u[1], u[6]};
+				float hv[3] = {v[0], v[1], v[6]};
+				wallPolygon(corners, hu, hv, 3, 0);
+				// Right arrow shaft: (3,2)-(6,2)-(6,4)-(3,4)
+				float su[4] = {u[2], u[3], u[4], u[5]};
+				float sv[4] = {v[2], v[3], v[4], v[5]};
+				wallPolygon(corners, su, sv, 4, 0);
+			} else if (cnum == 'c') {
+				// Left arrow shaft: (0,2)-(0,4)-(3,4)-(3,2)
+				float su[4] = {u[0], u[1], u[2], u[6]};
+				float sv[4] = {v[0], v[1], v[2], v[6]};
+				wallPolygon(corners, su, sv, 4, 0);
+				// Left arrow head: (3,6)-(6,3)-(3,0)
+				float hu[3] = {u[3], u[4], u[5]};
+				float hv[3] = {v[3], v[4], v[5]};
+				wallPolygon(corners, hu, hv, 3, 0);
+			}
 			_gfx->setWireframe(true, 255); // restore white wall fill
 		}
 		for (int i = 0; i < count; i++) {


Commit: 42254e584bcca85f200dd9e662ba04e1895f5fbe
    https://github.com/scummvm/scummvm/commit/42254e584bcca85f200dd9e662ba04e1895f5fbe
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:21+02:00

Commit Message:
COLONY: handle kColorClear surfaces as wireframe-only outlines

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index b8aac3780d3..eda62d3e5f4 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -904,8 +904,18 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 		}
 
 		if (count >= 3) {
-			if (colorIdx == kColorClear)
-				continue; // Transparent surface, skip
+			if (colorIdx == kColorClear) {
+				// CLEAR = no fill, but still draw wireframe outline.
+				// DOS DrawPrism skips CLEAR in the fill pass but draws
+				// outlines for ALL surfaces in the wireframe pass.
+				uint32 color = lit ? 0 : 15; // vBLACK lit, vINTWHITE unlit
+				for (int j = 0; j < count; j++) {
+					int next = (j + 1) % count;
+					_gfx->draw3DLine(px[j], py[j], pz[j],
+					                 px[next], py[next], pz[next], color);
+				}
+				continue;
+			}
 
 			if (lit) {
 				if (_renderMode == Common::kRenderMacintosh) {


Commit: dc7af136b3b06478966d80f2cd800cad4dd30003
    https://github.com/scummvm/scummvm/commit/dc7af136b3b06478966d80f2cd800cad4dd30003
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:22+02:00

Commit Message:
COLONY: add bench 3D object geometry

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 5590cda5231..5e13e6d98f1 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -79,7 +79,9 @@ enum ObjectType {
 	kObjTub = 38,
 	kObjSink = 39,
 	kObjToilet = 40,
+	kObjBench = 41,
 	kObjPToilet = 43,
+	kObjCBench = 44,
 	kObjProjector = 45,
 	kObjReactor = 46,
 	kObjFWall = 48,
@@ -100,6 +102,7 @@ enum ObjColor {
 	kColorSheet = 22,
 	kColorBed = 23,
 	kColorBox = 24,
+	kColorBench = 25,
 	kColorChair = 26,
 	kColorChairBase = 27,
 	kColorCouch = 28,
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index eda62d3e5f4..0530a13e284 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -54,6 +54,7 @@ static uint8 lookupLineColor(int colorIdx) {
 	case kColorSheet:     return 0;  // vBLACK (LINEFILLCOLOR for fill mode)
 	case kColorBed:       return 6;  // vBROWN
 	case kColorBox:       return 6;  // vBROWN
+	case kColorBench:     return 0;  // vBLACK
 	case kColorChair:     return 1;  // vBLUE
 	case kColorChairBase: return 1;  // vBLUE
 	case kColorCouch:     return 4;  // vRED
@@ -616,6 +617,34 @@ static const Colony::ColonyEngine::PrismPartDef kReactorParts[3] = {
 	{8, kReactorTopPts, 6, kReactorBaseSurf}
 };
 
+// Bench: simple box (1 part). DOS INITOBJ.C InitBench.
+static const int kBenchPts[8][3] = {
+	{-60, 128, 0}, {60, 128, 0}, {60, -128, 0}, {-60, -128, 0},
+	{-60, 128, 120}, {60, 128, 120}, {60, -128, 120}, {-60, -128, 120}
+};
+static const int kBenchSurf[5][8] = {
+	{kColorBench, 4, 0, 3, 7, 4, 0, 0}, {kColorBench, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBench, 4, 1, 0, 4, 5, 0, 0}, {kColorBench, 4, 2, 1, 5, 6, 0, 0},
+	{kColorBlack, 4, 7, 6, 5, 4, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kBenchPart = {8, kBenchPts, 5, kBenchSurf};
+
+// CBench: L-shaped corner bench (2 parts). DOS INITOBJ.C InitCBench.
+// Part 0: slanted front face (front-left Y=60, front-right Y=-60)
+static const int kCBenchPts[8][3] = {
+	{-60, 60, 0}, {60, -60, 0}, {60, -128, 0}, {-60, -128, 0},
+	{-60, 60, 120}, {60, -60, 120}, {60, -128, 120}, {-60, -128, 120}
+};
+// Part 1: wider perpendicular section
+static const int kDBenchPts[8][3] = {
+	{-60, 60, 0}, {128, 60, 0}, {128, -60, 0}, {60, -60, 0},
+	{-60, 60, 120}, {128, 60, 120}, {128, -60, 120}, {60, -60, 120}
+};
+static const Colony::ColonyEngine::PrismPartDef kCBenchParts[2] = {
+	{8, kCBenchPts, 5, kBenchSurf},
+	{8, kDBenchPts, 5, kBenchSurf}
+};
+
 // Power Suit: triangular prism body + small rectangular pedestal + flat table + hexagonal power source
 // DOS INITOBJ.C: 5 prism parts. Floor=160, so 2*Floor=320.
 static const int kPowerTopPts[3][3] = {{-150, 120, 320}, {150, 120, 320}, {0, -150, 320}};
@@ -1789,6 +1818,13 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 	case kObjBox1:
 		draw3DPrism(obj, kBox1Part, false);
 		break;
+	case kObjBench:
+		draw3DPrism(obj, kBenchPart, false);
+		break;
+	case kObjCBench:
+		for (int i = 0; i < 2; i++)
+			draw3DPrism(obj, kCBenchParts[i], false);
+		break;
 	case kObjBox2:
 		draw3DPrism(obj, kBox2Parts[1], false); // base first
 		draw3DPrism(obj, kBox2Parts[0], false); // top second


Commit: e0abaf317f129aef0e1fa706b9bef6330a40e834
    https://github.com/scummvm/scummvm/commit/e0abaf317f129aef0e1fa706b9bef6330a40e834
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:22+02:00

Commit Message:
COLONY: add animated COLOR wall feature and Mac stipple hole rendering

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 7ad0d5147ff..3d0f9bff6e4 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -390,6 +390,7 @@ Common::Error ColonyEngine::run() {
 		drawCrosshair();
 		checkCenter();
 		
+		_displayCount++; // Mac: count++ after Display()
 		_frameLimiter->delayBeforeSwap();
 		_gfx->copyToScreen();
 	}
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 5e13e6d98f1..4e47fae6add 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -292,6 +292,7 @@ private:
 	bool _gametest = false;
 	uint32 _blackoutColor = 0;
 	uint32 _lastClickTime = 0;
+	uint32 _displayCount = 0; // Frame counter for COLOR wall animation (Mac: count)
 	int _action0, _action1;
 	int _creature;
 
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 0530a13e284..9bd357d7aac 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -1259,6 +1259,8 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 	bool lit = (_corePower[_coreIndex] > 0);
 	uint32 holeColor = lit ? 0 : 7;
 
+	const bool macMode = (_renderMode == Common::kRenderMacintosh);
+
 	switch (map[0]) {
 	case 1: // SMHOLEFLR
 	case 3: // SMHOLECEIL
@@ -1267,7 +1269,14 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 		// Matching the CCenter proximity trigger zone (64..192 of 256)
 		float u[4] = {0.25f, 0.75f, 0.75f, 0.25f};
 		float v[4] = {0.25f, 0.25f, 0.75f, 0.75f};
-		wallPolygon(corners, u, v, 4, holeColor);
+		if (macMode) {
+			// Mac: SuperPoly(c_hole, ...) — c_hole = GRAY stipple, always filled
+			_gfx->setStippleData(kStippleGray);
+			wallPolygon(corners, u, v, 4, 0);
+			_gfx->setStippleData(nullptr);
+		} else {
+			wallPolygon(corners, u, v, 4, holeColor);
+		}
 		break;
 	}
 	case 2: // LGHOLEFLR
@@ -1276,13 +1285,31 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 		// DOS floor2hole/ceil2hole: full-cell hole
 		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-		wallPolygon(corners, u, v, 4, holeColor);
+		if (macMode) {
+			// Mac: SuperPoly(c_hole, ...) — c_hole = GRAY stipple, always filled
+			_gfx->setStippleData(kStippleGray);
+			wallPolygon(corners, u, v, 4, 0);
+			_gfx->setStippleData(nullptr);
+		} else {
+			wallPolygon(corners, u, v, 4, holeColor);
+		}
 		break;
 	}
-	case 5: // HOTFOOT — DOS draws X pattern (two diagonals), not a rectangle
+	case 5: // HOTFOOT
 	{
-		wallLine(corners, 0.0f, 0.0f, 1.0f, 1.0f, holeColor); // front-left to back-right
-		wallLine(corners, 1.0f, 0.0f, 0.0f, 1.0f, holeColor); // front-right to back-left
+		if (macMode) {
+			// Mac: SuperPoly(c_hotplate, ...) — c_hotplate = GRAY stipple, full cell
+			// Mac draws ONLY the filled polygon, no X-pattern
+			float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+			float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+			_gfx->setStippleData(kStippleGray);
+			wallPolygon(corners, u, v, 4, 0);
+			_gfx->setStippleData(nullptr);
+		} else {
+			// DOS non-polyfill: X pattern (two diagonals)
+			wallLine(corners, 0.0f, 0.0f, 1.0f, 1.0f, holeColor);
+			wallLine(corners, 1.0f, 0.0f, 0.0f, 1.0f, holeColor);
+		}
 		break;
 	}
 	default:
@@ -1654,9 +1681,45 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	}
 	case kWallFeatureColor: {
+		// Mac drawColor / DOS drawColor: 4 horizontal bands.
+		// 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) {
+			static const byte *stripPatterns[5] = {
+				nullptr, kStippleLtGray, kStippleGray, kStippleDkGray, nullptr
+			};
+			for (int i = 0; i < 4; i++) {
+				int pat = map[i + 1];
+				if (pat > 4)
+					pat = (pat + _displayCount / 6) % 5; // animated cycling
+				float vb = (3 - i) / 4.0f;
+				float vt = (4 - i) / 4.0f;
+				float ub[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+				float vb4[4] = {vb, vb, vt, vt};
+				if (pat == 4) {
+					// BLACK: solid black fill
+					_gfx->setWireframe(true, 0);
+					wallPolygon(corners, ub, vb4, 4, 0);
+					_gfx->setWireframe(true, 255);
+				} else if (pat == 0) {
+					// WHITE: no fill needed (wall background is white)
+				} else {
+					_gfx->setStippleData(stripPatterns[pat]);
+					wallPolygon(corners, ub, vb4, 4, 0);
+					_gfx->setStippleData(nullptr);
+				}
+			}
+		}
+
+		// EGA / both modes: colored lines at band boundaries.
+		// DOS non-polyfill draws 3 lines; we animate line colors for bands > 4.
 		for (int i = 1; i <= 3; i++) {
-			uint32 c = 120 + map[i] * 20;
-			if (c == 120 && map[i] == 0 && !map[1] && !map[2] && !map[3] && !map[4])
+			int val = map[i];
+			if (val > 4)
+				val = (val + _displayCount / 6) % 5; // animated cycling
+			uint32 c = 120 + val * 20;
+			if (c == 120 && val == 0 && !map[1] && !map[2] && !map[3] && !map[4])
 				c = 100 + (_level * 15);
 			float v = (float)i / 4.0f;
 			wallLine(corners, 0.0f, v, 1.0f, v, c);


Commit: 2d43ff6567c3ee707d478362614955ce938f95e3
    https://github.com/scummvm/scummvm/commit/2d43ff6567c3ee707d478362614955ce938f95e3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:22+02:00

Commit Message:
COLONY: implement PATCH.C object relocation system

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 3d0f9bff6e4..14da2a50750 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -75,6 +75,13 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	memset(_robotArray, 0, sizeof(_robotArray));
 	memset(_foodArray, 0, sizeof(_foodArray));
 
+	// PATCH.C init
+	memset(_levelData, 0, sizeof(_levelData));
+	memset(_carryPatch, 0, sizeof(_carryPatch));
+	_carryType = 0;
+	_fl = 0;
+	_patches.clear();
+
 	_screenR = Common::Rect(0, 0, _width, _height);
 	_clip = _screenR;
 	_dashBoardRect = Common::Rect(0, 0, 0, 0);
@@ -194,11 +201,187 @@ void ColonyEngine::loadMap(int mnum) {
 	free(buffer);
 	_level = mnum;
 	_me.type = MENUM;
+
+	getWall();  // restore saved wall state changes (airlocks)
+	doPatch();  // apply object relocations from patch table
+
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 		_robotArray[_me.xindex][_me.yindex] = MENUM;
 	debug("Successfully loaded map %d (static objects: %d)", mnum, (int)_objects.size());
 }
 
+// PATCH.C: Create a new object in _objects and register in _robotArray.
+// Mirrors DOS CreateObject() — sets basic Thing fields for static objects.
+void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
+	Thing obj;
+	memset(&obj, 0, sizeof(obj));
+	while (ang > 255) ang -= 256;
+	obj.alive = 1;
+	obj.visible = 0;
+	obj.type = type;
+	obj.where.xloc = xloc;
+	obj.where.yloc = yloc;
+	obj.where.xindex = xloc >> 8;
+	obj.where.yindex = yloc >> 8;
+	obj.where.delta = 4;
+	obj.where.ang = ang;
+	obj.where.look = ang;
+
+	// Try to reuse a dead slot (starting after MENUM)
+	int slot = -1;
+	for (int j = MENUM; j < (int)_objects.size(); j++) {
+		if (!_objects[j].alive) {
+			slot = j;
+			break;
+		}
+	}
+	if (slot >= 0) {
+		_objects[slot] = obj;
+	} else {
+		_objects.push_back(obj);
+		slot = (int)_objects.size() - 1;
+	}
+	int objNum = slot + 1; // 1-based for _robotArray
+	if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+	    obj.where.yindex >= 0 && obj.where.yindex < 32)
+		_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)objNum;
+	if (slot + 1 > _robotNum)
+		_robotNum = slot + 1;
+}
+
+// PATCH.C: DoPatch() — remove originals and install relocated objects.
+void ColonyEngine::doPatch() {
+	// Pass 1: remove objects that were moved away from this level
+	for (uint i = 0; i < _patches.size(); i++) {
+		if (_level == _patches[i].from.level) {
+			int robot = _robotArray[_patches[i].from.xindex][_patches[i].from.yindex];
+			if (robot > 0 && robot <= (int)_objects.size()) {
+				_robotArray[_objects[robot - 1].where.xindex][_objects[robot - 1].where.yindex] = 0;
+				_objects[robot - 1].alive = 0;
+			}
+		}
+	}
+	// Pass 2: install objects that were moved to this level
+	for (uint i = 0; i < _patches.size(); i++) {
+		if (_level == _patches[i].to.level) {
+			createObject(
+				(int)_patches[i].type,
+				(int)_patches[i].to.xloc,
+				(int)_patches[i].to.yloc,
+				_patches[i].to.ang);
+		}
+	}
+}
+
+// PATCH.C: savewall() — save 5 bytes of map feature data for persistence across level loads.
+void ColonyEngine::saveWall(int x, int y, int direction) {
+	if (_level < 1 || _level > 8) return;
+	LevelData &ld = _levelData[_level - 1];
+
+	// Search for existing entry at this location
+	for (int i = 0; i < ld.size; i++) {
+		if (ld.location[i][0] == x && ld.location[i][1] == y && ld.location[i][2] == direction) {
+			for (int j = 0; j < 5; j++)
+				ld.data[i][j] = _mapData[x][y][direction][j];
+			return;
+		}
+	}
+	// Add new entry (max 10)
+	if (ld.size >= 10) {
+		warning("saveWall: too many wall changes for level %d", _level);
+		return;
+	}
+	int i = ld.size;
+	for (int j = 0; j < 5; j++)
+		ld.data[i][j] = _mapData[x][y][direction][j];
+	ld.location[i][0] = x;
+	ld.location[i][1] = y;
+	ld.location[i][2] = direction;
+	ld.size++;
+}
+
+// PATCH.C: getwall() — restore saved wall bytes into _mapData after level load.
+void ColonyEngine::getWall() {
+	if (_level < 1 || _level > 8) return;
+	const LevelData &ld = _levelData[_level - 1];
+	for (int i = 0; i < ld.size; i++) {
+		int x = ld.location[i][0];
+		int y = ld.location[i][1];
+		int dir = ld.location[i][2];
+		if (x < 31 && y < 31 && dir < 5) {
+			for (int j = 0; j < 5; j++)
+				_mapData[x][y][dir][j] = ld.data[i][j];
+		}
+	}
+}
+
+// PATCH.C: newpatch() — create or update a patch entry.
+void ColonyEngine::newPatch(int type, const PassPatch &from, const PassPatch &to, const uint8 *mapdata) {
+	// Search for existing patch where 'from' matches an existing 'to'
+	for (uint i = 0; i < _patches.size(); i++) {
+		if (from.level == _patches[i].to.level &&
+		    from.xindex == _patches[i].to.xindex &&
+		    from.yindex == _patches[i].to.yindex) {
+			_patches[i].to.level = to.level;
+			_patches[i].to.xindex = to.xindex;
+			_patches[i].to.yindex = to.yindex;
+			_patches[i].to.xloc = to.xloc;
+			_patches[i].to.yloc = to.yloc;
+			_patches[i].to.ang = to.ang;
+			return;
+		}
+	}
+	// Create new patch entry (max 100)
+	if (_patches.size() >= 100) return;
+	PatchEntry pe;
+	pe.type = type;
+	pe.from.level = from.level;
+	pe.from.xindex = from.xindex;
+	pe.from.yindex = from.yindex;
+	pe.to.level = to.level;
+	pe.to.xindex = to.xindex;
+	pe.to.yindex = to.yindex;
+	pe.to.xloc = to.xloc;
+	pe.to.yloc = to.yloc;
+	pe.to.ang = to.ang;
+	if (mapdata) {
+		for (int j = 0; j < 5; j++)
+			pe.mapdata[j] = mapdata[j];
+	} else {
+		memset(pe.mapdata, 0, sizeof(pe.mapdata));
+	}
+	_patches.push_back(pe);
+}
+
+// PATCH.C: patchmapto() — find patch entry by destination, fill mapdata.
+bool ColonyEngine::patchMapTo(const PassPatch &to, uint8 *mapdata) {
+	for (uint i = 0; i < _patches.size(); i++) {
+		if (to.level == _patches[i].to.level &&
+		    to.xindex == _patches[i].to.xindex &&
+		    to.yindex == _patches[i].to.yindex) {
+			for (int j = 0; j < 5; j++)
+				mapdata[j] = _patches[i].mapdata[j];
+			return true;
+		}
+	}
+	return false;
+}
+
+// PATCH.C: patchmapfrom() — find patch entry by source, fill destination into mapdata.
+bool ColonyEngine::patchMapFrom(const PassPatch &from, uint8 *mapdata) {
+	for (uint i = 0; i < _patches.size(); i++) {
+		if (from.level == _patches[i].from.level &&
+		    from.xindex == _patches[i].from.xindex &&
+		    from.yindex == _patches[i].from.yindex) {
+			mapdata[2] = _patches[i].to.level;
+			mapdata[3] = _patches[i].to.xindex;
+			mapdata[4] = _patches[i].to.yindex;
+			return true;
+		}
+	}
+	return false;
+}
+
 void ColonyEngine::initTrig() {
 	// Compute standard sin/cos lookup tables (256 steps = full circle, scaled by 128)
 	for (int i = 0; i < 256; i++) {
@@ -346,6 +529,14 @@ Common::Error ColonyEngine::run() {
 						_system->warpMouse(_centerX, _centerY);
 						_system->getEventManager()->purgeMouseEvents();
 						break;
+					// Space: shoot or exit forklift (DOS: ScanCode 57)
+					case Common::KEYCODE_SPACE:
+						if (_fl == 2)
+							dropCarriedObject();
+						else if (_fl == 1)
+							exitForklift();
+						// else: shoot (TODO: implement shooting)
+						break;
 					// Escape: also opens ScummVM menu
 					case Common::KEYCODE_ESCAPE:
 						_system->lockMouse(false);
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 4e47fae6add..da6f98e9d80 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -174,6 +174,31 @@ struct Thing {
 	// void (*think)();
 };
 
+// PATCH.C: Tracks object relocations across levels (forklift carry/drop).
+struct PatchEntry {
+	struct { uint8 level, xindex, yindex; } from;
+	struct { uint8 level, xindex, yindex; int xloc, yloc; uint8 ang; } to;
+	uint8 type;
+	uint8 mapdata[5];
+};
+
+// Temporary location reference for carrying objects.
+struct PassPatch {
+	uint8 level, xindex, yindex;
+	int xloc, yloc;
+	uint8 ang;
+};
+
+// Per-level persistence: wall state changes (airlock locks) and visit flags.
+struct LevelData {
+	uint8 visit;
+	uint8 queen;
+	uint8 count;           // airlock open count (termination check)
+	uint8 size;            // number of saved wall changes (max 10)
+	uint8 location[10][3]; // [x, y, direction] of each changed wall
+	uint8 data[10][5];     // saved wall feature bytes (5 per location)
+};
+
 struct Image {
 	int16 width;
 	int16 height;
@@ -296,6 +321,13 @@ private:
 	int _action0, _action1;
 	int _creature;
 
+	// PATCH.C: object relocation + wall state persistence
+	Common::Array<PatchEntry> _patches;
+	PassPatch _carryPatch[2];   // [0]=forklift, [1]=carried object
+	int _carryType;             // type of object being carried
+	int _fl;                    // 0=not in forklift, 1=in forklift empty, 2=carrying object
+	LevelData _levelData[8];   // per-level wall state persistence
+
 	int _frntxWall, _frntyWall;
 	int _sidexWall, _sideyWall;
 	int _frntx, _frnty;
@@ -337,6 +369,17 @@ private:
 
 	int occupiedObjectAt(int x, int y, const Locate *pobject);
 	void interactWithObject(int objNum);
+
+	// PATCH.C: object relocation + wall state persistence
+	void createObject(int type, int xloc, int yloc, uint8 ang);
+	void doPatch();
+	void saveWall(int x, int y, int direction);
+	void getWall();
+	void newPatch(int type, const PassPatch &from, const PassPatch &to, const uint8 *mapdata);
+	bool patchMapTo(const PassPatch &to, uint8 *mapdata);
+	bool patchMapFrom(const PassPatch &from, uint8 *mapdata);
+	void exitForklift();
+	void dropCarriedObject();
 	bool setDoorState(int x, int y, int direction, int state);
 	int openAdjacentDoors(int x, int y);
 	int tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject);
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 96ce2f218a3..0122f101a74 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -472,6 +472,13 @@ bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
 			_mapData[nx][ny][opposite][1] = (uint8)state;
 	}
 
+	// Persist airlock state changes across level loads
+	if (wallType == kWallFeatureAirlock) {
+		saveWall(x, y, direction);
+		if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31 && opposite >= 0)
+			saveWall(nx, ny, opposite);
+	}
+
 	return true;
 }
 
@@ -715,12 +722,51 @@ void ColonyEngine::interactWithObject(int objNum) {
 		break;
 	case kObjTeleport:
 	{
+		if (_fl == 1) {
+			// In empty forklift — pick up the teleporter itself
+			if (loadAnimation("lift")) {
+				_animationResult = 0;
+				playAnimation();
+				if (_animationResult) {
+					PassPatch from;
+					from.level = _level;
+					from.xindex = obj.where.xindex;
+					from.yindex = obj.where.yindex;
+					from.xloc = obj.where.xloc;
+					from.yloc = obj.where.yloc;
+					from.ang = obj.where.ang;
+					_carryPatch[1].level = 100;
+					_carryPatch[1].xindex = 1;
+					_carryPatch[1].yindex = 1;
+					_carryPatch[1].xloc = 1;
+					_carryPatch[1].yloc = 1;
+					_carryPatch[1].ang = 1;
+					newPatch(kObjTeleport, from, _carryPatch[1], _mapData[from.xindex][from.yindex][4]);
+					_carryType = kObjTeleport;
+					_robotArray[obj.where.xindex][obj.where.yindex] = 0;
+					_objects[objNum - 1].alive = 0;
+					_fl = 2;
+				}
+			}
+			break;
+		}
+		// fl==0 or fl==2: use the teleporter
 		_sound->play(Sound::kTeleport);
-		const int targetLevelRaw = _mapData[x][y][4][2];
-
+		int targetLevelRaw = _mapData[x][y][4][2];
+		int targetX = _action0;
+		int targetY = _action1;
+		// Check if this teleporter was relocated via patch
+		PassPatch tp;
+		tp.level = _level;
+		tp.xindex = x;
+		tp.yindex = y;
+		uint8 patchData[5];
+		if (patchMapTo(tp, patchData)) {
+			targetLevelRaw = patchData[2];
+			targetX = patchData[3];
+			targetY = patchData[4];
+		}
 		const int targetLevel = (targetLevelRaw == 0) ? _level : targetLevelRaw;
-		const int targetX = _action0;
-		const int targetY = _action1;
 		if (targetLevel >= 100 || targetX <= 0 || targetX >= 31 || targetY <= 0 || targetY >= 31) {
 			inform("TELEPORTER INITIALIZATION FAILED", true);
 			break;
@@ -764,18 +810,99 @@ void ColonyEngine::interactWithObject(int objNum) {
 		_sound->play(Sound::kSink);
 		inform("A SINK. IT'S DRY.", true);
 		break;
-	case kObjCryo:
-		doText(_action0, 0);
-		break;
 	case kObjTV:
 		if (_level == 1) doText(56, 0);
 		else doText(16, 0);
 		break;
 	case kObjForkLift:
-	case kObjReactor:
+		if (_fl == 0) {
+			// Enter forklift: play animation, if confirmed, patch it away
+			if (loadAnimation("forklift")) {
+				playAnimation();
+				if (_animationResult) {
+					PassPatch from;
+					from.level = _level;
+					from.xindex = obj.where.xindex;
+					from.yindex = obj.where.yindex;
+					from.xloc = obj.where.xloc;
+					from.yloc = obj.where.yloc;
+					from.ang = obj.where.ang;
+					_carryPatch[0].level = 100;
+					_carryPatch[0].xindex = 0;
+					_carryPatch[0].yindex = 0;
+					_carryPatch[0].xloc = 0;
+					_carryPatch[0].yloc = 0;
+					_carryPatch[0].ang = 0;
+					newPatch(kObjForkLift, from, _carryPatch[0], _mapData[from.xindex][from.yindex][4]);
+					_robotArray[obj.where.xindex][obj.where.yindex] = 0;
+					_objects[objNum - 1].alive = 0;
+					_me.look = _me.ang = obj.where.ang;
+					_fl = 1;
+				}
+			}
+		}
+		break;
 	case kObjBox1:
 	case kObjBox2:
-		inform("NEEDS FORKLIFT INTERACTION", true);
+	case kObjCryo:
+		if (_fl == 1) {
+			// In empty forklift — pick up object
+			if (loadAnimation("lift")) {
+				_animationResult = 0;
+				playAnimation();
+				if (_animationResult) {
+					PassPatch from;
+					from.level = _level;
+					from.xindex = obj.where.xindex;
+					from.yindex = obj.where.yindex;
+					from.xloc = obj.where.xloc;
+					from.yloc = obj.where.yloc;
+					from.ang = obj.where.ang;
+					_carryPatch[1].level = 100;
+					_carryPatch[1].xindex = 1;
+					_carryPatch[1].yindex = 1;
+					_carryPatch[1].xloc = 1;
+					_carryPatch[1].yloc = 1;
+					_carryPatch[1].ang = 1;
+					newPatch(obj.type, from, _carryPatch[1], _mapData[from.xindex][from.yindex][4]);
+					_carryType = obj.type;
+					_robotArray[obj.where.xindex][obj.where.yindex] = 0;
+					_objects[objNum - 1].alive = 0;
+					_fl = 2;
+				}
+			}
+		} else if (_fl == 0 && obj.type == kObjCryo) {
+			// Not in forklift — read cryo text
+			doText(_action0, 0);
+		}
+		break;
+	case kObjReactor:
+		if (_fl == 1 && _coreState[_coreIndex] == 1) {
+			// Empty forklift at open reactor — pick up reactor core
+			if (loadAnimation("lift")) {
+				_animationResult = 0;
+				playAnimation();
+				if (_animationResult) {
+					_carryType = kObjReactor;
+					_corePower[2] = _corePower[_coreIndex];
+					_corePower[_coreIndex] = 0;
+					_coreState[_coreIndex] = 2;
+					_fl = 2;
+				}
+			}
+		} else if (_fl == 2 && _carryType == kObjReactor && _coreState[_coreIndex] == 2) {
+			// Carrying reactor core — drop it into reactor
+			if (loadAnimation("lift")) {
+				_animationResult = 0;
+				playAnimation();
+				if (!_animationResult) {
+					_carryType = 0;
+					_coreState[_coreIndex] = 1;
+					_corePower[_coreIndex] = _corePower[2];
+					_fl = 1;
+				}
+			}
+		}
 		break;
 	default:
 		break;
@@ -1081,6 +1208,110 @@ void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
 		_robotArray[_me.xindex][_me.yindex] = MENUM;
 }
 
+// DOS ExitFL(): step back one cell and drop the forklift.
+void ColonyEngine::exitForklift() {
+	if (_fl != 1) return;
+
+	int xloc = _me.xloc;
+	int yloc = _me.yloc;
+	int xindex = _me.xindex;
+	int yindex = _me.yindex;
+
+	// Walk backward until we move into a different cell
+	while (_me.xindex == xindex && _me.yindex == yindex) {
+		int xnew = _me.xloc - _cost[_me.ang];
+		int ynew = _me.yloc - _sint[_me.ang];
+		_me.type = 2; // temporary small collision type
+		if (checkwall(xnew, ynew, &_me)) {
+			_sound->play(Sound::kChime);
+			_me.type = MENUM;
+			return;
+		}
+		_me.type = MENUM;
+	}
+
+	// Snap to cell center for the dropped forklift
+	xloc = (xloc >> 8);
+	xloc = (xloc << 8) + 128;
+	yloc = (yloc >> 8);
+	yloc = (yloc << 8) + 128;
+
+	createObject(kObjForkLift, xloc, yloc, _me.ang);
+
+	PassPatch to;
+	to.level = _level;
+	to.xindex = xindex;
+	to.yindex = yindex;
+	to.xloc = xloc;
+	to.yloc = yloc;
+	to.ang = _me.ang;
+	newPatch(kObjForkLift, _carryPatch[0], to, nullptr);
+
+	_fl = 0;
+}
+
+// DOS DropFL(): step back one cell and drop the carried object, then return to fl=1.
+void ColonyEngine::dropCarriedObject() {
+	if (_fl != 2) return;
+
+	// Special case: carrying reactor core
+	if (_carryType == kObjReactor) {
+		_sound->play(Sound::kChime); // glass break sound
+		_carryType = 0;
+		_fl = 1;
+		return;
+	}
+
+	// Play the drop animation
+	if (loadAnimation("lift")) {
+		_animationResult = 0;
+		playAnimation();
+		if (!_animationResult) {
+			// Animation was cancelled — don't drop
+			return;
+		}
+	}
+
+	int xloc = _me.xloc;
+	int yloc = _me.yloc;
+	int xindex = _me.xindex;
+	int yindex = _me.yindex;
+
+	// Walk backward until we move into a different cell
+	while (_me.xindex == xindex && _me.yindex == yindex) {
+		int xnew = _me.xloc - _cost[_me.ang];
+		int ynew = _me.yloc - _sint[_me.ang];
+		_me.type = 2;
+		if (checkwall(xnew, ynew, &_me)) {
+			_sound->play(Sound::kChime);
+			_me.type = MENUM;
+			return;
+		}
+		_me.type = MENUM;
+	}
+
+	// DOS: teleport always drops at ang=0; other objects use player's angle
+	uint8 ang = (_carryType == kObjTeleport) ? 0 : _me.ang;
+
+	xloc = (xloc >> 8);
+	xloc = (xloc << 8) + 128;
+	yloc = (yloc >> 8);
+	yloc = (yloc << 8) + 128;
+
+	createObject(_carryType, xloc, yloc, ang);
+
+	PassPatch to;
+	to.level = _level;
+	to.xindex = xindex;
+	to.yindex = yindex;
+	to.xloc = xloc;
+	to.yloc = yloc;
+	to.ang = _me.ang;
+	newPatch(_carryType, _carryPatch[1], to, nullptr);
+
+	_fl = 1;
+}
+
 bool ColonyEngine::clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const {
 	if (clip.left >= clip.right || clip.top >= clip.bottom)
 		return false;


Commit: 121094cfc77d357eec21cdee0d3d552874f5d840
    https://github.com/scummvm/scummvm/commit/121094cfc77d357eec21cdee0d3d552874f5d840
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:23+02:00

Commit Message:
COLONY: robot rendering

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 14da2a50750..a26d8207406 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -204,10 +204,11 @@ void ColonyEngine::loadMap(int mnum) {
 
 	getWall();  // restore saved wall state changes (airlocks)
 	doPatch();  // apply object relocations from patch table
+	initRobots();  // spawn robot objects for this level
 
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 		_robotArray[_me.xindex][_me.yindex] = MENUM;
-	debug("Successfully loaded map %d (static objects: %d)", mnum, (int)_objects.size());
+	debug("Successfully loaded map %d (objects: %d)", mnum, (int)_objects.size());
 }
 
 // PATCH.C: Create a new object in _objects and register in _robotArray.
@@ -382,6 +383,69 @@ bool ColonyEngine::patchMapFrom(const PassPatch &from, uint8 *mapdata) {
 	return false;
 }
 
+// DOS InitObject() — spawn robots for the current level.
+// Level 1 = no robots; Level 2 = 25; Level 3-4 = 30; Level 5-7 = 35.
+// Robot #1 = QUEEN, #2 = SNOOP, rest = random type weighted by level.
+void ColonyEngine::initRobots() {
+	if (_level == 1) return;  // Level 1 has no robots
+
+	int maxrob;
+	switch (_level) {
+	case 2:  maxrob = 25; break;
+	case 3:
+	case 4:  maxrob = 30; break;
+	default: maxrob = 35; break;
+	}
+
+	int lvl = _level - 1;
+	if (lvl > 5) lvl = 5;
+
+	for (int i = 1; i <= maxrob; i++) {
+		uint8 ang = _randomSource.getRandomNumber(255);
+		int xloc, yloc;
+
+		// Find unoccupied cell (avoiding borders)
+		do {
+			if (_level == 7 && i == 1) {
+				// Queen on level 7 has fixed position
+				xloc = 27;
+				yloc = 10;
+			} else {
+				xloc = 2 + _randomSource.getRandomNumber(26);  // 2..28
+				yloc = 2 + _randomSource.getRandomNumber(26);  // 2..28
+			}
+		} while (_robotArray[xloc][yloc] != 0);
+
+		// Convert grid coords to world coords (center of cell)
+		int wxloc = (xloc << 8) + 128;
+		int wyloc = (yloc << 8) + 128;
+
+		int type;
+		if (i == 1)
+			type = kRobQueen;
+		else if (i == 2)
+			type = kRobSnoop;
+		else {
+			// Random type weighted by level difficulty
+			int rnd = _randomSource.getRandomNumber(lvl);
+			if (rnd > 5) rnd = 5;
+			switch (rnd) {
+			case 0: type = kRobCube; break;
+			case 1: type = kRobPyramid; break;
+			case 2: type = kRobUPyramid; break;
+			case 3: type = kRobEye; break;
+			case 4: type = kRobDrone; break;
+			case 5: type = kRobSoldier; break;
+			default: type = kRobCube; break;
+			}
+		}
+
+		createObject(type, wxloc, wyloc, ang);
+	}
+
+	debug("initRobots: spawned %d robots on level %d", maxrob, _level);
+}
+
 void ColonyEngine::initTrig() {
 	// Compute standard sin/cos lookup tables (256 steps = full circle, scaled by 128)
 	for (int i = 0; i < 256; i++) {
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index da6f98e9d80..a79a088508a 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -58,6 +58,29 @@ enum MapDirection {
 	kDirCenter = 4
 };
 
+enum RobotType {
+	kRobEye = 1,
+	kRobPyramid = 2,
+	kRobCube = 3,
+	kRobUPyramid = 4,
+	kRobFEye = 5,
+	kRobFPyramid = 6,
+	kRobFCube = 7,
+	kRobFUPyramid = 8,
+	kRobSEye = 9,
+	kRobSPyramid = 10,
+	kRobSCube = 11,
+	kRobSUPyramid = 12,
+	kRobMEye = 13,
+	kRobMPyramid = 14,
+	kRobMCube = 15,
+	kRobMUPyramid = 16,
+	kRobQueen = 17,
+	kRobDrone = 18,
+	kRobSoldier = 19,
+	kRobSnoop = 20
+};
+
 enum ObjectType {
 	kObjDesk = 21,
 	kObjPlant = 22,
@@ -135,7 +158,22 @@ enum ObjColor {
 	kColorTeleDoor = 67,
 	kColorWall = 77,
 	kColorRainbow1 = 80,
-	kColorRainbow2 = 81
+	kColorRainbow2 = 81,
+	// Robot colors
+	kColorCube = 36,
+	kColorDrone = 42,
+	kColorClaw1 = 43,
+	kColorClaw2 = 44,
+	kColorEyes = 45,
+	kColorEye = 46,
+	kColorIris = 47,
+	kColorPupil = 48,
+	kColorPyramid = 57,
+	kColorQueen = 58,
+	kColorTopSnoop = 59,
+	kColorBottomSnoop = 60,
+	kColorUPyramid = 68,
+	kColorShadow = 74
 };
 
 #define BASEOBJECT 20
@@ -359,7 +397,10 @@ public:
 private:
 	void draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook);
 	void draw3DLeaf(const Thing &obj, const PrismPartDef &def);
+	void draw3DSphere(const Thing &obj, int pt0x, int pt0y, int pt0z,
+	                  int pt1x, int pt1y, int pt1z, uint32 fillColor, uint32 outlineColor);
 	bool drawStaticObjectPrisms3D(const Thing &obj);
+	void initRobots();
 	void renderCorridor3D();
 	void drawWallFeatures3D();
 	void drawWallFeature3D(int cellX, int cellY, int direction);
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 9bd357d7aac..8b06cd1d55b 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -88,6 +88,21 @@ static uint8 lookupLineColor(int colorIdx) {
 	case kColorWall:      return 0;  // vBLACK
 	case kColorRainbow1:  return 4;  // vRED
 	case kColorRainbow2:  return 14; // vYELLOW
+	// Robot colors (from ROBOCOLR.C fill color column 2)
+	case kColorCube:        return 3;  // vCYAN
+	case kColorDrone:       return 3;  // vCYAN
+	case kColorClaw1:       return 4;  // vRED
+	case kColorClaw2:       return 3;  // vCYAN
+	case kColorEyes:        return 15; // vINTWHITE
+	case kColorEye:         return 15; // vINTWHITE
+	case kColorIris:        return 1;  // vBLUE
+	case kColorPupil:       return 0;  // vBLACK
+	case kColorPyramid:     return 4;  // vRED
+	case kColorQueen:       return 14; // vYELLOW
+	case kColorTopSnoop:    return 3;  // vCYAN
+	case kColorBottomSnoop: return 8;  // vDKGRAY
+	case kColorUPyramid:    return 9;  // vLTBLUE
+	case kColorShadow:      return 8;  // vDKGRAY
 	default:              return 0;  // vBLACK
 	}
 }
@@ -172,6 +187,13 @@ static int lookupMacPattern(int colorIdx) {
 	case kColorTVScreen:  return kPatternDkGray; // c_tvscreen = DKGRAY
 	case kColorMacScreen: return kPatternDkGray; // c_screen = DKGRAY
 	case kColorWater:     return kPatternDkGray; // c_water = DKGRAY
+	// Robot colors (from ROBOCOLR.C Mac pattern column 0)
+	case kColorEyes:        return kPatternWhite;
+	case kColorEye:         return kPatternWhite;
+	case kColorPupil:       return kPatternBlack;
+	case kColorShadow:      return kPatternDkGray;
+	case kColorBottomSnoop: return kPatternDkGray;
+	case kColorClaw1:       return kPatternDkGray;
 	default:              return kPatternGray;   // Most objects = GRAY
 	}
 }
@@ -842,6 +864,278 @@ static const Colony::ColonyEngine::PrismPartDef kProjectorParts[3] = {
 	{12, kPLensPts, 7, kPLensSurf}
 };
 
+// ============================================================================
+// Robot geometry data (from DOS PYRAMID.H, CUBE.H, EYE.H, UPYRAMID.H,
+// QUEEN.H, DRONE.H, SNOOP.H)
+// ============================================================================
+
+// --- Pyramid (type 2) ---
+static const int kPyramidPts[5][3] = {
+	{-75, 75, 30}, {75, 75, 30}, {75, -75, 30}, {-75, -75, 30}, {0, 0, 200}
+};
+static const int kPyramidSurf[4][8] = {
+	{kColorPyramid, 3, 1, 0, 4, 1, 0, 0}, {kColorPyramid, 3, 2, 1, 4, 2, 0, 0},
+	{kColorPyramid, 3, 3, 2, 4, 3, 0, 0}, {kColorPyramid, 3, 0, 3, 4, 0, 0, 0}
+};
+static const int kPShadowPts[4][3] = {
+	{-75, 75, 0}, {75, 75, 0}, {75, -75, 0}, {-75, -75, 0}
+};
+static const int kPShadowSurf[1][8] = {{kColorShadow, 4, 3, 2, 1, 0, 3, 0}};
+// Pyramid eye (ball on top)
+static const int kPIrisPts[4][3] = {
+	{15, 0, 185}, {15, 15, 200}, {15, 0, 215}, {15, -15, 200}
+};
+static const int kPIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
+static const int kPPupilPts[4][3] = {
+	{16, 0, 194}, {16, 6, 200}, {16, 0, 206}, {16, -6, 200}
+};
+static const int kPPupilSurf[1][8] = {{kColorPupil, 4, 0, 1, 2, 3, 0, 0}};
+
+static const Colony::ColonyEngine::PrismPartDef kPyramidBodyDef = {5, kPyramidPts, 4, kPyramidSurf};
+static const Colony::ColonyEngine::PrismPartDef kPShadowDef = {4, kPShadowPts, 1, kPShadowSurf};
+static const Colony::ColonyEngine::PrismPartDef kPIrisDef = {4, kPIrisPts, 1, kPIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kPPupilDef = {4, kPPupilPts, 1, kPPupilSurf};
+
+// --- Cube (type 3) --- (octahedron)
+static const int kCubePts[6][3] = {
+	{0, 0, 200}, {100, 0, 100}, {0, 100, 100}, {-100, 0, 100}, {0, -100, 100}, {0, 0, 0}
+};
+static const int kCubeSurf[8][8] = {
+	{kColorCube, 3, 0, 1, 2, 0, 0, 0}, {kColorCube, 3, 0, 2, 3, 0, 0, 0},
+	{kColorCube, 3, 0, 3, 4, 0, 0, 0}, {kColorCube, 3, 0, 4, 1, 0, 0, 0},
+	{kColorCube, 3, 5, 2, 1, 5, 0, 0}, {kColorCube, 3, 5, 3, 2, 5, 0, 0},
+	{kColorCube, 3, 5, 4, 3, 5, 0, 0}, {kColorCube, 3, 5, 1, 4, 5, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kCubeBodyDef = {6, kCubePts, 8, kCubeSurf};
+
+// --- UPyramid (type 4) --- (inverted pyramid)
+static const int kUPyramidPts[5][3] = {
+	{-75, 75, 190}, {75, 75, 190}, {75, -75, 190}, {-75, -75, 190}, {0, 0, 30}
+};
+static const int kUPyramidSurf[5][8] = {
+	{kColorUPyramid, 3, 0, 1, 4, 0, 0, 0}, {kColorUPyramid, 3, 1, 2, 4, 1, 0, 0},
+	{kColorUPyramid, 3, 2, 3, 4, 2, 0, 0}, {kColorUPyramid, 3, 3, 0, 4, 3, 0, 0},
+	{kColorUPyramid, 4, 3, 2, 1, 0, 3, 0}
+};
+static const int kUPShadowPts[4][3] = {
+	{-25, 25, 0}, {25, 25, 0}, {25, -25, 0}, {-25, -25, 0}
+};
+static const int kUPShadowSurf[1][8] = {{kColorShadow, 4, 3, 2, 1, 0, 3, 0}};
+
+static const Colony::ColonyEngine::PrismPartDef kUPyramidBodyDef = {5, kUPyramidPts, 5, kUPyramidSurf};
+static const Colony::ColonyEngine::PrismPartDef kUPShadowDef = {4, kUPShadowPts, 1, kUPShadowSurf};
+
+// --- Eye (type 1) ---
+// Ball is rendered by draw3DSphere(), not as a prism
+static const int kEyeIrisPts[4][3] = {
+	{60, 0, 140}, {60, 60, 200}, {60, 0, 260}, {60, -60, 200}
+};
+static const int kEyeIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
+static const int kEyePupilPts[4][3] = {
+	{66, 0, 175}, {66, 25, 200}, {66, 0, 225}, {66, -25, 200}
+};
+static const int kEyePupilSurf[1][8] = {{kColorBlack, 4, 0, 1, 2, 3, 0, 0}};
+
+static const Colony::ColonyEngine::PrismPartDef kEyeIrisDef = {4, kEyeIrisPts, 1, kEyeIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kEyePupilDef = {4, kEyePupilPts, 1, kEyePupilSurf};
+
+// --- Floating Pyramid (type 6) --- egg on ground
+static const int kFPyramidPts[5][3] = {
+	{-75, 75, 0}, {75, 75, 0}, {75, -75, 0}, {-75, -75, 0}, {0, 0, 170}
+};
+static const Colony::ColonyEngine::PrismPartDef kFPyramidBodyDef = {5, kFPyramidPts, 4, kPyramidSurf};
+
+// --- Small Pyramid (type 10) ---
+static const int kSPyramidPts[5][3] = {
+	{-40, 40, 0}, {40, 40, 0}, {40, -40, 0}, {-40, -40, 0}, {0, 0, 100}
+};
+static const Colony::ColonyEngine::PrismPartDef kSPyramidBodyDef = {5, kSPyramidPts, 4, kPyramidSurf};
+
+// --- Mini Pyramid (type 14) ---
+static const int kMPyramidPts[5][3] = {
+	{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0}, {0, 0, 50}
+};
+static const Colony::ColonyEngine::PrismPartDef kMPyramidBodyDef = {5, kMPyramidPts, 4, kPyramidSurf};
+
+// --- Floating Cube (type 7) ---
+static const int kFCubePts[6][3] = {
+	{0, 0, 150}, {75, 0, 75}, {0, 75, 75}, {-75, 0, 75}, {0, -75, 75}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kFCubeBodyDef = {6, kFCubePts, 8, kCubeSurf};
+
+// --- Small Cube (type 11) ---
+static const int kSCubePts[6][3] = {
+	{0, 0, 100}, {50, 0, 50}, {0, 50, 50}, {-50, 0, 50}, {0, -50, 50}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kSCubeBodyDef = {6, kSCubePts, 8, kCubeSurf};
+
+// --- Mini Cube (type 15) ---
+static const int kMCubePts[6][3] = {
+	{0, 0, 50}, {25, 0, 25}, {0, 25, 25}, {-25, 0, 25}, {0, -25, 25}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kMCubeBodyDef = {6, kMCubePts, 8, kCubeSurf};
+
+// --- Floating UPyramid (type 8) ---
+static const int kFUPyramidPts[5][3] = {
+	{-75, 75, 170}, {75, 75, 170}, {75, -75, 170}, {-75, -75, 170}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kFUPyramidBodyDef = {5, kFUPyramidPts, 5, kUPyramidSurf};
+
+// --- Small UPyramid (type 12) ---
+static const int kSUPyramidPts[5][3] = {
+	{-40, 40, 100}, {40, 40, 100}, {40, -40, 100}, {-40, -40, 100}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kSUPyramidBodyDef = {5, kSUPyramidPts, 5, kUPyramidSurf};
+
+// --- Mini UPyramid (type 16) ---
+static const int kMUPyramidPts[5][3] = {
+	{-20, 20, 50}, {20, 20, 50}, {20, -20, 50}, {-20, -20, 50}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kMUPyramidBodyDef = {5, kMUPyramidPts, 5, kUPyramidSurf};
+
+// --- Floating Eye (type 5) ---
+static const int kFEyeIrisPts[4][3] = {
+	{60, 0, 40}, {60, 60, 100}, {60, 0, 160}, {60, -60, 100}
+};
+static const int kFEyePupilPts[4][3] = {
+	{66, 0, 75}, {66, 25, 100}, {66, 0, 125}, {66, -25, 100}
+};
+static const Colony::ColonyEngine::PrismPartDef kFEyeIrisDef = {4, kFEyeIrisPts, 1, kEyeIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kFEyePupilDef = {4, kFEyePupilPts, 1, kEyePupilSurf};
+
+// --- Small Eye (type 9) ---
+static const int kSEyeIrisPts[4][3] = {
+	{30, 0, 20}, {30, 30, 50}, {30, 0, 80}, {30, -30, 50}
+};
+static const int kSEyePupilPts[4][3] = {
+	{33, 0, 38}, {33, 13, 50}, {33, 0, 63}, {33, -13, 50}
+};
+static const Colony::ColonyEngine::PrismPartDef kSEyeIrisDef = {4, kSEyeIrisPts, 1, kEyeIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kSEyePupilDef = {4, kSEyePupilPts, 1, kEyePupilSurf};
+
+// --- Mini Eye (type 13) ---
+static const int kMEyeIrisPts[4][3] = {
+	{15, 0, 10}, {15, 15, 25}, {15, 0, 40}, {15, -15, 25}
+};
+static const int kMEyePupilPts[4][3] = {
+	{16, 0, 19}, {16, 6, 25}, {16, 0, 31}, {16, -6, 25}
+};
+static const Colony::ColonyEngine::PrismPartDef kMEyeIrisDef = {4, kMEyeIrisPts, 1, kEyeIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kMEyePupilDef = {4, kMEyePupilPts, 1, kEyePupilSurf};
+
+// --- Queen (type 17) ---
+// Queen eye (ball rendered by draw3DSphere)
+static const int kQIrisPts[4][3] = {
+	{15, 0, 140}, {15, 15, 155}, {15, 0, 170}, {15, -15, 155}
+};
+static const int kQIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
+static const int kQPupilPts[4][3] = {
+	{16, 0, 148}, {16, 6, 155}, {16, 0, 161}, {16, -6, 155}
+};
+static const int kQPupilSurf[1][8] = {{kColorPupil, 4, 0, 1, 2, 3, 0, 0}};
+// Queen abdomen
+static const int kQAbdomenPts[9][3] = {
+	{120, 0, 130}, {30, 0, 160}, {30, 0, 100}, {30, 50, 130}, {30, -50, 130},
+	{0, 0, 150}, {0, 0, 110}, {0, 25, 130}, {0, -25, 130}
+};
+static const int kQAbdomenSurf[9][8] = {
+	{kColorQueen, 3, 0, 3, 1, 0, 0, 0}, {kColorQueen, 3, 0, 1, 4, 0, 0, 0},
+	{kColorQueen, 3, 0, 2, 3, 0, 0, 0}, {kColorQueen, 3, 0, 4, 2, 0, 0, 0},
+	{kColorQueen, 4, 1, 5, 8, 4, 1, 0}, {kColorQueen, 4, 1, 3, 7, 5, 1, 0},
+	{kColorQueen, 4, 2, 4, 8, 6, 2, 0}, {kColorQueen, 4, 2, 6, 7, 3, 2, 0},
+	{kColorClear, 4, 5, 7, 6, 8, 5, 0}
+};
+// Queen thorax
+static const int kQThoraxPts[9][3] = {
+	{-120, 0, 130}, {-50, 0, 170}, {-50, 0, 90}, {-50, 60, 130}, {-50, -60, 130},
+	{0, 0, 150}, {0, 0, 110}, {0, 25, 130}, {0, -25, 130}
+};
+static const int kQThoraxSurf[8][8] = {
+	{kColorQueen, 3, 0, 1, 3, 0, 0, 0}, {kColorQueen, 3, 0, 4, 1, 0, 0, 0},
+	{kColorQueen, 3, 0, 3, 2, 0, 0, 0}, {kColorQueen, 3, 0, 2, 4, 0, 0, 0},
+	{kColorQueen, 4, 1, 4, 8, 5, 1, 0}, {kColorQueen, 4, 1, 5, 7, 3, 1, 0},
+	{kColorQueen, 4, 2, 6, 8, 4, 2, 0}, {kColorQueen, 4, 2, 3, 7, 6, 2, 0}
+};
+// Queen wings
+static const int kQLWingPts[4][3] = {
+	{80, 0, 140}, {-40, 10, 200}, {-120, 60, 170}, {-40, 120, 140}
+};
+static const int kQLWingSurf[1][8] = {{kColorClear, 4, 0, 1, 2, 3, 0, 0}};
+static const int kQRWingPts[4][3] = {
+	{80, 0, 140}, {-40, -10, 200}, {-120, -60, 170}, {-40, -120, 140}
+};
+static const int kQRWingSurf[1][8] = {{kColorClear, 4, 0, 1, 2, 3, 0, 0}};
+
+static const Colony::ColonyEngine::PrismPartDef kQIrisDef = {4, kQIrisPts, 1, kQIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kQPupilDef = {4, kQPupilPts, 1, kQPupilSurf};
+static const Colony::ColonyEngine::PrismPartDef kQAbdomenDef = {9, kQAbdomenPts, 9, kQAbdomenSurf};
+static const Colony::ColonyEngine::PrismPartDef kQThoraxDef = {9, kQThoraxPts, 8, kQThoraxSurf};
+static const Colony::ColonyEngine::PrismPartDef kQLWingDef = {4, kQLWingPts, 1, kQLWingSurf};
+static const Colony::ColonyEngine::PrismPartDef kQRWingDef = {4, kQRWingPts, 1, kQRWingSurf};
+
+// --- Drone / Soldier (types 18, 19) ---
+static const int kDAbdomenPts[6][3] = {
+	{0, 0, 170}, {120, 0, 130}, {0, 100, 130}, {-130, 0, 130}, {0, -100, 130}, {0, 0, 100}
+};
+static const int kDAbdomenSurf[8][8] = {
+	{kColorDrone, 3, 0, 1, 2, 0, 0, 0}, {kColorDrone, 3, 0, 2, 3, 0, 0, 0},
+	{kColorDrone, 3, 0, 3, 4, 0, 0, 0}, {kColorDrone, 3, 0, 4, 1, 0, 0, 0},
+	{kColorDrone, 3, 5, 2, 1, 5, 0, 0}, {kColorDrone, 3, 5, 3, 2, 5, 0, 0},
+	{kColorDrone, 3, 5, 4, 3, 5, 0, 0}, {kColorDrone, 3, 5, 1, 4, 5, 0, 0}
+};
+// Drone static pincers (llPincer/rrPincer)
+static const int kDLLPincerPts[4][3] = {
+	{0, 0, 130}, {50, -2, 130}, {35, -20, 140}, {35, -20, 120}
+};
+static const int kDLPincerSurf[4][8] = {
+	{kColorClaw1, 3, 0, 2, 1, 0, 0, 0}, {kColorClaw1, 3, 0, 1, 3, 0, 0, 0},
+	{kColorClaw2, 3, 0, 3, 2, 0, 0, 0}, {kColorClaw2, 3, 1, 2, 3, 1, 0, 0}
+};
+static const int kDRRPincerPts[4][3] = {
+	{0, 0, 130}, {50, 2, 130}, {35, 20, 140}, {35, 20, 120}
+};
+static const int kDRPincerSurf[4][8] = {
+	{kColorClaw1, 3, 0, 1, 2, 0, 0, 0}, {kColorClaw1, 3, 0, 3, 1, 0, 0, 0},
+	{kColorClaw2, 3, 0, 2, 3, 0, 0, 0}, {kColorClaw2, 3, 1, 3, 2, 1, 0, 0}
+};
+// Drone eyes
+static const int kDLEyePts[3][3] = {
+	{60, 0, 150}, {60, 50, 130}, {60, 25, 150}
+};
+static const int kDLEyeSurf[2][8] = {
+	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
+};
+static const int kDREyePts[3][3] = {
+	{60, 0, 150}, {60, -50, 130}, {60, -25, 150}
+};
+static const int kDREyeSurf[2][8] = {
+	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
+};
+
+static const Colony::ColonyEngine::PrismPartDef kDAbdomenDef = {6, kDAbdomenPts, 8, kDAbdomenSurf};
+static const Colony::ColonyEngine::PrismPartDef kDLLPincerDef = {4, kDLLPincerPts, 4, kDLPincerSurf};
+static const Colony::ColonyEngine::PrismPartDef kDRRPincerDef = {4, kDRRPincerPts, 4, kDRPincerSurf};
+static const Colony::ColonyEngine::PrismPartDef kDLEyeDef = {3, kDLEyePts, 2, kDLEyeSurf};
+static const Colony::ColonyEngine::PrismPartDef kDREyeDef = {3, kDREyePts, 2, kDREyeSurf};
+
+// --- Snoop (type 20) ---
+static const int kSnoopAbdomenPts[4][3] = {
+	{0, 100, 0}, {-180, 0, 0}, {0, -100, 0}, {0, 0, 70}
+};
+static const int kSnoopAbdomenSurf[2][8] = {
+	{kColorTopSnoop, 3, 0, 1, 3, 0, 0, 0}, {kColorTopSnoop, 3, 2, 3, 1, 2, 0, 0}
+};
+static const int kSnoopHeadPts[4][3] = {
+	{0, 100, 0}, {150, 0, 0}, {0, -100, 0}, {0, 0, 70}
+};
+static const int kSnoopHeadSurf[3][8] = {
+	{kColorTopSnoop, 3, 0, 3, 1, 0, 0, 0}, {kColorTopSnoop, 3, 2, 1, 3, 2, 0, 0},
+	{kColorBottomSnoop, 3, 0, 1, 2, 0, 0, 0}
+};
+
+static const Colony::ColonyEngine::PrismPartDef kSnoopAbdomenDef = {4, kSnoopAbdomenPts, 2, kSnoopAbdomenSurf};
+static const Colony::ColonyEngine::PrismPartDef kSnoopHeadDef = {4, kSnoopHeadPts, 3, kSnoopHeadSurf};
+
 
 void ColonyEngine::quadrant() {
 	int remain;
@@ -1006,6 +1300,79 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 	}
 }
 
+void ColonyEngine::draw3DSphere(const Thing &obj, int pt0x, int pt0y, int pt0z,
+                                int pt1x, int pt1y, int pt1z,
+                                uint32 fillColor, uint32 outlineColor) {
+	// Sphere defined by two object-local points: pt0 (center bottom) and pt1 (center top).
+	// Center is midpoint, radius is half the distance (along z typically).
+	// Rendered as a billboard polygon facing the camera.
+	const uint8 ang = obj.where.ang + 32;
+	const long rotCos = _cost[ang];
+	const long rotSin = _sint[ang];
+	const bool lit = (_corePower[_coreIndex] > 0);
+
+	// Transform both points to world space
+	long rx0 = ((long)pt0x * rotCos - (long)pt0y * rotSin) >> 7;
+	long ry0 = ((long)pt0x * rotSin + (long)pt0y * rotCos) >> 7;
+	float wx0 = (float)(rx0 + obj.where.xloc);
+	float wy0 = (float)(ry0 + obj.where.yloc);
+	float wz0 = (float)(pt0z - 160);
+
+	long rx1 = ((long)pt1x * rotCos - (long)pt1y * rotSin) >> 7;
+	long ry1 = ((long)pt1x * rotSin + (long)pt1y * rotCos) >> 7;
+	float wx1 = (float)(rx1 + obj.where.xloc);
+	float wy1 = (float)(ry1 + obj.where.yloc);
+	float wz1 = (float)(pt1z - 160);
+
+	// Center and radius
+	float cx = (wx0 + wx1) * 0.5f;
+	float cy = (wy0 + wy1) * 0.5f;
+	float cz = (wz0 + wz1) * 0.5f;
+	float dx = wx1 - wx0, dy = wy1 - wy0, dz = wz1 - wz0;
+	float radius = sqrtf(dx * dx + dy * dy + dz * dz) * 0.5f;
+
+	// Billboard: create a polygon perpendicular to the camera direction.
+	// 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);
+	if (viewLen < 0.001f) return;
+
+	// "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;
+
+	// Create 12-sided polygon
+	static const int N = 12;
+	float px[N], py[N], pz[N];
+	for (int i = 0; i < N; i++) {
+		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);
+		pz[i] = cz + radius * (sinA * upZ);
+	}
+
+	if (lit) {
+		if (_renderMode == Common::kRenderMacintosh) {
+			int pattern = (fillColor == 15) ? kPatternWhite : kPatternGray;
+			if (!_wireframe) {
+				_gfx->setWireframe(true, pattern == kPatternBlack ? 0 : 255);
+			}
+			_gfx->setStippleData(kMacStippleData[pattern]);
+			_gfx->draw3DPolygon(px, py, pz, N, 0);
+			_gfx->setStippleData(nullptr);
+		} else {
+			_gfx->draw3DPolygon(px, py, pz, N, fillColor);
+		}
+	} else {
+		_gfx->draw3DPolygon(px, py, pz, N, outlineColor);
+	}
+}
+
 void ColonyEngine::computeVisibleCells() {
 	memset(_visibleCell, 0, sizeof(_visibleCell));
 
@@ -1940,6 +2307,89 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		draw3DPrism(obj, kForkliftParts[1], false); // treads
 		draw3DPrism(obj, kForkliftParts[0], false); // cab
 		break;
+	// === Robot types (1-20) ===
+	case kRobEye:
+		draw3DSphere(obj, 0, 0, 100, 0, 0, 200, 15, 15); // ball: white
+		draw3DPrism(obj, kEyeIrisDef, false);
+		draw3DPrism(obj, kEyePupilDef, false);
+		break;
+	case kRobPyramid:
+		draw3DPrism(obj, kPShadowDef, false);
+		draw3DPrism(obj, kPyramidBodyDef, false);
+		draw3DSphere(obj, 0, 0, 175, 0, 0, 200, 15, 15); // ball on top
+		draw3DPrism(obj, kPIrisDef, false);
+		draw3DPrism(obj, kPPupilDef, false);
+		break;
+	case kRobCube:
+		draw3DPrism(obj, kCubeBodyDef, false);
+		break;
+	case kRobUPyramid:
+		draw3DPrism(obj, kUPShadowDef, false);
+		draw3DPrism(obj, kUPyramidBodyDef, false);
+		break;
+	case kRobFEye:
+		draw3DSphere(obj, 0, 0, 0, 0, 0, 100, 15, 15);
+		draw3DPrism(obj, kFEyeIrisDef, false);
+		draw3DPrism(obj, kFEyePupilDef, false);
+		break;
+	case kRobFPyramid:
+		draw3DPrism(obj, kFPyramidBodyDef, false);
+		break;
+	case kRobFCube:
+		draw3DPrism(obj, kFCubeBodyDef, false);
+		break;
+	case kRobFUPyramid:
+		draw3DPrism(obj, kFUPyramidBodyDef, false);
+		break;
+	case kRobSEye:
+		draw3DSphere(obj, 0, 0, 0, 0, 0, 50, 15, 15);
+		draw3DPrism(obj, kSEyeIrisDef, false);
+		draw3DPrism(obj, kSEyePupilDef, false);
+		break;
+	case kRobSPyramid:
+		draw3DPrism(obj, kSPyramidBodyDef, false);
+		break;
+	case kRobSCube:
+		draw3DPrism(obj, kSCubeBodyDef, false);
+		break;
+	case kRobSUPyramid:
+		draw3DPrism(obj, kSUPyramidBodyDef, false);
+		break;
+	case kRobMEye:
+		draw3DSphere(obj, 0, 0, 0, 0, 0, 25, 15, 15);
+		draw3DPrism(obj, kMEyeIrisDef, false);
+		draw3DPrism(obj, kMEyePupilDef, false);
+		break;
+	case kRobMPyramid:
+		draw3DPrism(obj, kMPyramidBodyDef, false);
+		break;
+	case kRobMCube:
+		draw3DPrism(obj, kMCubeBodyDef, false);
+		break;
+	case kRobMUPyramid:
+		draw3DPrism(obj, kMUPyramidBodyDef, false);
+		break;
+	case kRobQueen:
+		draw3DPrism(obj, kQThoraxDef, false);
+		draw3DPrism(obj, kQAbdomenDef, false);
+		draw3DPrism(obj, kQLWingDef, false);
+		draw3DPrism(obj, kQRWingDef, false);
+		draw3DSphere(obj, 0, 0, 130, 0, 0, 155, 15, 15); // queen ball
+		draw3DPrism(obj, kQIrisDef, false);
+		draw3DPrism(obj, kQPupilDef, false);
+		break;
+	case kRobDrone:
+	case kRobSoldier:
+		draw3DPrism(obj, kDAbdomenDef, false);
+		draw3DPrism(obj, kDLLPincerDef, false);
+		draw3DPrism(obj, kDRRPincerDef, false);
+		draw3DPrism(obj, kDLEyeDef, false);
+		draw3DPrism(obj, kDREyeDef, false);
+		break;
+	case kRobSnoop:
+		draw3DPrism(obj, kSnoopAbdomenDef, false);
+		draw3DPrism(obj, kSnoopHeadDef, false);
+		break;
 	default:
 		return false;
 	}


Commit: 55f821b6a31bb6b05f5df2527c4571a198a702a8
    https://github.com/scummvm/scummvm/commit/55f821b6a31bb6b05f5df2527c4571a198a702a8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:23+02:00

Commit Message:
COLONY: adjusted code to follow the code conventions

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/render.cpp
    engines/colony/renderer_opengl.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index a26d8207406..dd304f2128b 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -97,7 +97,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_me.yindex = _me.yloc >> 8;
 	_me.look = 32;
 	_me.ang = 32;
-	_me.type = MENUM;
+	_me.type = kMeNum;
 
 	// Animation system init
 	_backgroundMask = nullptr;
@@ -112,9 +112,12 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	for (int i = 0; i < 4; i++) {
 		_decode1[i] = _decode2[i] = _decode3[i] = 0;
 	}
-	for (int i = 0; i < 6; i++) _animDisplay[i] = 1;
-	for (int i = 0; i < 2; i++) _coreState[i] = _coreHeight[i] = 0;
-	for (int i = 0; i < 3; i++) _corePower[i] = 0;
+	for (int i = 0; i < 6; i++)
+		_animDisplay[i] = 1;
+	for (int i = 0; i < 2; i++)
+		_coreState[i] = _coreHeight[i] = 0;
+	for (int i = 0; i < 3; i++)
+		_corePower[i] = 0;
 	_coreIndex = 0;
 	_orbit = 0;
 	_armor = 0;
@@ -163,7 +166,7 @@ void ColonyEngine::loadMap(int mnum) {
 
 	// expand logic
 	int c = 0;
-	_robotNum = MENUM + 1;
+	_robotNum = kMeNum + 1;
 	for (int i = 0; i < 32; i++) {
 		for (int j = 0; j < 32; j++) {
 			_wall[i][j] = buffer[c++];
@@ -179,7 +182,7 @@ void ColonyEngine::loadMap(int mnum) {
 							memset(&obj, 0, sizeof(obj));
 							obj.alive = 1;
 							obj.visible = 0;
-							obj.type = _mapData[i][j][4][1] + BASEOBJECT;
+							obj.type = _mapData[i][j][4][1] + kBaseObject;
 							obj.where.xloc = (i << 8) + 128;
 							obj.where.yloc = (j << 8) + 128;
 							obj.where.xindex = i;
@@ -200,14 +203,14 @@ void ColonyEngine::loadMap(int mnum) {
 	}
 	free(buffer);
 	_level = mnum;
-	_me.type = MENUM;
+	_me.type = kMeNum;
 
 	getWall();  // restore saved wall state changes (airlocks)
 	doPatch();  // apply object relocations from patch table
 	initRobots();  // spawn robot objects for this level
 
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-		_robotArray[_me.xindex][_me.yindex] = MENUM;
+		_robotArray[_me.xindex][_me.yindex] = kMeNum;
 	debug("Successfully loaded map %d (objects: %d)", mnum, (int)_objects.size());
 }
 
@@ -216,7 +219,8 @@ void ColonyEngine::loadMap(int mnum) {
 void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
 	Thing obj;
 	memset(&obj, 0, sizeof(obj));
-	while (ang > 255) ang -= 256;
+	while (ang > 255)
+		ang -= 256;
 	obj.alive = 1;
 	obj.visible = 0;
 	obj.type = type;
@@ -228,9 +232,9 @@ void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
 	obj.where.ang = ang;
 	obj.where.look = ang;
 
-	// Try to reuse a dead slot (starting after MENUM)
+	// Try to reuse a dead slot (starting after kMeNum)
 	int slot = -1;
-	for (int j = MENUM; j < (int)_objects.size(); j++) {
+	for (int j = kMeNum; j < (int)_objects.size(); j++) {
 		if (!_objects[j].alive) {
 			slot = j;
 			break;
@@ -276,7 +280,8 @@ void ColonyEngine::doPatch() {
 
 // PATCH.C: savewall() — save 5 bytes of map feature data for persistence across level loads.
 void ColonyEngine::saveWall(int x, int y, int direction) {
-	if (_level < 1 || _level > 8) return;
+	if (_level < 1 || _level > 8)
+		return;
 	LevelData &ld = _levelData[_level - 1];
 
 	// Search for existing entry at this location
@@ -303,7 +308,8 @@ void ColonyEngine::saveWall(int x, int y, int direction) {
 
 // PATCH.C: getwall() — restore saved wall bytes into _mapData after level load.
 void ColonyEngine::getWall() {
-	if (_level < 1 || _level > 8) return;
+	if (_level < 1 || _level > 8)
+		return;
 	const LevelData &ld = _levelData[_level - 1];
 	for (int i = 0; i < ld.size; i++) {
 		int x = ld.location[i][0];
@@ -333,7 +339,8 @@ void ColonyEngine::newPatch(int type, const PassPatch &from, const PassPatch &to
 		}
 	}
 	// Create new patch entry (max 100)
-	if (_patches.size() >= 100) return;
+	if (_patches.size() >= 100)
+		return;
 	PatchEntry pe;
 	pe.type = type;
 	pe.from.level = from.level;
@@ -387,7 +394,8 @@ bool ColonyEngine::patchMapFrom(const PassPatch &from, uint8 *mapdata) {
 // Level 1 = no robots; Level 2 = 25; Level 3-4 = 30; Level 5-7 = 35.
 // Robot #1 = QUEEN, #2 = SNOOP, rest = random type weighted by level.
 void ColonyEngine::initRobots() {
-	if (_level == 1) return;  // Level 1 has no robots
+	if (_level == 1)
+		return;  // Level 1 has no robots
 
 	int maxrob;
 	switch (_level) {
@@ -398,7 +406,8 @@ void ColonyEngine::initRobots() {
 	}
 
 	int lvl = _level - 1;
-	if (lvl > 5) lvl = 5;
+	if (lvl > 5)
+		lvl = 5;
 
 	for (int i = 1; i <= maxrob; i++) {
 		uint8 ang = _randomSource.getRandomNumber(255);
@@ -428,7 +437,8 @@ void ColonyEngine::initRobots() {
 		else {
 			// Random type weighted by level difficulty
 			int rnd = _randomSource.getRandomNumber(lvl);
-			if (rnd > 5) rnd = 5;
+			if (rnd > 5)
+				rnd = 5;
 			switch (rnd) {
 			case 0: type = kRobCube; break;
 			case 1: type = kRobPyramid; break;
@@ -473,16 +483,16 @@ Common::Error ColonyEngine::run() {
 
 	// Setup a palette with standard 16 colors followed by grayscale
 	byte pal[256 * 3];
-	static const byte ega_colors[16][3] = {
+	static const byte egaColors[16][3] = {
 		{0, 0, 0}, {0, 0, 170}, {0, 170, 0}, {0, 170, 170},
 		{170, 0, 0}, {170, 0, 170}, {170, 85, 0}, {170, 170, 170},
 		{85, 85, 85}, {85, 85, 255}, {85, 255, 85}, {85, 255, 255},
 		{255, 85, 85}, {255, 85, 255}, {255, 255, 85}, {255, 255, 255}
 	};
 	for (int i = 0; i < 16; i++) {
-		pal[i * 3 + 0] = ega_colors[i][0];
-		pal[i * 3 + 1] = ega_colors[i][1];
-		pal[i * 3 + 2] = ega_colors[i][2];
+		pal[i * 3 + 0] = egaColors[i][0];
+		pal[i * 3 + 1] = egaColors[i][1];
+		pal[i * 3 + 2] = egaColors[i][2];
 	}
 	for (int i = 16; i < 256; i++) {
 		pal[i * 3 + 0] = i;
@@ -700,7 +710,8 @@ void ColonyEngine::scrollInfo() {
 
 bool ColonyEngine::loadAnimation(const Common::String &name) {
 	_animationName = name;
-	for (int i = 0; i < 6; i++) _animDisplay[i] = 1;
+	for (int i = 0; i < 6; i++)
+		_animDisplay[i] = 1;
 	Common::String fileName = name + ".pic";
 	Common::File file;
 	if (!file.open(Common::Path(fileName))) {
@@ -770,11 +781,15 @@ bool ColonyEngine::loadAnimation(const Common::String &name) {
 }
 
 void ColonyEngine::deleteAnimation() {
-	delete _backgroundMask; _backgroundMask = nullptr;
-	delete _backgroundFG; _backgroundFG = nullptr;
-	for (uint i = 0; i < _cSprites.size(); i++) delete _cSprites[i];
+	delete _backgroundMask;
+	_backgroundMask = nullptr;
+	delete _backgroundFG;
+	_backgroundFG = nullptr;
+	for (uint i = 0; i < _cSprites.size(); i++)
+		delete _cSprites[i];
 	_cSprites.clear();
-	for (uint i = 0; i < _lSprites.size(); i++) delete _lSprites[i];
+	for (uint i = 0; i < _lSprites.size(); i++)
+		delete _lSprites[i];
 	_lSprites.clear();
 }
 
@@ -790,39 +805,42 @@ void ColonyEngine::playAnimation() {
 	if (_animationName == "security" && !_unlocked) {
 		for (int i = 0; i < 4; i++) {
 			_decode1[i] = (uint8)(2 + _randomSource.getRandomNumber(3));
-			SetObjectState(27 + i, _decode1[i]);
+			setObjectState(27 + i, _decode1[i]);
 		}
 	} else if (_animationName == "reactor") {
 		for (int i = 0; i < 6; i++) {
-			SetObjectOnOff(14 + i * 2, false);
-			SetObjectState(13 + i * 2, 1);
+			setObjectOnOff(14 + i * 2, false);
+			setObjectState(13 + i * 2, 1);
 		}
 	} else if (_animationName == "controls") {
 		switch (_corePower[_coreIndex]) {
-		case 0: SetObjectState(2, 1); SetObjectState(5, 1); break;
-		case 1: SetObjectState(2, 1); SetObjectState(5, 2); break;
-		case 2: SetObjectState(2, 2); SetObjectState(5, 1); break;
+		case 0: setObjectState(2, 1); setObjectState(5, 1); break;
+		case 1: setObjectState(2, 1); setObjectState(5, 2); break;
+		case 2: setObjectState(2, 2); setObjectState(5, 1); break;
 		}
 	} else if (_animationName == "desk") {
 		if (!(_action0 == 11 || _action0 == 18)) {
-			for (int i = 1; i <= 5; i++) SetObjectOnOff(i, false);
+			for (int i = 1; i <= 5; i++)
+				setObjectOnOff(i, false);
 		} else {
 			uint8 *decode = (_action0 == 11) ? _decode2 : _decode3;
 			for (int i = 0; i < 4; i++) {
 				if (decode[i])
-					SetObjectState(i + 2, decode[i]);
+					setObjectState(i + 2, decode[i]);
 				else
-					SetObjectState(i + 2, 1);
+					setObjectState(i + 2, 1);
 			}
 		}
 
 		if (_action0 != 10) {
-			SetObjectOnOff(23, false);
-			SetObjectOnOff(24, false);
+			setObjectOnOff(23, false);
+			setObjectOnOff(24, false);
 		}
-		if (_action0 != 30) SetObjectOnOff(6, false); // Teeth
+		if (_action0 != 30)
+			setObjectOnOff(6, false); // Teeth
 		if (_action0 != 33) { // Jack-in-the-box
-			for (int i = 18; i <= 21; i++) SetObjectOnOff(i, false);
+			for (int i = 18; i <= 21; i++)
+				setObjectOnOff(i, false);
 		}
 
 		int ntype = _action1 / 10;
@@ -831,34 +849,34 @@ void ColonyEngine::playAnimation() {
 		case 1:
 		case 2:
 		case 3:
-			SetObjectOnOff(7, false);
-			SetObjectOnOff(8, false);
-			SetObjectOnOff(9, false);
-			SetObjectOnOff(22, false);
-			SetObjectOnOff(25, false);
+			setObjectOnOff(7, false);
+			setObjectOnOff(8, false);
+			setObjectOnOff(9, false);
+			setObjectOnOff(22, false);
+			setObjectOnOff(25, false);
 			break;
 		case 4: // letters
-			SetObjectOnOff(22, false);
-			SetObjectOnOff(9, false);
-			SetObjectOnOff(25, false);
+			setObjectOnOff(22, false);
+			setObjectOnOff(9, false);
+			setObjectOnOff(25, false);
 			break;
 		case 5: // book
-			SetObjectOnOff(7, false);
-			SetObjectOnOff(8, false);
-			SetObjectOnOff(9, false);
-			SetObjectOnOff(25, false);
+			setObjectOnOff(7, false);
+			setObjectOnOff(8, false);
+			setObjectOnOff(9, false);
+			setObjectOnOff(25, false);
 			break;
 		case 6: // clipboard
-			SetObjectOnOff(22, false);
-			SetObjectOnOff(7, false);
-			SetObjectOnOff(8, false);
-			SetObjectOnOff(25, false);
+			setObjectOnOff(22, false);
+			setObjectOnOff(7, false);
+			setObjectOnOff(8, false);
+			setObjectOnOff(25, false);
 			break;
 		case 7: // postit
-			SetObjectOnOff(22, false);
-			SetObjectOnOff(7, false);
-			SetObjectOnOff(8, false);
-			SetObjectOnOff(9, false);
+			setObjectOnOff(22, false);
+			setObjectOnOff(7, false);
+			setObjectOnOff(8, false);
+			setObjectOnOff(9, false);
 			break;
 		}
 	} else if (_animationName == "vanity") {
@@ -870,33 +888,33 @@ void ColonyEngine::playAnimation() {
 		}
 		// DOS DoVanity: set suit state on mirror display (object 1)
 		if (_weapons && _armor)
-			SetObjectState(1, 3);
+			setObjectState(1, 3);
 		else if (_weapons)
-			SetObjectState(1, 2);
+			setObjectState(1, 2);
 		else if (_armor)
-			SetObjectState(1, 1);
+			setObjectState(1, 1);
 		else
-			SetObjectState(1, 4);
+			setObjectState(1, 4);
 		// Badge only visible on level 1
 		if (_level != 1)
-			SetObjectOnOff(14, false);
+			setObjectOnOff(14, false);
 		// Hide items based on action0 (num parameter in DOS)
 		if (_action0 < 90) { // coffee cup only
-			SetObjectOnOff(4, false);
-			SetObjectOnOff(7, false);
-			SetObjectOnOff(13, false);
+			setObjectOnOff(4, false);
+			setObjectOnOff(7, false);
+			setObjectOnOff(13, false);
 		} else if (_action0 < 100) { // paper
-			SetObjectOnOff(12, false);
-			SetObjectOnOff(4, false);
-			SetObjectOnOff(7, false);
+			setObjectOnOff(12, false);
+			setObjectOnOff(4, false);
+			setObjectOnOff(7, false);
 		} else if (_action0 < 110) { // diary
-			SetObjectOnOff(12, false);
-			SetObjectOnOff(13, false);
-			SetObjectOnOff(7, false);
+			setObjectOnOff(12, false);
+			setObjectOnOff(13, false);
+			setObjectOnOff(7, false);
 		} else if (_action0 < 120) { // book
-			SetObjectOnOff(12, false);
-			SetObjectOnOff(13, false);
-			SetObjectOnOff(4, false);
+			setObjectOnOff(12, false);
+			setObjectOnOff(13, false);
+			setObjectOnOff(4, false);
 		}
 	}
 
@@ -947,11 +965,10 @@ void ColonyEngine::playAnimation() {
 }
 
 void ColonyEngine::updateAnimation() {
-	static uint32 lastUpdate = 0;
 	uint32 now = _system->getMillis();
-	if (now - lastUpdate < 50) // Reduced to 50ms (20 fps) to make it "move"
+	if (now - _lastAnimUpdate < 50) // Reduced to 50ms (20 fps) to make it "move"
 		return;
-	lastUpdate = now;
+	_lastAnimUpdate = now;
 
 	for (uint i = 0; i < _lSprites.size(); i++) {
 		ComplexSprite *ls = _lSprites[i];
@@ -1003,13 +1020,16 @@ void ColonyEngine::drawAnimation() {
 
 void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
 	ComplexSprite *ls = _lSprites[index];
-	if (!ls->onoff) return;
+	if (!ls->onoff)
+		return;
 
 	int cnum = ls->current;
-	if (cnum < 0 || cnum >= (int)ls->objects.size()) return;
+	if (cnum < 0 || cnum >= (int)ls->objects.size())
+		return;
 
 	int spriteIdx = ls->objects[cnum].spritenum;
-	if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size()) return;
+	if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size())
+		return;
 
 	Sprite *s = _cSprites[spriteIdx];
 	int x = ox + ls->xloc + ls->objects[cnum].xloc + s->clip.left;
@@ -1019,7 +1039,8 @@ void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
 }
 
 void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y) {
-	if (!img || !img->data) return;
+	if (!img || !img->data)
+		return;
 
 	for (int iy = 0; iy < img->height; iy++) {
 		for (int ix = 0; ix < img->width; ix++) {
@@ -1031,7 +1052,8 @@ void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y) {
 				maskSet = (mask->data[byteIdx] & (1 << bitIdx)) != 0;
 			}
 
-			if (!maskSet) continue;
+			if (!maskSet)
+				continue;
 
 			bool fgSet = (img->data[byteIdx] & (1 << bitIdx)) != 0;
 			uint32 color = fgSet ? 15 : 0;
@@ -1094,13 +1116,16 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 
 	for (int i = _lSprites.size() - 1; i >= 0; i--) {
 		ComplexSprite *ls = _lSprites[i];
-		if (!ls->onoff) continue;
+		if (!ls->onoff)
+			continue;
 
 		int cnum = ls->current;
-		if (cnum < 0 || cnum >= (int)ls->objects.size()) continue;
+		if (cnum < 0 || cnum >= (int)ls->objects.size())
+			continue;
 
 		int spriteIdx = ls->objects[cnum].spritenum;
-		if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size()) continue;
+		if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size())
+			continue;
 
 		Sprite *s = _cSprites[spriteIdx];
 		int xloc = ls->xloc + ls->objects[cnum].xloc;
@@ -1109,7 +1134,8 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 		Common::Rect r = s->clip;
 		r.translate(xloc, yloc);
 
-		if (!r.contains(pt)) continue;
+		if (!r.contains(pt))
+			continue;
 
 		// Pixel-perfect mask test (matches DOS WhichlSprite)
 		Image *mask = s->mask;
@@ -1148,9 +1174,11 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 			ComplexSprite *ls = _lSprites[i];
 			if (ls->onoff) {
 				int cnum = ls->current;
-				if (cnum < 0 || cnum >= (int)ls->objects.size()) continue;
+				if (cnum < 0 || cnum >= (int)ls->objects.size())
+					continue;
 				int spriteIdx = ls->objects[cnum].spritenum;
-				if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size()) continue;
+				if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size())
+					continue;
 				Sprite *s = _cSprites[spriteIdx];
 
 				int xloc = ls->xloc + ls->objects[cnum].xloc;
@@ -1219,7 +1247,7 @@ void ColonyEngine::handleAnimationClick(int item) {
 		if (item == 12) { // Coffee cup - spill animation
 			if (!_doorOpen) { // reuse _doorOpen as "spilled" flag
 				for (int i = 1; i < 6; i++) {
-					SetObjectState(12, i);
+					setObjectState(12, i);
 					drawAnimation();
 					_gfx->copyToScreen();
 					_system->delayMillis(50);
@@ -1242,24 +1270,28 @@ void ColonyEngine::handleAnimationClick(int item) {
 			doText(261 + _creature, 0);
 		} else if (item == 5) { // Prev
 			_creature--;
-			if (_creature == 0) _creature = 8;
-			SetObjectState(1, _creature);
+			if (_creature == 0)
+				_creature = 8;
+			setObjectState(1, _creature);
 		} else if (item == 6) { // Next
 			_creature++;
-			if (_creature == 9) _creature = 1;
-			SetObjectState(1, _creature);
+			if (_creature == 9)
+				_creature = 1;
+			setObjectState(1, _creature);
 		}
 	} else if (_animationName == "teleshow") {
 		if (item == 2) { // Speaker
 			doText(269 + _creature, 0);
 		} else if (item == 5) { // Prev
 			_creature--;
-			if (_creature == 0) _creature = 7;
-			SetObjectState(1, _creature);
+			if (_creature == 0)
+				_creature = 7;
+			setObjectState(1, _creature);
 		} else if (item == 6) { // Next
 			_creature++;
-			if (_creature == 8) _creature = 1;
-			SetObjectState(1, _creature);
+			if (_creature == 8)
+				_creature = 1;
+			setObjectState(1, _creature);
 		}
 	} else if (_animationName == "reactor" || _animationName == "security" || _animationName == "suit") {
 		if (item >= 1 && item <= 10 && _animationName != "suit") {
@@ -1275,7 +1307,7 @@ void ColonyEngine::handleAnimationClick(int item) {
 				_animDisplay[i] = 1;
 			// Reset keypad buttons to unpressed state
 			for (int i = 1; i <= 10; i++)
-				SetObjectState(i, 1);
+				setObjectState(i, 1);
 			refreshAnimationDisplay();
 			drawAnimation();
 			_gfx->copyToScreen();
@@ -1317,44 +1349,56 @@ void ColonyEngine::handleAnimationClick(int item) {
 			if (item == 1) { // Armor
 				if (_armor == 3) {
 					for (int i = 6; i >= 1; i--) {
-						SetObjectState(1, i);
-						SetObjectState(3, i / 2 + 1);
-						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(30);
+						setObjectState(1, i);
+						setObjectState(3, i / 2 + 1);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(30);
 					}
 					_armor = 0;
 				} else {
-					SetObjectState(1, (_armor * 2 + 1) + 1); // intermediate/pressed
-					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(50);
+					setObjectState(1, (_armor * 2 + 1) + 1); // intermediate/pressed
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(50);
 					_armor++;
 				}
-				SetObjectState(1, _armor * 2 + 1); // target state
-				SetObjectState(3, _armor + 1); // display state
-				drawAnimation(); _gfx->copyToScreen();
-				if (_armor == 3 && _weapons == 3) _corePower[_coreIndex] = 2;
+				setObjectState(1, _armor * 2 + 1); // target state
+				setObjectState(3, _armor + 1); // display state
+				drawAnimation();
+				_gfx->copyToScreen();
+				if (_armor == 3 && _weapons == 3)
+					_corePower[_coreIndex] = 2;
 			} else if (item == 2) { // Weapons
 				if (_weapons == 3) {
 					for (int i = 6; i >= 1; i--) {
-						SetObjectState(2, i);
-						SetObjectState(4, i / 2 + 1);
-						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(30);
+						setObjectState(2, i);
+						setObjectState(4, i / 2 + 1);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(30);
 					}
 					_weapons = 0;
 				} else {
-					SetObjectState(2, (_weapons * 2 + 1) + 1); // intermediate/pressed
-					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(50);
+					setObjectState(2, (_weapons * 2 + 1) + 1); // intermediate/pressed
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(50);
 					_weapons++;
 				}
-				SetObjectState(2, _weapons * 2 + 1);
-				SetObjectState(4, _weapons + 1);
-				drawAnimation(); _gfx->copyToScreen();
-				if (_armor == 3 && _weapons == 3) _corePower[_coreIndex] = 2;
+				setObjectState(2, _weapons * 2 + 1);
+				setObjectState(4, _weapons + 1);
+				drawAnimation();
+				_gfx->copyToScreen();
+				if (_armor == 3 && _weapons == 3)
+					_corePower[_coreIndex] = 2;
 			}
 		}
 		if (_animationName == "reactor" || _animationName == "security") {
 			if (item <= 12) {
-				// SetObjectState(item, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
+				// setObjectState(item, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
 				if (item > 10) // Clear/Enter should return to Off
-					SetObjectState(item, 1);
+					setObjectState(item, 1);
 				drawAnimation();
 				_gfx->copyToScreen();
 			}
@@ -1366,18 +1410,22 @@ void ColonyEngine::handleAnimationClick(int item) {
 			if (_doorOpen) {
 				for (int i = 3; i >= 1; i--) {
 					_doorOpen = !_doorOpen;
-					SetObjectState(2, i);
-					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+					setObjectState(2, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(80);
 				}
 			} else {
 				for (int i = 1; i < 4; i++) {
 					_doorOpen = !_doorOpen;
-					SetObjectState(2, i);
-					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+					setObjectState(2, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(80);
 				}
 			}
 		}
-		if (item == 1 || (item == 101 && ObjectState(2) == 3)) {
+		if (item == 1 || (item == 101 && objectState(2) == 3)) {
 			_animationResult = 1;
 			_animationRunning = false;
 		}
@@ -1391,13 +1439,17 @@ void ColonyEngine::handleAnimationClick(int item) {
 			_sound->play(Sound::kAirlock);
 			if (_doorOpen) {
 				for (int i = 3; i >= 1; i--) {
-					SetObjectState(2, i);
-					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+					setObjectState(2, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(80);
 				}
 			} else {
 				for (int i = 1; i < 4; i++) {
-					SetObjectState(2, i);
-					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+					setObjectState(2, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(80);
 				}
 			}
 			_doorOpen = !_doorOpen;
@@ -1416,30 +1468,35 @@ void ColonyEngine::handleAnimationClick(int item) {
 				_sound->play(Sound::kElevator);
 				if (!_doorOpen) {
 					for (int i = 1; i < 4; i++) {
-						SetObjectState(3, i);
-						SetObjectState(4, i);
-						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+						setObjectState(3, i);
+						setObjectState(4, i);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(80);
 					}
 					_doorOpen = true;
 				} else {
 					for (int i = 3; i >= 1; i--) {
-						SetObjectState(4, i);
-						SetObjectState(3, i);
-						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+						setObjectState(4, i);
+						setObjectState(3, i);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(80);
 					}
 					_doorOpen = false;
 				}
 			} else if (item == 2 || (item == 101 && _doorOpen)) {
 				// Enter the elevator (transition to phase 2)
 				_animationResult = 2;
-				SetObjectOnOff(6, true);
-				SetObjectOnOff(7, true);
-				SetObjectOnOff(8, true);
-				SetObjectOnOff(9, true);
-				SetObjectOnOff(10, true);
-				SetObjectOnOff(2, false);
-				SetObjectOnOff(5, false);
-				drawAnimation(); _gfx->copyToScreen();
+				setObjectOnOff(6, true);
+				setObjectOnOff(7, true);
+				setObjectOnOff(8, true);
+				setObjectOnOff(9, true);
+				setObjectOnOff(10, true);
+				setObjectOnOff(2, false);
+				setObjectOnOff(5, false);
+				drawAnimation();
+				_gfx->copyToScreen();
 			} else if (item == 101 && !_doorOpen) {
 				// Exit without entering
 				_animationResult = 0;
@@ -1450,21 +1507,25 @@ void ColonyEngine::handleAnimationClick(int item) {
 			if (item >= 6 && item <= 10) {
 				int fl = item - 5;
 				if (fl == _elevatorFloor) {
-					SetObjectState(item, 1); // already on this floor
+					setObjectState(item, 1); // already on this floor
 				} else {
 					_sound->play(Sound::kElevator);
 					for (int i = 3; i >= 1; i--) {
-						SetObjectState(4, i);
-						SetObjectState(3, i);
-						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+						setObjectState(4, i);
+						setObjectState(3, i);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(80);
 					}
 					_elevatorFloor = fl;
 					for (int i = 1; i <= 3; i++) {
-						SetObjectState(4, i);
-						SetObjectState(3, i);
-						drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(80);
+						setObjectState(4, i);
+						setObjectState(3, i);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(80);
 					}
-					SetObjectState(item, 1);
+					setObjectState(item, 1);
 				}
 			} else if (item == 1 || item == 101) {
 				// Exit elevator
@@ -1479,24 +1540,28 @@ void ColonyEngine::handleAnimationClick(int item) {
 				debug(0, "Taking off!");
 				// Animate the lever moving full range
 				for (int i = 1; i <= 6; i++) {
-					SetObjectState(4, i);
-					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(30);
+					setObjectState(4, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(30);
 				}
 				_animationRunning = false;
 				return; // Exit animation immediately on success
 			} else {
 				debug(0, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
 				// Fail animation click
-				SetObjectState(4, 1);
+				setObjectState(4, 1);
 				// dolSprite(3); // Animate lever moving and returning - handled by top dolSprite
 				for (int i = 6; i > 0; i--) {
-					SetObjectState(4, i);
-					drawAnimation(); _gfx->copyToScreen(); _system->delayMillis(20);
+					setObjectState(4, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(20);
 				}
 			}
 			break;
 		case 5: // Emergency power
-			// SetObjectState(5, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
+			// setObjectState(5, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
 			// dolSprite(4); // Animate the button press - handled by top dolSprite
 			if (_coreState[_coreIndex] < 2) {
 				if (_corePower[_coreIndex] == 0)
@@ -1506,9 +1571,9 @@ void ColonyEngine::handleAnimationClick(int item) {
 			}
 			// Finalize visual state according to power settings
 			switch (_corePower[_coreIndex]) {
-			case 0: SetObjectState(2, 1); SetObjectState(5, 1); break;
-			case 1: SetObjectState(2, 1); SetObjectState(5, 2); break;
-			case 2: SetObjectState(2, 2); SetObjectState(5, 1); break;
+			case 0: setObjectState(2, 1); setObjectState(5, 1); break;
+			case 1: setObjectState(2, 1); setObjectState(5, 2); break;
+			case 2: setObjectState(2, 2); setObjectState(5, 1); break;
 			}
 			drawAnimation();
 			_gfx->copyToScreen();
@@ -1524,8 +1589,9 @@ void ColonyEngine::handleAnimationClick(int item) {
 				doText(66, 0); // Orbital stabilization
 			}
 			
-			SetObjectState(7, 1); // Reset button
-			drawAnimation(); _gfx->copyToScreen();
+			setObjectState(7, 1); // Reset button
+			drawAnimation();
+			_gfx->copyToScreen();
 			break;
 		}
 			break;
@@ -1571,7 +1637,7 @@ void ColonyEngine::moveObject(int index) {
 	int oy = _screenR.top + (_screenR.height() - 264) / 2;
 
 	// Drag loop: track mouse while left button held.
-	// NOTE: The original DOS hides dragged sprites during drag (SetObjectOnOff FALSE)
+	// NOTE: The original DOS hides dragged sprites during drag (setObjectOnOff FALSE)
 	// and redraws them separately on top. We improve on this by keeping them visible
 	// throughout, and drawing an extra copy on top so they render above drawers.
 	while (!shouldQuit()) {
@@ -1729,20 +1795,20 @@ void ColonyEngine::dolSprite(int index) {
 	}
 }
 
-void ColonyEngine::SetObjectState(int num, int state) {
+void ColonyEngine::setObjectState(int num, int state) {
 	num--;
 	if (num >= 0 && num < (int)_lSprites.size())
 		_lSprites[num]->current = state - 1;
 }
 
-int ColonyEngine::ObjectState(int num) const {
+int ColonyEngine::objectState(int num) const {
 	num--;
 	if (num >= 0 && num < (int)_lSprites.size())
 		return _lSprites[num]->current + 1;
 	return 0;
 }
 
-void ColonyEngine::SetObjectOnOff(int num, bool on) {
+void ColonyEngine::setObjectOnOff(int num, bool on) {
 	num--;
 	if (num >= 0 && num < (int)_lSprites.size())
 		_lSprites[num]->onoff = on;
@@ -1751,13 +1817,13 @@ void ColonyEngine::SetObjectOnOff(int num, bool on) {
 void ColonyEngine::refreshAnimationDisplay() {
 	for (int i = 0; i < 6; i++) {
 		if (_animDisplay[i] < 9) {
-			SetObjectOnOff(13 + i * 2, true);
-			SetObjectOnOff(14 + i * 2, false);
-			SetObjectState(13 + i * 2, _animDisplay[i]);
+			setObjectOnOff(13 + i * 2, true);
+			setObjectOnOff(14 + i * 2, false);
+			setObjectState(13 + i * 2, _animDisplay[i]);
 		} else {
-			SetObjectOnOff(14 + i * 2, true);
-			SetObjectOnOff(13 + i * 2, false);
-			SetObjectState(14 + i * 2, _animDisplay[i] - 8);
+			setObjectOnOff(14 + i * 2, true);
+			setObjectOnOff(13 + i * 2, false);
+			setObjectState(14 + i * 2, _animDisplay[i] - 8);
 		}
 	}
 }
@@ -1771,7 +1837,8 @@ void ColonyEngine::crypt(uint8 sarray[6], int i, int j, int k, int l) {
 	res[4] = ((l * 17) | (j * 19) | (k * 23) | (i * 29)) % 10;
 	res[5] = (29 + (l * 17) - (j * 19) - (k * 23) - (i * 29)) % 10;
 	for (int m = 0; m < 6; m++) {
-		if (res[m] < 0) res[m] = -res[m];
+		if (res[m] < 0)
+			res[m] = -res[m];
 		sarray[m] = (uint8)(res[m] + 2);
 	}
 }
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index a79a088508a..7549d323f85 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -176,8 +176,8 @@ enum ObjColor {
 	kColorShadow = 74
 };
 
-#define BASEOBJECT 20
-#define MENUM 101
+static const int kBaseObject = 20;
+static const int kMeNum = 101;
 
 struct Locate {
 	uint8 ang;
@@ -356,6 +356,7 @@ private:
 	uint32 _blackoutColor = 0;
 	uint32 _lastClickTime = 0;
 	uint32 _displayCount = 0; // Frame counter for COLOR wall animation (Mac: count)
+	uint32 _lastAnimUpdate = 0;
 	int _action0, _action1;
 	int _creature;
 
@@ -469,9 +470,9 @@ private:
 	void handleAnimationClick(int item);
 	void dolSprite(int index);
 	void moveObject(int index);
-	void SetObjectState(int num, int state);
-	int ObjectState(int num) const;
-	void SetObjectOnOff(int num, bool on);
+	void setObjectState(int num, int state);
+	int objectState(int num) const;
+	void setObjectOnOff(int num, bool on);
 	void refreshAnimationDisplay();
 	void crypt(uint8 sarray[6], int i, int j, int k, int l);
 	void terminateGame(bool blowup);
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 8b06cd1d55b..79531bbab37 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -1201,7 +1201,8 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 	for (int i = 0; i < def.surfaceCount; i++) {
 		const int colorIdx = def.surfaces[i][0];
 		const int n = def.surfaces[i][1];
-		if (n < 2) continue;
+		if (n < 2)
+			continue;
 
 		float px[8];
 		float py[8];
@@ -1210,7 +1211,8 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 
 		for (int j = 0; j < n; j++) {
 			const int cur = def.surfaces[i][j + 2];
-			if (cur < 0 || cur >= def.pointCount) continue;
+			if (cur < 0 || cur >= def.pointCount)
+				continue;
 
 			int ox = def.points[cur][0];
 			int oy = def.points[cur][1];
@@ -1244,7 +1246,8 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 				if (_renderMode == Common::kRenderMacintosh) {
 					// Mac B&W: stipple dither pattern fill + black outline
 					int pattern = lookupMacPattern(colorIdx);
-					if (pattern == kPatternClear) continue;
+					if (pattern == kPatternClear)
+						continue;
 					if (!_wireframe) {
 						_gfx->setWireframe(true, pattern == kPatternBlack ? 0 : 255);
 					}
@@ -1275,14 +1278,16 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 
 	for (int i = 0; i < def.surfaceCount; i++) {
 		const int n = def.surfaces[i][1];
-		if (n < 2) continue;
+		if (n < 2)
+			continue;
 
 		float px[8], py[8], pz[8];
 		int count = 0;
 
 		for (int j = 0; j < n; j++) {
 			const int cur = def.surfaces[i][j + 2];
-			if (cur < 0 || cur >= def.pointCount) continue;
+			if (cur < 0 || cur >= def.pointCount)
+				continue;
 			int ox = def.points[cur][0];
 			int oy = def.points[cur][1];
 			int oz = def.points[cur][2];
@@ -1336,7 +1341,8 @@ void ColonyEngine::draw3DSphere(const Thing &obj, int pt0x, int pt0y, int pt0z,
 	float viewDx = cx - (float)_me.xloc;
 	float viewDy = cy - (float)_me.yloc;
 	float viewLen = sqrtf(viewDx * viewDx + viewDy * viewDy);
-	if (viewLen < 0.001f) return;
+	if (viewLen < 0.001f)
+		return;
 
 	// "right" vector: perpendicular to view in XY plane
 	float rightX = -viewDy / viewLen;
@@ -1537,7 +1543,8 @@ void ColonyEngine::wallLine(const float corners[4][3], float u1, float v1, float
 // Draw a filled polygon on a wall face using normalized (u,v) coordinates
 void ColonyEngine::wallPolygon(const float corners[4][3], const float *u, const float *v, int count, uint32 color) {
 	float px[8], py[8], pz[8];
-	if (count > 8) count = 8;
+	if (count > 8)
+		count = 8;
 	for (int i = 0; i < count; i++) {
 		float p[3];
 		wallPoint(corners, u[i], v[i], p);
@@ -1548,12 +1555,14 @@ void ColonyEngine::wallPolygon(const float corners[4][3], const float *u, const
 
 void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 	// Character 'b' (right arrow) and 'c' (left arrow) coordinates on 0-6 grid
-	static const uint8 wallchar_b[] = {7, 0,3, 3,0, 3,2, 6,2, 6,4, 3,4, 3,6};
-	static const uint8 wallchar_c[] = {7, 0,2, 0,4, 3,4, 3,6, 6,3, 3,0, 3,2};
+	static const uint8 wallcharB[] = {7, 0,3, 3,0, 3,2, 6,2, 6,4, 3,4, 3,6};
+	static const uint8 wallcharC[] = {7, 0,2, 0,4, 3,4, 3,6, 6,3, 3,0, 3,2};
 	
 	const uint8 *data = nullptr;
-	if (cnum == 'b') data = wallchar_b;
-	else if (cnum == 'c') data = wallchar_c;
+	if (cnum == 'b')
+		data = wallcharB;
+	else if (cnum == 'c')
+		data = wallcharC;
 	
 	if (data) {
 		int count = data[0];
@@ -1604,7 +1613,10 @@ void ColonyEngine::getCellFace3D(int cellX, int cellY, bool ceiling, float corne
 	float x1 = x0 + 256.0f;
 	float y1 = y0 + 256.0f;
 	const float eps = 0.1f;
-	if (ceiling) z -= eps; else z += eps;
+	if (ceiling)
+		z -= eps;
+	else
+		z += eps;
 
 	corners[0][0] = x0; corners[0][1] = y0; corners[0][2] = z;
 	corners[1][0] = x1; corners[1][1] = y0; corners[1][2] = z;
@@ -1711,25 +1723,25 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		bool shipLevel = (_level == 1 || _level == 5 || _level == 6);
 
 		if (shipLevel) {
-			static const float u_ss[8] = { 0.375f, 0.250f, 0.250f, 0.375f, 0.625f, 0.750f, 0.750f, 0.625f };
-			static const float v_ss[8] = { 0.125f, 0.250f, 0.750f, 0.875f, 0.875f, 0.750f, 0.250f, 0.125f };
+			static const float uSs[8] = { 0.375f, 0.250f, 0.250f, 0.375f, 0.625f, 0.750f, 0.750f, 0.625f };
+			static const float vSs[8] = { 0.125f, 0.250f, 0.750f, 0.875f, 0.875f, 0.750f, 0.250f, 0.125f };
 
 			if (macMode) {
 				if (map[1] != 0) {
 					// Closed: fill octagon (c_bulkhead = GRAY stipple)
 					_gfx->setStippleData(kStippleGray);
-					wallPolygon(corners, u_ss, v_ss, 8, 0);
+					wallPolygon(corners, uSs, vSs, 8, 0);
 					_gfx->setStippleData(nullptr);
 				} else {
 					// Open: fill with BLACK (passable opening)
 					_gfx->setWireframe(true, 0);
-					wallPolygon(corners, u_ss, v_ss, 8, 0);
+					wallPolygon(corners, uSs, vSs, 8, 0);
 					_gfx->setWireframe(true, 255);
 				}
 			}
 
 			for (int i = 0; i < 8; i++)
-				wallLine(corners, u_ss[i], v_ss[i], u_ss[(i + 1) % 8], v_ss[(i + 1) % 8], doorColor);
+				wallLine(corners, uSs[i], vSs[i], uSs[(i + 1) % 8], vSs[(i + 1) % 8], doorColor);
 
 			if (map[1] != 0) {
 				wallLine(corners, 0.375f, 0.25f, 0.375f, 0.75f, doorColor);
@@ -1994,15 +2006,15 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	}
 	case kWallFeatureTunnel: {
 		// Tunnel: hexagonal opening from Grid (0,0 0,5 1,6 5,6 6,5 6,0)
-		static const float u_t[6] = { 0.0f,    0.0f,    1/6.0f,  5/6.0f,  1.0f,    1.0f };
-		static const float v_t[6] = { 0.0f,    0.750f,  0.875f,  0.875f,  0.750f,  0.0f };
+		static const float uT[6] = { 0.0f,    0.0f,    1/6.0f,  5/6.0f,  1.0f,    1.0f };
+		static const float vT[6] = { 0.0f,    0.750f,  0.875f,  0.875f,  0.750f,  0.0f };
 		if (_renderMode == Common::kRenderMacintosh) {
 			// Mac: c_tunnel = GRAY stipple fill + black outline
 			_gfx->setStippleData(kStippleGray);
-			wallPolygon(corners, u_t, v_t, 6, 0);
+			wallPolygon(corners, uT, vT, 6, 0);
 			_gfx->setStippleData(nullptr);
 		} else {
-			wallPolygon(corners, u_t, v_t, 6, 0); // vBLACK outline
+			wallPolygon(corners, uT, vT, 6, 0); // vBLACK outline
 		}
 		break;
 	}
@@ -2154,11 +2166,11 @@ void ColonyEngine::renderCorridor3D() {
 	// Draw ceiling grid (Cuadricule) - Historically only on ceiling
 	for (int i = 0; i <= 32; i++) {
 		float d = i * 256.0f;
-		float max_d = 32.0f * 256.0f;
+		float maxD = 32.0f * 256.0f;
 		float zCeil = 160.0f;
 		
-		_gfx->draw3DLine(d, 0.0f, zCeil, d, max_d, zCeil, wallColor);
-		_gfx->draw3DLine(0.0f, d, zCeil, max_d, d, zCeil, wallColor);
+		_gfx->draw3DLine(d, 0.0f, zCeil, d, maxD, zCeil, wallColor);
+		_gfx->draw3DLine(0.0f, d, zCeil, maxD, d, zCeil, wallColor);
 	}
 
 	for (int y = 0; y < 32; y++) {
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index b62049f48c5..0813628f353 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -88,7 +88,8 @@ OpenGLRenderer::OpenGLRenderer(OSystem *system, int width, int height) : _system
 	memset(_palette, 0, sizeof(_palette));
 	
 	// Default to white for initial colors if setPalette isn't called yet
-	for (int i = 0; i < 256 * 3; i++) _palette[i] = 255;
+	for (int i = 0; i < 256 * 3; i++)
+		_palette[i] = 255;
 
 	glDisable(GL_LIGHTING);
 	glDisable(GL_TEXTURE_2D);
@@ -113,7 +114,8 @@ OpenGLRenderer::~OpenGLRenderer() {
 }
 
 void OpenGLRenderer::setPalette(const byte *palette, uint start, uint count) {
-	if (start + count > 256) count = 256 - start;
+	if (start + count > 256)
+		count = 256 - start;
 	memcpy(_palette + start * 3, palette, count * 3);
 }
 
@@ -157,11 +159,14 @@ void OpenGLRenderer::fillRect(const Common::Rect &rect, uint32 color) {
 }
 
 void OpenGLRenderer::drawString(const Graphics::Font *font, const Common::String &str, int x, int y, uint32 color, Graphics::TextAlign align) {
-	if (!font) return;
+	if (!font)
+		return;
 	int width = font->getStringWidth(str);
 	int height = font->getFontHeight();
-	if (align == Graphics::kTextAlignCenter) x -= width / 2;
-	else if (align == Graphics::kTextAlignRight) x -= width;
+	if (align == Graphics::kTextAlignCenter)
+		x -= width / 2;
+	else if (align == Graphics::kTextAlignRight)
+		x -= width;
 	
 	Graphics::Surface surface;
 	surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
@@ -376,7 +381,8 @@ void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2
 }
  
 void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *z, int count, uint32 color) {
-	if (count < 3) return;
+	if (count < 3)
+		return;
 
 	if (_wireframe) {
 		if (_wireframeFillColor >= 0 || _stippleData) {
@@ -386,19 +392,22 @@ void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *
 			if (_stippleData) {
 				useColor(255);
 				glBegin(GL_POLYGON);
-				for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+				for (int i = 0; i < count; i++)
+					glVertex3f(x[i], y[i], z[i]);
 				glEnd();
 				glEnable(GL_POLYGON_STIPPLE);
 				glPolygonStipple(_stippleData);
 				useColor(0);
 				glBegin(GL_POLYGON);
-				for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+				for (int i = 0; i < count; i++)
+					glVertex3f(x[i], y[i], z[i]);
 				glEnd();
 				glDisable(GL_POLYGON_STIPPLE);
 			} else {
 				useColor((uint32)_wireframeFillColor);
 				glBegin(GL_POLYGON);
-				for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+				for (int i = 0; i < count; i++)
+					glVertex3f(x[i], y[i], z[i]);
 				glEnd();
 			}
 			glDisable(GL_POLYGON_OFFSET_FILL);
@@ -417,19 +426,22 @@ void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *
 		if (_stippleData) {
 			useColor(255);
 			glBegin(GL_POLYGON);
-			for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+			for (int i = 0; i < count; i++)
+				glVertex3f(x[i], y[i], z[i]);
 			glEnd();
 			glEnable(GL_POLYGON_STIPPLE);
 			glPolygonStipple(_stippleData);
 			useColor(0);
 			glBegin(GL_POLYGON);
-			for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+			for (int i = 0; i < count; i++)
+				glVertex3f(x[i], y[i], z[i]);
 			glEnd();
 			glDisable(GL_POLYGON_STIPPLE);
 		} else {
 			useColor(color);
 			glBegin(GL_POLYGON);
-			for (int i = 0; i < count; i++) glVertex3f(x[i], y[i], z[i]);
+			for (int i = 0; i < count; i++)
+				glVertex3f(x[i], y[i], z[i]);
 			glEnd();
 		}
 		glDisable(GL_POLYGON_OFFSET_FILL);
@@ -549,7 +561,8 @@ void OpenGLRenderer::drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, in
 }
 
 void OpenGLRenderer::drawPolygon(const int *x, const int *y, int count, uint32 color) {
-	if (count < 3) return;
+	if (count < 3)
+		return;
 	useColor(color);
 	glBegin(GL_POLYGON);
 	for (int i = 0; i < count; i++) {
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 0122f101a74..3237815cdab 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -277,7 +277,7 @@ int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
 		return 0;
 	if (pobject == &_me && rnum <= (int)_objects.size()) {
 		Thing &obj = _objects[rnum - 1];
-		if (obj.type <= BASEOBJECT)
+		if (obj.type <= kBaseObject)
 			obj.where.look = obj.where.ang = _me.ang + 128;
 	}
 	return rnum;
@@ -517,7 +517,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 				return 0;
 			_animationResult = 0;
 			_doorOpen = false;
-			SetObjectState(2, 1); // door starts closed
+			setObjectState(2, 1); // door starts closed
 			playAnimation();
 			if (_animationResult) {
 				setDoorState(fromX, fromY, direction, 0);
@@ -536,8 +536,8 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 				return 0;
 			_animationResult = 0;
 			_doorOpen = false;
-			SetObjectState(2, 1); // airlock starts closed
-			SetObjectState(1, 1);
+			setObjectState(2, 1); // airlock starts closed
+			setObjectState(1, 1);
 			playAnimation();
 			if (_animationResult) {
 				setDoorState(fromX, fromY, direction, 0);
@@ -591,7 +591,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 
 		if (pobject->xindex >= 0 && pobject->xindex < 32 &&
 		    pobject->yindex >= 0 && pobject->yindex < 32)
-			_robotArray[pobject->xindex][pobject->yindex] = MENUM;
+			_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
 
 		debug("Level change via %s: level=%d pos=(%d,%d)",
 		      map[0] == kWallFeatureUpStairs ? "upstairs" :
@@ -614,11 +614,11 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 		_animationResult = 0;
 		_doorOpen = false;
 		_elevatorFloor = _level - 1; // DOS: fl = level-1
-		SetObjectOnOff(6, false);
-		SetObjectOnOff(7, false);
-		SetObjectOnOff(8, false);
-		SetObjectOnOff(9, false);
-		SetObjectOnOff(10, false);
+		setObjectOnOff(6, false);
+		setObjectOnOff(7, false);
+		setObjectOnOff(8, false);
+		setObjectOnOff(9, false);
+		setObjectOnOff(10, false);
 		playAnimation();
 
 		bool entered = (_animationResult >= 2);
@@ -645,7 +645,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 
 			if (pobject->xindex >= 0 && pobject->xindex < 32 &&
 			    pobject->yindex >= 0 && pobject->yindex < 32)
-				_robotArray[pobject->xindex][pobject->yindex] = MENUM;
+				_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
 
 			debug("Elevator: level=%d pos=(%d,%d)", _level, pobject->xindex, pobject->yindex);
 			return 2; // teleported
@@ -783,7 +783,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 		if (oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32)
 			_robotArray[oldX][oldY] = 0;
 		if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-			_robotArray[_me.xindex][_me.yindex] = MENUM;
+			_robotArray[_me.xindex][_me.yindex] = kMeNum;
 		break;
 	}
 	case kObjDrawer:
@@ -811,8 +811,10 @@ void ColonyEngine::interactWithObject(int objNum) {
 		inform("A SINK. IT'S DRY.", true);
 		break;
 	case kObjTV:
-		if (_level == 1) doText(56, 0);
-		else doText(16, 0);
+		if (_level == 1)
+			doText(56, 0);
+		else
+			doText(16, 0);
 		break;
 	case kObjForkLift:
 		if (_fl == 0) {
@@ -924,18 +926,19 @@ void ColonyEngine::printMessage(const char *text[], bool hold) {
 	
 	while (text[numLines] != nullptr) {
 		int w = font.getStringWidth(text[numLines]);
-		if (w > width) width = w;
+		if (w > width)
+			width = w;
 		numLines++;
 	}
 
-	int px_per_inch_x = 72;
-	int px_per_inch_y = 72;
+	int pxPerInchX = 72;
+	int pxPerInchY = 72;
 
 	Common::Rect rr;
-	rr.top = _centerY - (numLines + 1) * (px_per_inch_y / 4);
-	rr.bottom = _centerY + (numLines + 1) * (px_per_inch_y / 4);
-	rr.left = _centerX - width / 2 - (px_per_inch_x / 2);
-	rr.right = _centerX + width / 2 + (px_per_inch_x / 2);
+	rr.top = _centerY - (numLines + 1) * (pxPerInchY / 4);
+	rr.bottom = _centerY + (numLines + 1) * (pxPerInchY / 4);
+	rr.left = _centerX - width / 2 - (pxPerInchX / 2);
+	rr.right = _centerX + width / 2 + (pxPerInchX / 2);
 
 	_gfx->fillDitherRect(_screenR, 0, 15);
 	makeMessageRect(rr);
@@ -943,8 +946,8 @@ void ColonyEngine::printMessage(const char *text[], bool hold) {
 	int start;
 	int step;
 	if (numLines > 1) {
-		start = rr.top + (px_per_inch_y / 4) * 2;
-		step = (rr.height() - (px_per_inch_y / 4) * 4) / (numLines - 1);
+		start = rr.top + (pxPerInchY / 4) * 2;
+		step = (rr.height() - (pxPerInchY / 4) * 4) / (numLines - 1);
 	} else {
 		start = (rr.top + rr.bottom) / 2;
 		step = 0;
@@ -1018,26 +1021,31 @@ void ColonyEngine::doText(int entry, int center) {
 	for (int i = 0; i < ch; i++) {
 		if (p[i] == '\r' || p[i] == '\n') {
 			p[i] = 0;
-			if (p[start]) lineArray.push_back(&p[start]);
+			if (p[start])
+				lineArray.push_back(&p[start]);
 			start = i + 1;
 		}
 	}
-	if (start < ch && p[start]) lineArray.push_back(&p[start]);
+	if (start < ch && p[start])
+		lineArray.push_back(&p[start]);
 
 	Graphics::DosFont font;
 	int width = 0;
 	for (uint i = 0; i < lineArray.size(); i++) {
 		int w = font.getStringWidth(lineArray[i]);
-		if (w > width) width = w;
+		if (w > width)
+			width = w;
 	}
 	const char *kpress = "-Press Any Key to Continue-";
 	int kw = font.getStringWidth(kpress);
-	if (kw > width) width = kw;
+	if (kw > width)
+		width = kw;
 	width += 12;
 
 	int lineheight = 14;
 	int maxlines = (_screenR.height() / lineheight) - 2;
-	if (maxlines > (int)lineArray.size()) maxlines = lineArray.size();
+	if (maxlines > (int)lineArray.size())
+		maxlines = lineArray.size();
 
 	Common::Rect r;
 	r.top = _centerY - (((maxlines + 1) * lineheight / 2) + 4);
@@ -1125,11 +1133,13 @@ void ColonyEngine::fallThroughHole() {
 			for (int ring = 0; ring < visibleRings; ring++) {
 				// Each ring's depth combines the overall fall progress with per-ring spacing
 				float depth = progress * 0.6f + (float)ring / (maxRings + 2.0f);
-				if (depth >= 1.0f) break;
+				if (depth >= 1.0f)
+					break;
 				float scale = 1.0f - depth;
 				int rw = (int)(hw * scale);
 				int rh = (int)(hh * scale);
-				if (rw < 2 || rh < 2) break;
+				if (rw < 2 || rh < 2)
+					break;
 				Common::Rect r(cx - rw, cy - rh, cx + rw, cy + rh);
 				_gfx->drawRect(r, 15); // white outline
 			}
@@ -1155,7 +1165,7 @@ void ColonyEngine::fallThroughHole() {
 		_me.yloc = (targetY << 8) + ymod;
 		_me.yindex = targetY;
 
-		_robotArray[targetX][targetY] = MENUM;
+		_robotArray[targetX][targetY] = kMeNum;
 	}
 
 	// DOS: if(map) load_mapnum(map, TRUE) — always reload when map != 0
@@ -1205,12 +1215,13 @@ void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
 		interactWithObject(robot);
 
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-		_robotArray[_me.xindex][_me.yindex] = MENUM;
+		_robotArray[_me.xindex][_me.yindex] = kMeNum;
 }
 
 // DOS ExitFL(): step back one cell and drop the forklift.
 void ColonyEngine::exitForklift() {
-	if (_fl != 1) return;
+	if (_fl != 1)
+		return;
 
 	int xloc = _me.xloc;
 	int yloc = _me.yloc;
@@ -1224,10 +1235,10 @@ void ColonyEngine::exitForklift() {
 		_me.type = 2; // temporary small collision type
 		if (checkwall(xnew, ynew, &_me)) {
 			_sound->play(Sound::kChime);
-			_me.type = MENUM;
+			_me.type = kMeNum;
 			return;
 		}
-		_me.type = MENUM;
+		_me.type = kMeNum;
 	}
 
 	// Snap to cell center for the dropped forklift
@@ -1252,7 +1263,8 @@ void ColonyEngine::exitForklift() {
 
 // DOS DropFL(): step back one cell and drop the carried object, then return to fl=1.
 void ColonyEngine::dropCarriedObject() {
-	if (_fl != 2) return;
+	if (_fl != 2)
+		return;
 
 	// Special case: carrying reactor core
 	if (_carryType == kObjReactor) {
@@ -1284,10 +1296,10 @@ void ColonyEngine::dropCarriedObject() {
 		_me.type = 2;
 		if (checkwall(xnew, ynew, &_me)) {
 			_sound->play(Sound::kChime);
-			_me.type = MENUM;
+			_me.type = kMeNum;
 			return;
 		}
-		_me.type = MENUM;
+		_me.type = kMeNum;
 	}
 
 	// DOS: teleport always drops at ang=0; other objects use player's angle
@@ -1344,19 +1356,23 @@ bool ColonyEngine::clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Comm
 		int x = 0;
 		int y = 0;
 		if (cOut & 8) {
-			if (y2 == y1) return false;
+			if (y2 == y1)
+				return false;
 			x = x1 + (x2 - x1) * (b - y1) / (y2 - y1);
 			y = b;
 		} else if (cOut & 4) {
-			if (y2 == y1) return false;
+			if (y2 == y1)
+				return false;
 			x = x1 + (x2 - x1) * (t - y1) / (y2 - y1);
 			y = t;
 		} else if (cOut & 2) {
-			if (x2 == x1) return false;
+			if (x2 == x1)
+				return false;
 			y = y1 + (y2 - y1) * (r - x1) / (x2 - x1);
 			x = r;
 		} else {
-			if (x2 == x1) return false;
+			if (x2 == x1)
+				return false;
 			y = y1 + (y2 - y1) * (l - x1) / (x2 - x1);
 			x = l;
 		}


Commit: b93bfeac8bc7063efd42272a893e6cd86352b1d5
    https://github.com/scummvm/scummvm/commit/b93bfeac8bc7063efd42272a893e6cd86352b1d5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:23+02:00

Commit Message:
COLONY: initial mac support

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/detection.cpp
    engines/colony/sound.cpp
    engines/colony/sound.h
    engines/colony/ui.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index dd304f2128b..3f6f987cffe 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -138,26 +138,32 @@ ColonyEngine::~ColonyEngine() {
 
 
 void ColonyEngine::loadMap(int mnum) {
-	Common::String mapName = Common::String::format("MAP.%d", mnum);
-	Common::File file;
-	if (!file.open(Common::Path(mapName))) {
-		warning("Could not open map file %s", mapName.c_str());
-		return;
+	Common::Path mapPath(Common::String::format("MAP.%d", mnum));
+	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(mapPath);
+	if (!file) {
+		// Try Mac-style path
+		mapPath = Common::Path(Common::String::format("CData/map.%d", mnum));
+		file = Common::MacResManager::openFileOrDataFork(mapPath);
+		if (!file) {
+			warning("Could not open map file %s", mapPath.toString().c_str());
+			return;
+		}
 	}
 
-	file.readUint32BE(); // "DAVE" header
+	file->readUint32BE(); // "DAVE" header
 	int16 mapDefs[10];
 	for (int i = 0; i < 10; i++) {
-		mapDefs[i] = file.readSint16BE(); // Swapped in original code
+		mapDefs[i] = file->readSint16BE();
 	}
 
-	uint16 bLength = file.readUint16BE(); // Swapped in original code
+	uint16 bLength = file->readUint16BE();
 	uint8 *buffer = (uint8 *)malloc(bLength);
 	if (!buffer) {
+		delete file;
 		error("Out of memory loading map");
 	}
-	file.read(buffer, bLength);
-	file.close();
+	file->read(buffer, bLength);
+	delete file;
 
 	memset(_mapData, 0, sizeof(_mapData));
 	memset(_robotArray, 0, sizeof(_robotArray));
@@ -712,71 +718,84 @@ bool ColonyEngine::loadAnimation(const Common::String &name) {
 	_animationName = name;
 	for (int i = 0; i < 6; i++)
 		_animDisplay[i] = 1;
+
 	Common::String fileName = name + ".pic";
-	Common::File file;
-	if (!file.open(Common::Path(fileName))) {
-		warning("Could not open animation file %s", fileName.c_str());
-		return false;
+	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
+	if (!file) {
+		// Try lowercase for Mac
+		fileName = name;
+		fileName.toLowercase();
+		file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
+		if (!file) {
+			// Try CData directory
+			fileName = "CData/" + fileName;
+			file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
+			if (!file) {
+				warning("Could not open animation file %s", name.c_str());
+				return false;
+			}
+		}
 	}
 
 	deleteAnimation();
 
 	// Read background data
-	file.read(_topBG, 8);
-	file.read(_bottomBG, 8);
-	_divideBG = file.readSint16LE();
-	_backgroundActive = file.readSint16LE() != 0;
+	file->read(_topBG, 8);
+	file->read(_bottomBG, 8);
+	_divideBG = readSint16(*file);
+	_backgroundActive = readSint16(*file) != 0;
 	if (_backgroundActive) {
-		_backgroundClip = readRect(file);
-		_backgroundLocate = readRect(file);
-		_backgroundMask = loadImage(file);
-		_backgroundFG = loadImage(file);
+		_backgroundClip = readRect(*file);
+		_backgroundLocate = readRect(*file);
+		_backgroundMask = loadImage(*file);
+		_backgroundFG = loadImage(*file);
 	}
 
 	// Read sprite data
-	int16 maxsprite = file.readSint16LE();
-	file.readSint16LE(); // locSprite
+	int16 maxsprite = readSint16(*file);
+	readSint16(*file); // locSprite
 	for (int i = 0; i < maxsprite; i++) {
 		Sprite *s = new Sprite();
-		s->fg = loadImage(file);
-		s->mask = loadImage(file);
-		s->used = file.readSint16LE() != 0;
-		s->clip = readRect(file);
-		s->locate = readRect(file);
+		s->fg = loadImage(*file);
+		s->mask = loadImage(*file);
+		s->used = readSint16(*file) != 0;
+		s->clip = readRect(*file);
+		s->locate = readRect(*file);
 		_cSprites.push_back(s);
 	}
 
 	// Read complex sprite data
-	int16 maxLSprite = file.readSint16LE();
-	file.readSint16LE(); // anum
+	int16 maxLSprite = readSint16(*file);
+	readSint16(*file); // anum
 	for (int i = 0; i < maxLSprite; i++) {
 		ComplexSprite *ls = new ComplexSprite();
-		int16 size = file.readSint16LE();
+		int16 size = readSint16(*file);
 		for (int j = 0; j < size; j++) {
 			ComplexSprite::SubObject sub;
-			sub.spritenum = file.readSint16LE();
-			sub.xloc = file.readSint16LE();
-			sub.yloc = file.readSint16LE();
+			sub.spritenum = readSint16(*file);
+			sub.xloc = readSint16(*file);
+			sub.yloc = readSint16(*file);
 			ls->objects.push_back(sub);
 		}
-		ls->bounds = readRect(file);
-		ls->visible = file.readSint16LE() != 0;
-		ls->current = file.readSint16LE();
-		ls->xloc = file.readSint16LE();
-		ls->yloc = file.readSint16LE();
-		ls->acurrent = file.readSint16LE();
-		ls->axloc = file.readSint16LE();
-		ls->ayloc = file.readSint16LE();
-		ls->type = file.readByte();
-		ls->frozen = file.readByte();
-		ls->locked = file.readByte();
-		ls->link = file.readSint16LE();
-		ls->key = file.readSint16LE();
-		ls->lock = file.readSint16LE();
+		ls->bounds = readRect(*file);
+		ls->visible = readSint16(*file) != 0;
+		ls->current = readSint16(*file);
+		ls->xloc = readSint16(*file);
+		ls->yloc = readSint16(*file);
+		ls->acurrent = readSint16(*file);
+		ls->axloc = readSint16(*file);
+		ls->ayloc = readSint16(*file);
+		ls->type = file->readByte();
+		ls->frozen = file->readByte();
+		ls->locked = file->readByte();
+		ls->link = readSint16(*file);
+		ls->key = readSint16(*file);
+		ls->lock = readSint16(*file);
 		ls->onoff = true;
 		_lSprites.push_back(ls);
 	}
 
+	delete file;
 	return true;
 }
 
@@ -1065,22 +1084,33 @@ void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y) {
 
 Image *ColonyEngine::loadImage(Common::SeekableReadStream &file) {
 	Image *im = new Image();
-	im->width = file.readSint16LE();
-	im->height = file.readSint16LE();
-	im->align = file.readSint16LE();
-	im->rowBytes = file.readSint16LE();
-	im->bits = file.readByte();
-	im->planes = file.readByte();
-
-	int16 tf = file.readSint16LE();
+	if (getPlatform() == Common::kPlatformMacintosh) {
+		readUint32(file); // baseAddr placeholder
+		im->rowBytes = readSint16(file);
+		Common::Rect r = readRect(file);
+		im->width = r.width();
+		im->height = r.height();
+		im->align = 0;
+		im->bits = 1;
+		im->planes = 1;
+	} else {
+		im->width = readSint16(file);
+		im->height = readSint16(file);
+		im->align = readSint16(file);
+		im->rowBytes = readSint16(file);
+		im->bits = file.readByte();
+		im->planes = file.readByte();
+	}
+
+	int16 tf = readSint16(file);
 	uint32 size;
 	if (tf) {
-		/* uint32 bsize = */ file.readUint32LE();
-		size = file.readUint32LE();
+		/* uint32 bsize = */ readUint32(file);
+		size = readUint32(file);
 		im->data = (byte *)malloc(size);
 		unpackBytes(file, im->data, size);
 	} else {
-		size = file.readUint32LE();
+		size = readUint32(file);
 		im->data = (byte *)malloc(size);
 		file.read(im->data, size);
 	}
@@ -1099,11 +1129,37 @@ void ColonyEngine::unpackBytes(Common::SeekableReadStream &file, byte *dst, uint
 }
 
 Common::Rect ColonyEngine::readRect(Common::SeekableReadStream &file) {
-	int16 left = file.readSint16LE();
-	int16 top = file.readSint16LE();
-	int16 right = file.readSint16LE();
-	int16 bottom = file.readSint16LE();
-	return Common::Rect(left, top, right, bottom);
+	if (getPlatform() == Common::kPlatformMacintosh) {
+		int16 top = readSint16(file);
+		int16 left = readSint16(file);
+		int16 bottom = readSint16(file);
+		int16 right = readSint16(file);
+		return Common::Rect(left, top, right, bottom);
+	} else {
+		int16 left = readSint16(file);
+		int16 top = readSint16(file);
+		int16 right = readSint16(file);
+		int16 bottom = readSint16(file);
+		return Common::Rect(left, top, right, bottom);
+	}
+}
+
+int16 ColonyEngine::readSint16(Common::SeekableReadStream &s) {
+	if (getPlatform() == Common::kPlatformMacintosh)
+		return s.readSint16BE();
+	return s.readSint16LE();
+}
+
+uint16 ColonyEngine::readUint16(Common::SeekableReadStream &s) {
+	if (getPlatform() == Common::kPlatformMacintosh)
+		return s.readUint16BE();
+	return s.readUint16LE();
+}
+
+uint32 ColonyEngine::readUint32(Common::SeekableReadStream &s) {
+	if (getPlatform() == Common::kPlatformMacintosh)
+		return s.readUint32BE();
+	return s.readUint32LE();
 }
 
 int ColonyEngine::whichSprite(const Common::Point &p) {
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 7549d323f85..6e917653c34 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -290,6 +290,7 @@ public:
 	virtual ~ColonyEngine();
 
 	Common::Error run() override;
+	Common::Platform getPlatform() const { return _gameDescription->platform; }
 
 	void initTrig();
 	void loadMap(int mnum);
@@ -466,6 +467,9 @@ private:
 	Image *loadImage(Common::SeekableReadStream &file);
 	void unpackBytes(Common::SeekableReadStream &file, byte *dst, uint32 len);
 	Common::Rect readRect(Common::SeekableReadStream &file);
+	int16 readSint16(Common::SeekableReadStream &s);
+	uint16 readUint16(Common::SeekableReadStream &s);
+	uint32 readUint32(Common::SeekableReadStream &s);
 	int whichSprite(const Common::Point &p);
 	void handleAnimationClick(int item);
 	void dolSprite(int index);
diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
index 92dfcb5fe42..4ca8ca5b479 100644
--- a/engines/colony/detection.cpp
+++ b/engines/colony/detection.cpp
@@ -29,6 +29,26 @@ static const PlainGameDescriptor colonyGames[] = {
 };
 
 const ADGameDescription gameDescriptions[] = {
+	{
+		"colony",
+		"B&W",
+		AD_ENTRY2s("Colony", "50f79a9fd68055a49171eaf92f3f6b13", 229888,
+		           "Zounds", "9de09af0a8d62f30d6863ff86c95cc51", 552576),
+		Common::EN_ANY,
+		Common::kPlatformMacintosh,
+		ADGF_NO_FLAGS,
+		GUIO2(GUIO_RENDERMACINTOSH, GUIO_RENDEREGA)
+	},
+	{
+		"colony",
+		"Color",
+		AD_ENTRY2s("Color256", "7955c1cd7b94bba89a31c5c96840eeb2", 1588,
+		           "Color16", "e5382c33f47e5619887eac8991a5c249", 1588),
+		Common::EN_ANY,
+		Common::kPlatformMacintosh,
+		ADGF_NO_FLAGS,
+		GUIO2(GUIO_RENDERMACINTOSH, GUIO_RENDEREGA)
+	},
 	{
 		"colony",
 		"",
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index 023aa0f85a1..dfb5a5cf7d5 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -21,16 +21,27 @@
 
 #include "colony/sound.h"
 #include "colony/colony.h"
+#include "common/stream.h"
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
 
 namespace Colony {
 
 Sound::Sound(ColonyEngine *vm) : _vm(vm) {
 	_speaker = new Audio::PCSpeaker();
 	_speaker->init();
+
+	_resMan = new Common::MacResManager();
+	if (!_resMan->open("Zounds")) {
+		if (!_resMan->open("CData/Zounds")) {
+			debug("Could not open Zounds resource file");
+		}
+	}
 }
 
 Sound::~Sound() {
 	delete _speaker;
+	delete _resMan;
 }
 
 void Sound::stop() {
@@ -41,7 +52,11 @@ void Sound::stop() {
 void Sound::play(int soundID) {
 	if (_speaker->isPlaying())
 		_speaker->stop();
-	playPCSpeaker(soundID);
+
+	if (_vm->getPlatform() == Common::kPlatformMacintosh)
+		playMacSound(soundID);
+	else
+		playPCSpeaker(soundID);
 }
 
 void Sound::playPCSpeaker(int soundID) {
@@ -219,4 +234,68 @@ void Sound::playPCSpeaker(int soundID) {
 	}
 }
 
+bool Sound::playMacSound(int soundID) {
+	int resID = -1;
+	switch (soundID) {
+	case kKlaxon: resID = 27317; break;
+	case kAirlock: resID = 5141; break;
+	case kOuch: resID = 9924; break;
+	case kChime: resID = 24694; break;
+	case kBang: resID = 24433; break;
+	case kShoot: resID = 24010; break;
+	case kEat: resID = 11783; break;
+	case kBonk: resID = 7970; break;
+	case kBzzz: resID = 11642; break;
+	case kExplode: resID = 1432; break;
+	case kElevator: resID = 12019; break;
+	case kPShot: resID = 27539; break;
+	case kTest: resID = 25795; break;
+	case kDit: resID = 1516; break;
+	case kSink: resID = 2920; break;
+	case kClatter: resID = 11208; break;
+	case kStop: resID = 29382; break;
+	case kTeleport: resID = 9757; break;
+	case kSlug: resID = 8347; break;
+	case kTunnel2: resID = 17354; break;
+	case kLift: resID = 28521; break;
+	case kGlass: resID = 19944; break;
+	case kDoor: resID = 26867; break;
+	case kToilet: resID = 4955; break;
+	case kBath: resID = 11589; break;
+	default: break;
+	}
+
+	if (resID != -1 && playResource(resID))
+		return true;
+
+	// Fallback to DOS sounds if Mac resource is missing
+	playPCSpeaker(soundID);
+	return false;
+}
+
+bool Sound::playResource(int resID) {
+	if (!_resMan || !_resMan->isMacFile())
+		return false;
+
+	Common::SeekableReadStream *snd = _resMan->getResource(MKTAG('s', 'n', 'd', ' '), resID);
+	if (!snd)
+		return false;
+
+	// The Mac source skips 42 bytes of the 'snd ' resource header to get to wave data.
+	if (snd->size() <= 42) {
+		delete snd;
+		return false;
+	}
+
+	snd->seek(42);
+	uint32 dataSize = snd->size() - 42;
+	byte *data = (byte *)malloc(dataSize);
+	snd->read(data, dataSize);
+	delete snd;
+
+	Audio::AudioStream *stream = Audio::makeRawStream(data, dataSize, 11127, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, nullptr, stream);
+	return true;
+}
+
 } // End of namespace Colony
diff --git a/engines/colony/sound.h b/engines/colony/sound.h
index dd247caef16..bcd5c9c86d7 100644
--- a/engines/colony/sound.h
+++ b/engines/colony/sound.h
@@ -23,7 +23,9 @@
 #define COLONY_SOUND_H
 
 #include "audio/softsynth/pcspk.h"
+#include "audio/mixer.h"
 #include "common/ptr.h"
+#include "common/macresman.h"
 
 namespace Colony {
 
@@ -73,8 +75,11 @@ public:
 private:
 	ColonyEngine *_vm;
 	Audio::PCSpeaker *_speaker;
+	Common::MacResManager *_resMan;
 
 	void playPCSpeaker(int soundID);
+	bool playMacSound(int soundID);
+	bool playResource(int resID);
 };
 
 } // End of namespace Colony
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 3237815cdab..f854246eaf2 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -981,33 +981,36 @@ void ColonyEngine::makeMessageRect(Common::Rect &rr) {
 }
 
 void ColonyEngine::doText(int entry, int center) {
-	Common::File file;
-	if (!file.open("T.DAT")) {
-		warning("doText: Could not open T.DAT");
-		return;
+	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(Common::Path("T.DAT"));
+	if (!file) {
+		file = Common::MacResManager::openFileOrDataFork(Common::Path("CData/Tdata"));
+		if (!file) {
+			warning("doText: Could not open text file");
+			return;
+		}
 	}
 
-	uint32 entries = file.readUint32BE();
+	uint32 entries = file->readUint32BE();
 	if (entry < 0 || (uint32)entry >= entries) {
 		warning("doText: Entry %d out of range (max %d)", entry, entries);
-		file.close();
+		delete file;
 		return;
 	}
 
-	file.seek(4 + entry * 8);
-	uint32 offset = file.readUint32BE();
-	uint16 ch = file.readUint16BE();
-	file.readUint16BE(); // lines (unused)
+	file->seek(4 + entry * 8);
+	uint32 offset = file->readUint32BE();
+	uint16 ch = file->readUint16BE();
+	file->readUint16BE(); // lines (unused)
 
 	if (ch == 0) {
-		file.close();
+		delete file;
 		return;
 	}
 
 	byte *page = (byte *)malloc(ch + 1);
-	file.seek(offset);
-	file.read(page, ch);
-	file.close();
+	file->seek(offset);
+	file->read(page, ch);
+	delete file;
 	page[ch] = 0;
 
 	// Decode: Chain XOR starting from end with '\'


Commit: 69a21736a3a13fc153e92e55000aea426bc92f84
    https://github.com/scummvm/scummvm/commit/69a21736a3a13fc153e92e55000aea426bc92f84
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:23+02:00

Commit Message:
COLONY: improved mac intro

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 3f6f987cffe..6f0d2a2fff5 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -35,7 +35,9 @@
 #include "graphics/fontman.h"
 #include "graphics/font.h"
 #include "graphics/fonts/dosfont.h"
+#include "graphics/fonts/macfont.h"
 #include "graphics/cursorman.h"
+#include "image/pict.h"
 #include <math.h>
 
 namespace Colony {
@@ -125,6 +127,12 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_blackoutColor = 15; // Set to white (vINTWHITE) for better visibility in darkness
 
 	_sound = new Sound(this);
+	_resMan = new Common::MacResManager();
+	if (getPlatform() == Common::kPlatformMacintosh) {
+		if (!_resMan->open("Colony")) {
+			_resMan->open("Colony.bin");
+		}
+	}
 	initTrig();
 }
 
@@ -134,6 +142,7 @@ ColonyEngine::~ColonyEngine() {
 	delete _frameLimiter;
 	delete _gfx;
 	delete _sound;
+	delete _resMan;
 }
 
 
@@ -510,7 +519,7 @@ Common::Error ColonyEngine::run() {
 	// Frame limiter: target 60fps, like Freescape engine
 	_frameLimiter = new Graphics::FrameLimiter(_system, 60);
 
-	scrollInfo();
+	playIntro();
 
 	loadMap(1); // Try to load the first map
 	_system->lockMouse(true);
@@ -669,8 +678,109 @@ Common::Error ColonyEngine::run() {
 	return Common::kNoError;
 }
 
-void ColonyEngine::scrollInfo() {
+void ColonyEngine::playIntro() {
+	if (getPlatform() == Common::kPlatformMacintosh) {
+		// Load the Mac "Commando" font (FOND 190, 12pt) from Colony resources.
+		// Original intro.c: TextFont(190); TextSize(12);
+		// FONT resource ID = FOND_ID * 128 + size = 190 * 128 + 12 = 24332
+		Graphics::MacFONTFont *macFont = nullptr;
+		if (_resMan) {
+			Common::SeekableReadStream *fontStream = _resMan->getResource(MKTAG('F', 'O', 'N', 'T'), 24332);
+			if (fontStream) {
+				macFont = new Graphics::MacFONTFont();
+				if (!macFont->loadFont(*fontStream)) {
+					warning("playIntro: failed to load Commando 12pt font");
+					delete macFont;
+					macFont = nullptr;
+				}
+				delete fontStream;
+			} else {
+				warning("playIntro: FONT 24332 not found in Colony resources");
+			}
+		}
+
+		// Original: intro() in intro.c
+		// 1. ScrollInfo() - scrolling story text with BeamMe sound
+		scrollInfo(macFont);
+
+		// 2. Wait for sound to finish
+		_gfx->clear(_gfx->black());
+		_gfx->copyToScreen();
+
+		// 3. "MindScape Presents" logo + PlayMars() + makestars()
+		// B&W Colony has PICT -32748 (254x251) for logo
+		_gfx->clear(_gfx->black());
+		drawPict(-32748);
+		_sound->play(Sound::kMars);
+		if (makeStars(_screenR, 0)) { _sound->stop(); delete macFont; return; }
+
+		// 4. "The Colony by David A. Smith" logo + makestars()
+		// B&W Colony has PICT -32750 (394x252) for title card
+		_gfx->clear(_gfx->black());
+		drawPict(-32750);
+		if (makeStars(_screenR, 0)) { _sound->stop(); delete macFont; return; }
+
+		// 5. Empty stars
+		_gfx->clear(_gfx->black());
+		_gfx->copyToScreen();
+		if (makeStars(_screenR, 0)) { _sound->stop(); delete macFont; return; }
+
+		// 6. TimeSquare("...BLACK HOLE COLLISION...")
+		if (timeSquare("...BLACK HOLE COLLISION...", macFont)) { _sound->stop(); delete macFont; return; }
+
+		// 7. Makeblackhole()
+		_gfx->clear(_gfx->black());
+		_gfx->copyToScreen();
+		if (makeBlackHole()) { _sound->stop(); delete macFont; return; }
+
+		// 8. TimeSquare("...FUEL HAS BEEN DEPLETED...")
+		if (timeSquare("...FUEL HAS BEEN DEPLETED...", macFont)) { _sound->stop(); delete macFont; return; }
+
+		// 9. TimeSquare("...PREPARE FOR CRASH LANDING...")
+		if (timeSquare("...PREPARE FOR CRASH LANDING...", macFont)) { _sound->stop(); delete macFont; return; }
+
+		// 10. makeplanet() + EndCSound() — simplified: just a delay
+		_gfx->clear(_gfx->black());
+		_gfx->copyToScreen();
+		_system->delayMillis(500);
+
+		// 11. Final crash: DoExplodeSound() + InvertRect flashing
+		_gfx->clear(_gfx->black());
+		_gfx->copyToScreen();
+		_sound->play(Sound::kExplode);
+		for (int i = 0; i < 16; i++) {
+			_gfx->clear(i % 2 ? _gfx->white() : _gfx->black());
+			_gfx->copyToScreen();
+			_system->delayMillis(50);
+		}
+		_gfx->clear(_gfx->black());
+		_gfx->copyToScreen();
+		_sound->stop();
+		delete macFont;
+		macFont = nullptr;
+
+		// Restore palette entries modified during intro (128, 160-176, 200-213)
+		// back to grayscale so normal gameplay rendering isn't affected
+		byte restorePal[256 * 3];
+		for (int i = 0; i < 256; i++) {
+			restorePal[i * 3 + 0] = i;
+			restorePal[i * 3 + 1] = i;
+			restorePal[i * 3 + 2] = i;
+		}
+		_gfx->setPalette(restorePal + 128 * 3, 128, 128);
+	} else {
+		scrollInfo();
+	}
+}
+
+void ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
+	// Original: ScrollInfo() in intro.c
+	// Displays story text in blue gradient on black, plays DoBeammeSound(),
+	// scrolls text up from bottom, waits for click, scrolls off top.
+	// Mac original: TextFont(190 = Commando); TextSize(12);
+	// Text starts bright blue (0xFFFF) and fades by -4096 per line.
 	const char *story[] = {
+		"",
 		"Mankind has left the",
 		"cradle of earth and",
 		"is beginning to eye",
@@ -679,27 +789,48 @@ void ColonyEngine::scrollInfo() {
 		"distant planets but has",
 		"yet to meet any alien",
 		"life forms.",
-		"****",
+		"",      // null separator in original
 		"Until now...",
-		"****",
-		"Press any key to begin",
+		"",      // null separator in original
+		"Click to begin",
 		"the Adventure..."
 	};
 	const int storyLength = ARRAYSIZE(story);
 
+	if (getPlatform() == Common::kPlatformMacintosh)
+		_sound->play(Sound::kBeamMe);
+
 	_gfx->clear(_gfx->black());
 	Graphics::DosFont dosFont;
-	const Graphics::Font *font = &dosFont;
+	const Graphics::Font *font = macFont ? macFont : (const Graphics::Font *)&dosFont;
 
-	int centerY = _height / 2;
-	centerY -= (storyLength * 10) / 2;
-	centerY += 5;
+	// Original uses 19px line height, centers vertically
+	int lineHeight = 19;
+	int totalHeight = lineHeight * storyLength;
+	int ht = (_height - totalHeight) / 2;
 
+	// Set up blue gradient palette entries (200-213) for story text
+	// Mac original: tColor.blue starts at 0xFFFF and decreases by 4096 per visible line
+	byte pal[14 * 3]; // storyLength entries
+	memset(pal, 0, sizeof(pal));
 	for (int i = 0; i < storyLength; i++) {
-		_gfx->drawString(font, story[i], _width / 2, centerY + 10 * i, 9, Graphics::kTextAlignCenter);
+		// Blue intensity: starts at 255 (0xFFFF >> 8), decreases by ~16 per line
+		int blue = 255 - i * 16;
+		if (blue < 0) blue = 0;
+		pal[i * 3 + 0] = 0;     // R
+		pal[i * 3 + 1] = 0;     // G
+		pal[i * 3 + 2] = blue;  // B
+	}
+	_gfx->setPalette(pal, 200, storyLength);
+
+	// Draw text with per-line blue gradient
+	for (int i = 0; i < storyLength; i++) {
+		if (strlen(story[i]) > 0)
+			_gfx->drawString(font, story[i], _width / 2, ht + lineHeight * i, 200 + i, Graphics::kTextAlignCenter);
 	}
 	_gfx->copyToScreen();
 
+	// Wait for click (original: while(!Button()); while(Button()&&!qt);)
 	bool waiting = true;
 	while (waiting && !shouldQuit()) {
 		Common::Event event;
@@ -710,8 +841,358 @@ void ColonyEngine::scrollInfo() {
 		_system->delayMillis(10);
 	}
 
-	_gfx->fillRect(_screenR, 0);
+	_sound->stop();
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+}
+
+bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
+	// Original: makestars() in stars.c
+	// Uses 75 moving stars that streak outward from center using XOR lines.
+	const int MAXSTAR = 0x1FF;
+	const int NSTARS = 75;
+	const int deltapd = 0x008;
+
+	int centerX = r.width() / 2;
+	int centerY = r.height() / 2;
+
+	// Build perspective lookup table: rtable[i] = (128*128)/i
+	int rtable[MAXSTAR + 1];
+	rtable[0] = 32000;
+	for (int i = 1; i <= MAXSTAR; i++)
+		rtable[i] = (128 * 128) / i;
+
+	// First draw static background stars (150 random dots)
+	for (int i = 0; i < 150; i++) {
+		int s = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
+		int c = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
+		int d = _randomSource.getRandomNumber(MAXSTAR);
+		if (d < 1) d = 1;
+		int rr = rtable[d];
+		int xx = centerX + (int)(((long long)s * rr) >> 7);
+		int yy = centerY + (int)(((long long)c * rr) >> 7);
+		if (xx >= 0 && xx < _width && yy >= 0 && yy < _height)
+			_gfx->setPixel(xx, yy, 15);
+	}
+
+	// Initialize moving stars
+	int xang[NSTARS], yang[NSTARS], dist[NSTARS];
+	int xsave1[NSTARS], ysave1[NSTARS], xsave2[NSTARS], ysave2[NSTARS];
+
+	for (int i = 0; i < NSTARS; i++) {
+		int d = dist[i] = _randomSource.getRandomNumber(MAXSTAR);
+		if (d <= 0x030) d = dist[i] = MAXSTAR;
+		int s = xang[i] = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
+		int c = yang[i] = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
+
+		int rr = rtable[d];
+		xsave1[i] = centerX + (int)(((long long)s * rr) >> 7);
+		ysave1[i] = centerY + (int)(((long long)c * rr) >> 7);
+
+		int d2 = d - deltapd;
+		if (d2 < 1) d2 = 1;
+		rr = rtable[d2];
+		xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
+		ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
+
+		_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
+	}
 	_gfx->copyToScreen();
+
+	// Animate: original loops ~200 frames or until Mars sound repeats 2x
+	for (int k = 0; k < 120; k++) {
+		Common::Event event;
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONUP)
+				return true;
+		}
+		if (shouldQuit()) return true;
+
+		for (int i = 0; i < NSTARS; i++) {
+			// Erase previous
+			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 0);
+
+			int s = xang[i];
+			int c = yang[i];
+
+			if (dist[i] <= 0x030) {
+				dist[i] = MAXSTAR;
+				int rr = rtable[MAXSTAR];
+				xsave1[i] = centerX + (int)(((long long)s * rr) >> 7);
+				ysave1[i] = centerY + (int)(((long long)c * rr) >> 7);
+			} else {
+				xsave1[i] = xsave2[i];
+				ysave1[i] = ysave2[i];
+			}
+
+			int d = (dist[i] -= deltapd);
+			if (d < 1) d = 1;
+			int rr = rtable[d];
+			xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
+			ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
+
+			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
+		}
+		_gfx->copyToScreen();
+		_system->delayMillis(16);
+	}
+
+	// Fade-out phase: stars fly off without resetting
+	int nstars = 2 * ((MAXSTAR - 0x030) / deltapd);
+	if (nstars > 200) nstars = 200;
+	for (int k = 0; k < nstars; k++) {
+		Common::Event event;
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONUP)
+				return true;
+		}
+		if (shouldQuit()) return true;
+
+		for (int i = 0; i < NSTARS; i++) {
+			int d = dist[i];
+			int s = xang[i];
+			int c = yang[i];
+			dist[i] -= deltapd;
+			if (dist[i] <= 0x030) dist[i] = MAXSTAR;
+
+			if (d >= 1 && d <= MAXSTAR) {
+				int rr1 = rtable[d];
+				int d2 = d - deltapd;
+				if (d2 < 1) d2 = 1;
+				int rr2 = rtable[d2];
+				int x1 = centerX + (int)(((long long)s * rr1) >> 7);
+				int y1 = centerY + (int)(((long long)c * rr1) >> 7);
+				int x2 = centerX + (int)(((long long)s * rr2) >> 7);
+				int y2 = centerY + (int)(((long long)c * rr2) >> 7);
+				_gfx->drawLine(x1, y1, x2, y2, 15);
+			}
+		}
+		_gfx->copyToScreen();
+		_system->delayMillis(8);
+	}
+
+	return false;
+}
+
+bool ColonyEngine::makeBlackHole() {
+	// Original: Makeblackhole() in intro.c
+	// Mac original draws spiral lines with fading colors:
+	// bcolor starts at (0,0,0) and subtracts (rd=2048, gd=1024, bd=4096) per step,
+	// which wraps around creating shifting color gradients.
+	// We use palette entries 128-191 for the gradient colors.
+	int centerX = _width / 2;
+	int centerY = _height / 2;
+	int dec = 16;
+	int starcnt = 0;
+
+	// Build a lookup table matching the original rtable: rtable[i] = (128*128)/i
+	int rtable[1024];
+	rtable[0] = 32000;
+	for (int i = 1; i < 1024; i++)
+		rtable[i] = (128 * 128) / i;
+
+	for (int k = 0; k < 17; k += 4) {
+		// Reset color per k-iteration (matches Mac: bcolor = {0,0,0} at start of each k)
+		int colorR = 0, colorG = 0, colorB = 0;
+		int rd = 2048, gd = 1024, bd = 4096;
+
+		for (int i = 1000; i > 32; i -= dec) {
+			// Mac original subtracts from color channels (wrapping as uint16);
+			// We simulate this as a gradient from dark to bright
+			// Since Mac uses unsigned wrap: 0 - 4096 = 0xF000 = bright.
+			// After one full cycle (16 steps), the colors cycle.
+			// Map to palette entry based on step
+			colorB = (colorB - bd) & 0xFFFF;
+			colorR = (colorR - rd) & 0xFFFF;
+			colorG = (colorG - gd) & 0xFFFF;
+
+			// Map Mac 16-bit color to 8-bit
+			uint8 palR = (colorR >> 8) & 0xFF;
+			uint8 palG = (colorG >> 8) & 0xFF;
+			uint8 palB = (colorB >> 8) & 0xFF;
+
+			// Use palette entry 128 for current step color
+			byte pal[3] = { palR, palG, palB };
+			_gfx->setPalette(pal, 128, 1);
+
+			starcnt++;
+			if (starcnt == 8) starcnt = 0;
+
+			for (int j = 0; j < 256; j += 8) {
+				int idx = (j + starcnt) & 0xFF;
+				int rt1 = rtable[MIN(i + k, 1023)];
+				int x1 = centerX + (int)(((long long)rt1 * _sint[idx]) >> 7);
+				int y1 = centerY + (int)(((long long)rt1 * _cost[idx]) >> 7);
+
+				int rt2 = rtable[MIN(i + k + 8, 1023)];
+				int x2 = centerX + (int)(((long long)rt2 * _sint[idx]) >> 7);
+				int y2 = centerY + (int)(((long long)rt2 * _cost[idx]) >> 7);
+
+				_gfx->drawLine(x1, y1, x2, y2, 128);
+			}
+
+			// Update screen every step and add a small delay
+			// to simulate the original 68k rendering speed
+			_gfx->copyToScreen();
+			_system->delayMillis(16);
+
+			Common::Event event;
+			while (_system->getEventManager()->pollEvent(event)) {
+				if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONUP)
+					return true;
+			}
+			if (shouldQuit()) return true;
+		}
+	}
+	_gfx->copyToScreen();
+	return false;
+}
+
+bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *macFont) {
+	// Original: TimeSquare() in intro.c
+	// 1. Draw horizontal blue gradient lines above/below center
+	// 2. Scroll red text from right to center
+	// 3. Flash klaxon 6 times with inverted rect
+	// 4. Play Mars again, scroll text off to the left
+	//
+	// Mac original: fcolor starts at (0,0,0xFFFF) and subtracts 4096 per pair of lines.
+	// Text is drawn in red (0xFFFF,0,0) on black background.
+
+	_gfx->clear(_gfx->black());
+
+	Graphics::DosFont dosFont;
+	const Graphics::Font *font = macFont ? macFont : (const Graphics::Font *)&dosFont;
+	int swidth = font->getStringWidth(str);
+
+	int centery = _height / 2 - 10;
+
+	// Set up gradient palette entries (160-175) for the blue gradient lines
+	// Mac original: blue starts at 0xFFFF and decreases by 4096 per line pair
+	for (int i = 0; i < 16; i++) {
+		int blue = 255 - i * 16; // 255, 239, 223, ... 15
+		if (blue < 0) blue = 0;
+		byte pal[3] = { 0, 0, (byte)blue };
+		_gfx->setPalette(pal, 160 + i, 1);
+	}
+	// Set palette entry 176 for red text
+	{
+		byte pal[3] = { 255, 0, 0 };
+		_gfx->setPalette(pal, 176, 1);
+	}
+
+	// Draw blue gradient lines above/below center band
+	for (int i = 0; i < 16; i++) {
+		_gfx->drawLine(0, centery - 2 - i * 2, _width, centery - 2 - i * 2, 160 + i);
+		_gfx->drawLine(0, centery - 2 - (i * 2 + 1), _width, centery - 2 - (i * 2 + 1), 160 + i);
+		_gfx->drawLine(0, centery + 16 + i * 2, _width, centery + 16 + i * 2, 160 + i);
+		_gfx->drawLine(0, centery + 16 + i * 2 + 1, _width, centery + 16 + i * 2 + 1, 160 + i);
+	}
+	_gfx->copyToScreen();
+
+	// Phase 1: Scroll text in from the right to center
+	int targetX = (_width - swidth) / 2;
+	for (int x = _width; x > targetX; x -= 2) {
+		// Redraw center band
+		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), 0);
+		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft); // Red text
+		_gfx->copyToScreen();
+
+		Common::Event event;
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
+				return true;
+		}
+		if (shouldQuit()) return true;
+		_system->delayMillis(8);
+	}
+
+	// Phase 2: Klaxon flash – original does 6 iterations of:
+	//   while(!SoundDone()); StopSound(); PlayKlaxon(); InvertRect(&invrt);
+	_sound->stop();
+	for (int i = 0; i < 6; i++) {
+		_sound->play(Sound::kKlaxon);
+		// Invert the center band area
+		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), i % 2 ? 15 : 0);
+		_gfx->drawString(font, str, targetX, centery + 2, i % 2 ? 0 : 15, Graphics::kTextAlignLeft);
+		_gfx->copyToScreen();
+		_system->delayMillis(300);
+
+		Common::Event event;
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
+				return true;
+		}
+		if (shouldQuit()) return true;
+	}
+
+	// Phase 3: PlayMars(), scroll text off to the left
+	_sound->play(Sound::kMars);
+	for (int x = targetX; x > -swidth; x -= 2) {
+		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), 0);
+		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft); // Red text
+		_gfx->copyToScreen();
+
+		Common::Event event;
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
+				return true;
+		}
+		if (shouldQuit()) return true;
+		_system->delayMillis(8);
+	}
+
+	return false;
+}
+
+void ColonyEngine::drawPict(int resID) {
+	// Original: DoPicture() in intro.c
+	// Loads a PICT resource from the Colony application, centers it on screen.
+	if (!_resMan || !(_resMan->isMacFile() || _resMan->hasResFork()))
+		return;
+
+	// Try both signed interpretations for negative resource IDs
+	Common::SeekableReadStream *pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
+	if (!pictStream) {
+		pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), resID);
+		if (!pictStream) {
+			warning("drawPict: could not load PICT %d", resID);
+			return;
+		}
+	}
+
+	::Image::PICTDecoder decoder;
+	if (decoder.loadStream(*pictStream)) {
+		const Graphics::Surface *surface = decoder.getSurface();
+		if (surface) {
+			// Center like the original: locate = centered, clip = inset by 1
+			int x = (_width - surface->w) / 2;
+			int y = (_height - surface->h) / 2;
+
+			// Detect if this is a 1-bit B&W PICT (CLUT8 with only values 0/1)
+			// In Mac QuickDraw: bit 0 = white (background), bit 1 = black (foreground)
+			bool isBW = (surface->format == Graphics::PixelFormat::createFormatCLUT8());
+
+			for (int iy = 0; iy < surface->h; iy++) {
+				if (y + iy < 0 || y + iy >= _height) continue;
+				for (int ix = 0; ix < surface->w; ix++) {
+					if (x + ix < 0 || x + ix >= _width) continue;
+					uint32 color = surface->getPixel(ix, iy);
+					if (isBW) {
+						// Mac QuickDraw: 0=white, 1=black
+						// Map to our palette: white=15 (IntWhite), black=0
+						color = (color == 0) ? 15 : 0;
+					}
+					// Only draw non-black pixels so the logo sits on the black background
+					if (color != 0)
+						_gfx->setPixel(x + ix, y + iy, color);
+				}
+			}
+			_gfx->copyToScreen();
+		}
+	} else {
+		warning("drawPict: failed to decode PICT %d", resID);
+	}
+	delete pictStream;
 }
 
 bool ColonyEngine::loadAnimation(const Common::String &name) {
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 6e917653c34..40b3aa9a866 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -298,7 +298,7 @@ public:
 	void quadrant();
 	int checkwall(int xnew, int ynew, Locate *pobject);
 	void cCommand(int xnew, int ynew, bool allowInteraction);
-	void scrollInfo();
+	void scrollInfo(const Graphics::Font *macFont = nullptr);
 	void checkCenter();
 	void fallThroughHole();
 
@@ -448,6 +448,7 @@ private:
 	Common::Rect _backgroundClip;
 	Common::Rect _backgroundLocate;
 	bool _backgroundActive;
+	Common::MacResManager *_resMan;
 	byte _topBG[8];
 	byte _bottomBG[8];
 	int16 _divideBG;
@@ -457,6 +458,11 @@ private:
 	bool _doorOpen;
 	int _elevatorFloor;
 
+	void playIntro();
+	bool makeStars(const Common::Rect &r, int btn);
+	bool makeBlackHole();
+	bool timeSquare(const Common::String &str, const Graphics::Font *macFont = nullptr);
+	void drawPict(int resID);
 	bool loadAnimation(const Common::String &name);
 	void deleteAnimation();
 	void playAnimation();
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index dfb5a5cf7d5..61cfaae3010 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -47,11 +47,12 @@ Sound::~Sound() {
 void Sound::stop() {
 	if (_speaker->isPlaying())
 		_speaker->stop();
+	if (_vm->_mixer->isSoundHandleActive(_handle))
+		_vm->_mixer->stopHandle(_handle);
 }
 
 void Sound::play(int soundID) {
-	if (_speaker->isPlaying())
-		_speaker->stop();
+	stop();
 
 	if (_vm->getPlatform() == Common::kPlatformMacintosh)
 		playMacSound(soundID);
@@ -262,6 +263,8 @@ bool Sound::playMacSound(int soundID) {
 	case kDoor: resID = 26867; break;
 	case kToilet: resID = 4955; break;
 	case kBath: resID = 11589; break;
+	case kMars: resID = 23390; break;
+	case kBeamMe: resID = 5342; break;
 	default: break;
 	}
 
@@ -294,7 +297,7 @@ bool Sound::playResource(int resID) {
 	delete snd;
 
 	Audio::AudioStream *stream = Audio::makeRawStream(data, dataSize, 11127, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
-	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, nullptr, stream);
+	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_handle, stream);
 	return true;
 }
 
diff --git a/engines/colony/sound.h b/engines/colony/sound.h
index bcd5c9c86d7..c8d9579c344 100644
--- a/engines/colony/sound.h
+++ b/engines/colony/sound.h
@@ -69,13 +69,16 @@ public:
 		kStars3,
 		kStars4,
 		kToilet,
-		kBath
+		kBath,
+		kMars,
+		kBeamMe
 	};
 
 private:
 	ColonyEngine *_vm;
 	Audio::PCSpeaker *_speaker;
 	Common::MacResManager *_resMan;
+	Audio::SoundHandle _handle;
 
 	void playPCSpeaker(int soundID);
 	bool playMacSound(int soundID);


Commit: 743a8862da577cde6d9e07937cbf4c5b96910764
    https://github.com/scummvm/scummvm/commit/743a8862da577cde6d9e07937cbf4c5b96910764
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:24+02:00

Commit Message:
COLONY: auto-detect render mode from platform and add Mac color pipeline

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/detection.cpp
    engines/colony/render.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 6f0d2a2fff5..cbc195a2419 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -60,16 +60,22 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_hasKeycard = false;
 	_unlocked = false;
 	_weapons = 0;
-	_wireframe = false;
 	_widescreen = ConfMan.getBool("widescreen_mod");
 
 	// Render mode: EGA (DOS wireframe default) or Macintosh (filled polygons)
 	if (!ConfMan.hasKey("render_mode") || ConfMan.get("render_mode").empty())
-		_renderMode = Common::kRenderEGA;
+		_renderMode = Common::kRenderDefault;
 	else
 		_renderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
-	if (_renderMode == Common::kRenderDefault)
-		_renderMode = Common::kRenderEGA;
+
+	if (_renderMode == Common::kRenderDefault) {
+		if (getPlatform() == Common::kPlatformMacintosh)
+			_renderMode = Common::kRenderMacintosh;
+		else
+			_renderMode = Common::kRenderEGA;
+	}
+	
+	_wireframe = (_renderMode != Common::kRenderMacintosh);
 	_speedShift = 2; // DOS default: speedshift=1, but 2 feels better with our frame rate
 	
 	memset(_wall, 0, sizeof(_wall));
@@ -121,6 +127,8 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	for (int i = 0; i < 3; i++)
 		_corePower[i] = 0;
 	_coreIndex = 0;
+	_hasMacColors = false;
+	memset(_macColors, 0, sizeof(_macColors));
 	_orbit = 0;
 	_armor = 0;
 	_gametest = false;
@@ -471,6 +479,50 @@ void ColonyEngine::initRobots() {
 	debug("initRobots: spawned %d robots on level %d", maxrob, _level);
 }
 
+void ColonyEngine::loadMacColors() {
+	_hasMacColors = false;
+	Common::SeekableReadStream *file = nullptr;
+
+	// Try MacResManager first (for resource fork / AppleDouble files)
+	Common::Path path("Color256");
+	file = Common::MacResManager::openFileOrDataFork(path);
+	if (!file) {
+		path = Common::Path("CData/Color256");
+		file = Common::MacResManager::openFileOrDataFork(path);
+	}
+
+	// Fallback to plain file open (for raw data files)
+	if (!file) {
+		Common::File *f = new Common::File();
+		if (f->open(Common::Path("Color256"))) {
+			file = f;
+		} else if (f->open(Common::Path("CData/Color256"))) {
+			file = f;
+		} else {
+			delete f;
+		}
+	}
+	if (!file) return;
+
+	uint32 vers = file->readUint32BE(); // Should be 'v1.0' = 0x76312E30
+	(void)vers; // Ignore
+	uint16 cnum = file->readUint16BE();
+	if (cnum > 145) cnum = 145;
+
+	for (int i = 0; i < cnum; i++) {
+		_macColors[i].fg[0] = file->readUint16BE();
+		_macColors[i].fg[1] = file->readUint16BE();
+		_macColors[i].fg[2] = file->readUint16BE();
+		_macColors[i].bg[0] = file->readUint16BE();
+		_macColors[i].bg[1] = file->readUint16BE();
+		_macColors[i].bg[2] = file->readUint16BE();
+		_macColors[i].pattern = file->readUint16BE();
+	}
+	delete file;
+	_hasMacColors = true;
+	debug("Loaded %d Mac colors", cnum);
+}
+
 void ColonyEngine::initTrig() {
 	// Compute standard sin/cos lookup tables (256 steps = full circle, scaled by 128)
 	for (int i = 0; i < 256; i++) {
@@ -514,6 +566,16 @@ Common::Error ColonyEngine::run() {
 		pal[i * 3 + 1] = i;
 		pal[i * 3 + 2] = i;
 	}
+
+	loadMacColors();
+	if (_hasMacColors) {
+		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;
+			pal[(100 + i) * 3 + 2] = _macColors[i].fg[2] >> 8;
+		}
+	}
+
 	_gfx->setPalette(pal, 0, 256);
 
 	// Frame limiter: target 60fps, like Freescape engine
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 40b3aa9a866..618d5a275ae 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -237,6 +237,12 @@ struct LevelData {
 	uint8 data[10][5];     // saved wall feature bytes (5 per location)
 };
 
+struct MacColor {
+	uint16 fg[3];
+	uint16 bg[3];
+	uint16 pattern;
+};
+
 struct Image {
 	int16 width;
 	int16 height;
@@ -293,6 +299,7 @@ public:
 	Common::Platform getPlatform() const { return _gameDescription->platform; }
 
 	void initTrig();
+	void loadMacColors();
 	void loadMap(int mnum);
 	void corridor();
 	void quadrant();
@@ -368,6 +375,9 @@ private:
 	int _fl;                    // 0=not in forklift, 1=in forklift empty, 2=carrying object
 	LevelData _levelData[8];   // per-level wall state persistence
 
+	MacColor _macColors[145];
+	bool _hasMacColors;
+
 	int _frntxWall, _frntyWall;
 	int _sidexWall, _sideyWall;
 	int _frntx, _frnty;
diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
index 4ca8ca5b479..63f71e658eb 100644
--- a/engines/colony/detection.cpp
+++ b/engines/colony/detection.cpp
@@ -31,7 +31,7 @@ static const PlainGameDescriptor colonyGames[] = {
 const ADGameDescription gameDescriptions[] = {
 	{
 		"colony",
-		"B&W",
+		"",
 		AD_ENTRY2s("Colony", "50f79a9fd68055a49171eaf92f3f6b13", 229888,
 		           "Zounds", "9de09af0a8d62f30d6863ff86c95cc51", 552576),
 		Common::EN_ANY,
@@ -39,16 +39,6 @@ const ADGameDescription gameDescriptions[] = {
 		ADGF_NO_FLAGS,
 		GUIO2(GUIO_RENDERMACINTOSH, GUIO_RENDEREGA)
 	},
-	{
-		"colony",
-		"Color",
-		AD_ENTRY2s("Color256", "7955c1cd7b94bba89a31c5c96840eeb2", 1588,
-		           "Color16", "e5382c33f47e5619887eac8991a5c249", 1588),
-		Common::EN_ANY,
-		Common::kPlatformMacintosh,
-		ADGF_NO_FLAGS,
-		GUIO2(GUIO_RENDERMACINTOSH, GUIO_RENDEREGA)
-	},
 	{
 		"colony",
 		"",
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 79531bbab37..3eeff1caa42 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -198,6 +198,93 @@ static int lookupMacPattern(int colorIdx) {
 	}
 }
 
+// Pack Mac 16-bit RGB into 32-bit ARGB with 0xFF000000 marker for direct RGB.
+static uint32 packMacColor(const uint16 rgb[3]) {
+	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
+}
+
+// Map ObjColor constant → Mac Color256 index (cColor[] from colordef.h).
+static int mapObjColorToMacColor(int colorIdx) {
+	switch (colorIdx) {
+	case kColorBath:      return 97;  // c_tub
+	case kColorWater:     return 102; // c_water
+	case kColorSilver:    return 103; // c_mirror
+	case kColorReactor:   return 106; // c_reactor
+	case kColorBlanket:   return 68;  // c_bedblnkt
+	case kColorSheet:     return 69;  // c_bedsheet
+	case kColorBed:       return 70;  // c_bedhead
+	case kColorBox:       return 84;  // c_box1
+	case kColorBench:     return 104; // c_bench
+	case kColorChair:     return 66;  // c_cchair (captain's chair; DOS cCHAIR != Mac c_chair)
+	case kColorChairBase: return 67;  // c_cchairbase
+	case kColorCouch:     return 74;  // c_couch
+	case kColorConsole:   return 79;  // c_console
+	case kColorTV:        return 76;  // c_tv
+	case kColorTVScreen:  return 77;  // c_tvscreen
+	case kColorDrawer:    return 96;  // c_vanity
+	case kColorDesk:      return 58;  // c_desk
+	case kColorDeskTop:   return 59;  // c_desktop
+	case kColorDeskChair: return 60;  // c_deskchair
+	case kColorMac:       return 61;  // c_computer
+	case kColorMacScreen: return 62;  // c_screen
+	case kColorCryo:      return 90;  // c_cryo
+	case kColorCryoGlass: return 91;  // c_cryoglass
+	case kColorCryoBase:  return 92;  // c_cryostand
+	case kColorForklift:  return 86;  // c_forklift
+	case kColorTread1:    return 88;  // c_fltreadtop
+	case kColorTread2:    return 89;  // c_fltreadside
+	case kColorPot:       return 64;  // c_pot
+	case kColorPlant:     return 63;  // c_plant
+	case kColorPower:     return 80;  // c_consoletop
+	case kColorPBase:     return 81;  // c_powerbase
+	case kColorPSource:   return 83;  // c_powersource
+	case kColorTable:     return 71;  // c_table
+	case kColorTableBase: return 71;  // c_table
+	case kColorPStand:    return 72;  // c_proj
+	case kColorPLens:     return 73;  // c_projlens
+	case kColorProjector: return 72;  // c_proj
+	case kColorTele:      return 93;  // c_teleport
+	case kColorTeleDoor:  return 94;  // c_teledoor
+	case kColorRainbow1:  return 26;  // c_color0
+	case kColorRainbow2:  return 27;  // c_color1
+	case kColorCube:      return 39;  // c_diamond (DOS cCUBE = diamond/octahedron alien)
+	case kColorDrone:     return 50;  // c_drone
+	case kColorClaw1:     return 54;  // c_jaws1
+	case kColorClaw2:     return 55;  // c_jaws2
+	case kColorEyes:      return 32;  // c_eye
+	case kColorEye:       return 34;  // c_eyeball (DOS cEYE = eyeball globe, not c_meye)
+	case kColorIris:      return 35;  // c_iris
+	case kColorPupil:     return 36;  // c_pupil
+	case kColorPyramid:   return 37;  // c_pyramid
+	case kColorQueen:     return 48;  // c_queenP
+	case kColorTopSnoop:  return 56;  // c_snooper1
+	case kColorBottomSnoop: return 57; // c_snooper2
+	case kColorUPyramid:  return 41;  // c_upyramid
+	case kColorShadow:    return 26;  // c_color0
+	case kColorWall:      return 6;   // c_dwall
+	default: return 6; // c_dwall fallback
+	}
+}
+
+// Helper: set up Mac color stipple rendering for a given cColor[] pattern.
+// Configures setMacColors/setStippleData/setWireframe for the pattern type.
+// Returns the stipple pointer (null for solid patterns) — caller must clear with setStippleData(nullptr).
+static const byte *setupMacPattern(Renderer *gfx, int pattern, uint32 fg, uint32 bg) {
+	const byte *stipple = (pattern >= 1 && pattern <= 3) ? kMacStippleData[pattern] : nullptr;
+	if (stipple) {
+		gfx->setMacColors(fg, bg);
+		gfx->setStippleData(stipple);
+		gfx->setWireframe(true, (int)bg);
+	} else if (pattern == 4) {
+		// BLACK: solid fg fill
+		gfx->setWireframe(true, (int)fg);
+	} else {
+		// WHITE (0): solid bg fill + fg outline
+		gfx->setWireframe(true, (int)bg);
+	}
+	return stipple;
+}
+
 // DOS object geometry constants
 static const int kScreenPts[8][3] = {
 	{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
@@ -1242,7 +1329,37 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 				continue;
 			}
 
-			if (lit) {
+			if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+				// Mac color rendering: follows SuperPoly() from calcrobo.c:429-505.
+				int mIdx = mapObjColorToMacColor(colorIdx);
+				int pattern = _macColors[mIdx].pattern;
+				uint32 fg = packMacColor(_macColors[mIdx].fg);
+				uint32 bg = packMacColor(_macColors[mIdx].bg);
+				debug(5, "draw3DPrism Mac: colorIdx=%d mIdx=%d pat=%d fg=0x%08X bg=0x%08X lit=%d",
+				      colorIdx, mIdx, pattern, fg, bg, lit);
+
+				if (!lit) {
+					// Mac unlit: all non-wall surfaces fill solid black
+					fg = 0xFF000000;
+					bg = 0xFF000000;
+					pattern = 4; // force BLACK (solid fill)
+				}
+
+				if (pattern == 5) {
+					// CLEAR: outline only (Mac: FramePoly with fg, no fill)
+					_gfx->setWireframe(true, -1);
+					_gfx->draw3DPolygon(px, py, pz, count, 0xFF000000);
+				} else {
+					const byte *stipple = setupMacPattern(_gfx, pattern, fg, bg);
+					// Mac SuperPoly: FramePoly uses fg color for outline.
+					// In Color256, fg is (0,0,0) for ~90% of materials.
+					// BLACK pattern has no FramePoly in original, so outline
+					// matches fill (invisible). Others: black outline.
+					uint32 outlineColor = (pattern == 4) ? fg : (uint32)0xFF000000;
+					_gfx->draw3DPolygon(px, py, pz, count, outlineColor);
+					if (stipple) _gfx->setStippleData(nullptr);
+				}
+			} else if (lit) {
 				if (_renderMode == Common::kRenderMacintosh) {
 					// Mac B&W: stipple dither pattern fill + black outline
 					int pattern = lookupMacPattern(colorIdx);
@@ -1273,8 +1390,13 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 	const long rotCos = _cost[ang];
 	const long rotSin = _sint[ang];
 	const bool lit = (_corePower[_coreIndex] > 0);
-	// Mac B&W: black outlines; EGA: green outlines; unlit: white
-	const uint32 color = lit ? (_renderMode == Common::kRenderMacintosh ? 0 : 2) : 15;
+	// Mac color: c_plant bg; Mac B&W: black; EGA: green; unlit: white/black
+	uint32 color;
+	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+		color = lit ? packMacColor(_macColors[63].bg) : 0xFF000000; // c_plant
+	} else {
+		color = lit ? (_renderMode == Common::kRenderMacintosh ? 0 : 2) : 15;
+	}
 
 	for (int i = 0; i < def.surfaceCount; i++) {
 		const int n = def.surfaces[i][1];
@@ -1362,7 +1484,22 @@ void ColonyEngine::draw3DSphere(const Thing &obj, int pt0x, int pt0y, int pt0z,
 		pz[i] = cz + radius * (sinA * upZ);
 	}
 
-	if (lit) {
+	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+		// Mac color: map fillColor to Mac color index and use RGB
+		// fillColor is an ObjColor enum value passed by the caller
+		int mIdx = mapObjColorToMacColor((int)fillColor);
+		int pattern = _macColors[mIdx].pattern;
+		uint32 fg = packMacColor(_macColors[mIdx].fg);
+		uint32 bg = packMacColor(_macColors[mIdx].bg);
+		if (!lit) {
+			fg = 0xFF000000;
+			bg = 0xFF000000;
+			pattern = 4;
+		}
+		const byte *stipple = setupMacPattern(_gfx, pattern, fg, bg);
+		_gfx->draw3DPolygon(px, py, pz, N, fg);
+		if (stipple) _gfx->setStippleData(nullptr);
+	} else if (lit) {
 		if (_renderMode == Common::kRenderMacintosh) {
 			int pattern = (fillColor == 15) ? kPatternWhite : kPatternGray;
 			if (!_wireframe) {
@@ -1571,37 +1708,51 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 			u[i] = 0.2f + (data[1 + i*2] / 6.0f) * 0.6f;
 			v[i] = 0.2f + (data[2 + i*2] / 6.0f) * 0.6f;
 		}
-		// Mac: fill arrow polygon with BLACK.
-		// drawchar() sets cColor[cc].pattern=WHITE (all background) and
-		// background is {0,0,0}=BLACK, so SuperPoly fills solid black.
+		// Mac drawchar(): sets cc=c_char0+level-1, fg=black, pattern=WHITE.
+		// SuperPoly fills with BACKGROUND color = cColor[cc].b.
+		// B&W: bg=black. Color: bg=c_char0.bg (e.g., yellow for level 1).
 		// Arrow shapes are concave — GL_POLYGON only handles convex polys.
 		// Decompose into convex parts: triangle head + rectangle shaft.
 		if (_renderMode == Common::kRenderMacintosh) {
-			_gfx->setWireframe(true, 0); // BLACK fill
+			uint32 fillColor;
+			if (_hasMacColors) {
+				int cc = 8 + _level - 1; // c_char0 + level - 1
+				fillColor = packMacColor(_macColors[cc].bg);
+			} else {
+				fillColor = 0; // B&W: black
+			}
+			_gfx->setWireframe(true, (int)fillColor);
 			if (cnum == 'b') {
 				// Right arrow head: (0,3)-(3,0)-(3,6)
 				float hu[3] = {u[0], u[1], u[6]};
 				float hv[3] = {v[0], v[1], v[6]};
-				wallPolygon(corners, hu, hv, 3, 0);
+				wallPolygon(corners, hu, hv, 3, fillColor);
 				// Right arrow shaft: (3,2)-(6,2)-(6,4)-(3,4)
 				float su[4] = {u[2], u[3], u[4], u[5]};
 				float sv[4] = {v[2], v[3], v[4], v[5]};
-				wallPolygon(corners, su, sv, 4, 0);
+				wallPolygon(corners, su, sv, 4, fillColor);
 			} else if (cnum == 'c') {
 				// Left arrow shaft: (0,2)-(0,4)-(3,4)-(3,2)
 				float su[4] = {u[0], u[1], u[2], u[6]};
 				float sv[4] = {v[0], v[1], v[2], v[6]};
-				wallPolygon(corners, su, sv, 4, 0);
+				wallPolygon(corners, su, sv, 4, fillColor);
 				// Left arrow head: (3,6)-(6,3)-(3,0)
 				float hu[3] = {u[3], u[4], u[5]};
 				float hv[3] = {v[3], v[4], v[5]};
-				wallPolygon(corners, hu, hv, 3, 0);
+				wallPolygon(corners, hu, hv, 3, fillColor);
 			}
-			_gfx->setWireframe(true, 255); // restore white wall fill
+			// Restore wall fill
+			uint32 wallFill = _hasMacColors
+				? packMacColor(_macColors[8 + _level - 1].fg)
+				: (uint32)255;
+			_gfx->setWireframe(true, (int)wallFill);
 		}
+		// Outline in black (or vBLACK for EGA)
+		uint32 lineColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh)
+			? (uint32)0xFF000000 : 0;
 		for (int i = 0; i < count; i++) {
 			int n = (i + 1) % count;
-			wallLine(corners, u[i], v[i], u[n], v[n], 0); // vBLACK
+			wallLine(corners, u[i], v[i], u[n], v[n], lineColor);
 		}
 	}
 }
@@ -1639,51 +1790,49 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 	uint32 holeColor = lit ? 0 : 7;
 
 	const bool macMode = (_renderMode == Common::kRenderMacintosh);
+	const bool macColors = (macMode && _hasMacColors);
+
+	// 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) {
+		if (macColors) {
+			uint32 fg = packMacColor(_macColors[macIdx].fg);
+			uint32 bg = packMacColor(_macColors[macIdx].bg);
+			int pat = _macColors[macIdx].pattern;
+			const byte *stipple = setupMacPattern(_gfx, pat, fg, bg);
+			wallPolygon(corners, u, v, cnt, fg);
+			if (stipple) _gfx->setStippleData(nullptr);
+		} else if (macMode) {
+			_gfx->setStippleData(kStippleGray);
+			wallPolygon(corners, u, v, cnt, 0);
+			_gfx->setStippleData(nullptr);
+		} else {
+			wallPolygon(corners, u, v, cnt, holeColor);
+		}
+	};
 
 	switch (map[0]) {
 	case 1: // SMHOLEFLR
 	case 3: // SMHOLECEIL
 	{
-		// DOS floor1hole/ceil1hole: hole spans 25%-75% of cell in each dimension
-		// Matching the CCenter proximity trigger zone (64..192 of 256)
 		float u[4] = {0.25f, 0.75f, 0.75f, 0.25f};
 		float v[4] = {0.25f, 0.25f, 0.75f, 0.75f};
-		if (macMode) {
-			// Mac: SuperPoly(c_hole, ...) — c_hole = GRAY stipple, always filled
-			_gfx->setStippleData(kStippleGray);
-			wallPolygon(corners, u, v, 4, 0);
-			_gfx->setStippleData(nullptr);
-		} else {
-			wallPolygon(corners, u, v, 4, holeColor);
-		}
+		drawHolePoly(u, v, 4, 30); // c_hole
 		break;
 	}
 	case 2: // LGHOLEFLR
 	case 4: // LGHOLECEIL
 	{
-		// DOS floor2hole/ceil2hole: full-cell hole
 		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-		if (macMode) {
-			// Mac: SuperPoly(c_hole, ...) — c_hole = GRAY stipple, always filled
-			_gfx->setStippleData(kStippleGray);
-			wallPolygon(corners, u, v, 4, 0);
-			_gfx->setStippleData(nullptr);
-		} else {
-			wallPolygon(corners, u, v, 4, holeColor);
-		}
+		drawHolePoly(u, v, 4, 30); // c_hole
 		break;
 	}
 	case 5: // HOTFOOT
 	{
+		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
 		if (macMode) {
-			// Mac: SuperPoly(c_hotplate, ...) — c_hotplate = GRAY stipple, full cell
-			// Mac draws ONLY the filled polygon, no X-pattern
-			float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-			float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-			_gfx->setStippleData(kStippleGray);
-			wallPolygon(corners, u, v, 4, 0);
-			_gfx->setStippleData(nullptr);
+			drawHolePoly(u, v, 4, 100); // c_hotplate
 		} else {
 			// DOS non-polyfill: X pattern (two diagonals)
 			wallLine(corners, 0.0f, 0.0f, 1.0f, 1.0f, holeColor);
@@ -1714,12 +1863,25 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	
 	float corners[4][3];
 	getWallFace3D(cellX, cellY, direction, corners);
+	const bool macColors = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+
+	// Wall faces are already filled with level-specific color (c_char0+level-1.fg)
+	// by the wall grid in renderCorridor3D(). Features are drawn on top.
+
+	// Helper lambda: Mac color fill for a wall feature polygon
+	auto macFillPoly = [&](const float *u, const float *v, int cnt, int macIdx) {
+		uint32 fg = packMacColor(_macColors[macIdx].fg);
+		uint32 bg = packMacColor(_macColors[macIdx].bg);
+		const byte *stipple = setupMacPattern(_gfx, _macColors[macIdx].pattern, fg, bg);
+		wallPolygon(corners, u, v, cnt, fg);
+		if (stipple) _gfx->setStippleData(nullptr);
+	};
 
 	switch (map[0]) {
 	case kWallFeatureDoor: {
 		// EGA: vDKGRAY outlines; Mac B&W: black outlines + gray fills
 		const bool macMode = (_renderMode == Common::kRenderMacintosh);
-		const uint32 doorColor = macMode ? 0 : 8; // Mac: black, EGA: vDKGRAY
+		const uint32 doorColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
 		bool shipLevel = (_level == 1 || _level == 5 || _level == 6);
 
 		if (shipLevel) {
@@ -1728,10 +1890,13 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 
 			if (macMode) {
 				if (map[1] != 0) {
-					// Closed: fill octagon (c_bulkhead = GRAY stipple)
-					_gfx->setStippleData(kStippleGray);
-					wallPolygon(corners, uSs, vSs, 8, 0);
-					_gfx->setStippleData(nullptr);
+					if (macColors) {
+						macFillPoly(uSs, vSs, 8, 15); // c_bulkhead
+					} else {
+						_gfx->setStippleData(kStippleGray);
+						wallPolygon(corners, uSs, vSs, 8, 0);
+						_gfx->setStippleData(nullptr);
+					}
 				} else {
 					// Open: fill with BLACK (passable opening)
 					_gfx->setWireframe(true, 0);
@@ -1757,10 +1922,13 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 				float ud[4] = {xl, xr, xr, xl};
 				float vd[4] = {yb, yb, yt, yt};
 				if (map[1] != 0) {
-					// Closed: fill (c_door = GRAY stipple)
-					_gfx->setStippleData(kStippleGray);
-					wallPolygon(corners, ud, vd, 4, 0);
-					_gfx->setStippleData(nullptr);
+					if (macColors) {
+						macFillPoly(ud, vd, 4, 16); // c_door
+					} else {
+						_gfx->setStippleData(kStippleGray);
+						wallPolygon(corners, ud, vd, 4, 0);
+						_gfx->setStippleData(nullptr);
+					}
 				} else {
 					// Open: fill with BLACK (passable opening)
 					_gfx->setWireframe(true, 0);
@@ -1782,18 +1950,22 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	}
 	case kWallFeatureWindow: {
 		const bool macMode = (_renderMode == Common::kRenderMacintosh);
-		const uint32 winColor = macMode ? 0 : 8; // Mac: black, EGA: vDKGRAY
+		const uint32 winColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
 		float xl = 0.25f, xr = 0.75f;
 		float yb = 0.25f, yt = 0.75f;
 		float xc = 0.5f, yc = 0.5f;
 
-		// Mac: fill window pane (c_window = DKGRAY stipple)
+		// Mac: fill window pane
 		if (macMode) {
-			_gfx->setStippleData(kStippleDkGray);
 			float uw[4] = {xl, xr, xr, xl};
 			float vw[4] = {yb, yb, yt, yt};
-			wallPolygon(corners, uw, vw, 4, 0);
-			_gfx->setStippleData(nullptr);
+			if (macColors) {
+				macFillPoly(uw, vw, 4, 17); // c_window
+			} else {
+				_gfx->setStippleData(kStippleDkGray);
+				wallPolygon(corners, uw, vw, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
 		}
 
 		wallLine(corners, xl, yb, xl, yt, winColor);
@@ -1807,15 +1979,19 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	case kWallFeatureShelves: {
 		// DOS drawbooks: recessed bookcase with 3D depth.
 		const bool macMode = (_renderMode == Common::kRenderMacintosh);
-		const uint32 shelfColor = macMode ? 0 : 8;
+		const uint32 shelfColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
 
-		// Mac: fill shelves area (c_shelves = LTGRAY stipple)
+		// Mac: fill shelves area
 		if (macMode) {
-			_gfx->setStippleData(kStippleLtGray);
 			float us[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 			float vs[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-			wallPolygon(corners, us, vs, 4, 0);
-			_gfx->setStippleData(nullptr);
+			if (macColors) {
+				macFillPoly(us, vs, 4, 18); // c_shelves
+			} else {
+				_gfx->setStippleData(kStippleLtGray);
+				wallPolygon(corners, us, vs, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
 		}
 		float bx = 0.1875f, bxr = 0.8125f;
 		float by = 0.1875f, byt = 0.8125f;
@@ -1838,15 +2014,19 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	}
 	case kWallFeatureUpStairs: {
 		// DOS: draw_up_stairs — staircase ascending into the wall with perspective
-		const uint32 col = 0; // vBLACK
+		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
 
-		// Mac: fill entire wall face (c_upstairs = GRAY stipple)
+		// Mac: fill entire wall face (c_upstairs)
 		if (_renderMode == Common::kRenderMacintosh) {
-			_gfx->setStippleData(kStippleGray);
 			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-			float vf[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-			wallPolygon(corners, uf, vf, 4, 0);
-			_gfx->setStippleData(nullptr);
+			float vf2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+			if (macColors) {
+				macFillPoly(uf, vf2, 4, 19); // c_upstairs1
+			} else {
+				_gfx->setStippleData(kStippleGray);
+				wallPolygon(corners, uf, vf2, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
 		}
 
 		// Perspective convergence: back of passage at ~1/3 width (1 cell deep)
@@ -1911,15 +2091,19 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	}
 	case kWallFeatureDnStairs: {
 		// DOS: draw_dn_stairs — staircase descending into the wall with perspective
-		const uint32 col = 0; // vBLACK
+		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
 
-		// Mac: fill entire wall face (c_dnstairs = GRAY stipple)
+		// Mac: fill entire wall face (c_dnstairs)
 		if (_renderMode == Common::kRenderMacintosh) {
-			_gfx->setStippleData(kStippleGray);
 			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-			float vf[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-			wallPolygon(corners, uf, vf, 4, 0);
-			_gfx->setStippleData(nullptr);
+			float vf2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+			if (macColors) {
+				macFillPoly(uf, vf2, 4, 21); // c_dnstairs
+			} else {
+				_gfx->setStippleData(kStippleGray);
+				wallPolygon(corners, uf, vf2, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
 		}
 
 		float ul[7], ur[7], vf[7], vc[7];
@@ -1965,15 +2149,19 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	case kWallFeatureGlyph: {
 		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
 		const bool macMode = (_renderMode == Common::kRenderMacintosh);
-		const uint32 glyphColor = macMode ? 0 : 8;
+		const uint32 glyphColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
 
-		// Mac: fill glyph area (c_glyph = GRAY stipple)
+		// Mac: fill glyph area
 		if (macMode) {
-			_gfx->setStippleData(kStippleGray);
 			float ug[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 			float vg[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-			wallPolygon(corners, ug, vg, 4, 0);
-			_gfx->setStippleData(nullptr);
+			if (macColors) {
+				macFillPoly(ug, vg, 4, 22); // c_glyph
+			} else {
+				_gfx->setStippleData(kStippleGray);
+				wallPolygon(corners, ug, vg, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
 		}
 
 		for (int i = 0; i < 7; i++) {
@@ -1984,17 +2172,21 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	}
 	case kWallFeatureElevator: {
 		const bool macMode = (_renderMode == Common::kRenderMacintosh);
-		const uint32 elevColor = macMode ? 0 : 8; // Mac: black, EGA: vDKGRAY
+		const uint32 elevColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
 		float xl = 0.2f, xr = 0.8f;
 		float yb = 0.1f, yt = 0.9f;
 
-		// Mac: fill elevator door (c_elevator = GRAY stipple)
+		// Mac: fill elevator door
 		if (macMode) {
-			_gfx->setStippleData(kStippleGray);
 			float ue[4] = {xl, xr, xr, xl};
 			float ve[4] = {yb, yb, yt, yt};
-			wallPolygon(corners, ue, ve, 4, 0);
-			_gfx->setStippleData(nullptr);
+			if (macColors) {
+				macFillPoly(ue, ve, 4, 23); // c_elevator
+			} else {
+				_gfx->setStippleData(kStippleGray);
+				wallPolygon(corners, ue, ve, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
 		}
 
 		wallLine(corners, xl, yb, xl, yt, elevColor);
@@ -2009,10 +2201,13 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		static const float uT[6] = { 0.0f,    0.0f,    1/6.0f,  5/6.0f,  1.0f,    1.0f };
 		static const float vT[6] = { 0.0f,    0.750f,  0.875f,  0.875f,  0.750f,  0.0f };
 		if (_renderMode == Common::kRenderMacintosh) {
-			// Mac: c_tunnel = GRAY stipple fill + black outline
-			_gfx->setStippleData(kStippleGray);
-			wallPolygon(corners, uT, vT, 6, 0);
-			_gfx->setStippleData(nullptr);
+			if (macColors) {
+				macFillPoly(uT, vT, 6, 24); // c_tunnel
+			} else {
+				_gfx->setStippleData(kStippleGray);
+				wallPolygon(corners, uT, vT, 6, 0);
+				_gfx->setStippleData(nullptr);
+			}
 		} else {
 			wallPolygon(corners, uT, vT, 6, 0); // vBLACK outline
 		}
@@ -2034,14 +2229,18 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 			bool lit = (_corePower[_coreIndex] > 0);
 			_gfx->setWireframe(true, lit ? (macMode ? 255 : 7) : 0);
 		} else {
-			// Mac: fill airlock (c_airlock = GRAY stipple) when closed
+			// Mac: fill airlock when closed
 			if (macMode) {
-				_gfx->setStippleData(kStippleGray);
-				wallPolygon(corners, u, v, 8, 0);
-				_gfx->setStippleData(nullptr);
+				if (macColors) {
+					macFillPoly(u, v, 8, 25); // c_airlock
+				} else {
+					_gfx->setStippleData(kStippleGray);
+					wallPolygon(corners, u, v, 8, 0);
+					_gfx->setStippleData(nullptr);
+				}
 			}
 
-			const uint32 airlockColor = macMode ? 0 : 8; // Mac: black, EGA: vDKGRAY
+			const uint32 airlockColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
 			for (int i = 0; i < 8; i++) {
 				int n = (i + 1) % 8;
 				wallLine(corners, u[i], v[i], u[n], v[n], airlockColor);
@@ -2065,43 +2264,67 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		// 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) {
-			static const byte *stripPatterns[5] = {
-				nullptr, kStippleLtGray, kStippleGray, kStippleDkGray, nullptr
-			};
-			for (int i = 0; i < 4; i++) {
-				int pat = map[i + 1];
-				if (pat > 4)
-					pat = (pat + _displayCount / 6) % 5; // animated cycling
-				float vb = (3 - i) / 4.0f;
-				float vt = (4 - i) / 4.0f;
-				float ub[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-				float vb4[4] = {vb, vb, vt, vt};
-				if (pat == 4) {
-					// BLACK: solid black fill
-					_gfx->setWireframe(true, 0);
-					wallPolygon(corners, ub, vb4, 4, 0);
-					_gfx->setWireframe(true, 255);
-				} else if (pat == 0) {
-					// WHITE: no fill needed (wall background is white)
-				} else {
-					_gfx->setStippleData(stripPatterns[pat]);
-					wallPolygon(corners, ub, vb4, 4, 0);
-					_gfx->setStippleData(nullptr);
+			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.
+				for (int i = 0; i < 4; i++) {
+					int val = map[i + 1];
+					if (val > 4)
+						val = (val + _displayCount / 6) % 5;
+					float vb = (3 - i) / 4.0f;
+					float vt = (4 - i) / 4.0f;
+					float ub[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+					float vb4[4] = {vb, vb, vt, vt};
+					if (val == 4) {
+						// BLACK: solid black fill
+						_gfx->setWireframe(true, (int)(uint32)0xFF000000);
+						wallPolygon(corners, ub, vb4, 4, 0xFF000000);
+						_gfx->setWireframe(true, (int)packMacColor(_macColors[8 + _level - 1].fg));
+					} else {
+						macFillPoly(ub, vb4, 4, 26 + val); // c_color0 + val
+					}
+				}
+			} else {
+				static const byte *stripPatterns[5] = {
+					nullptr, kStippleLtGray, kStippleGray, kStippleDkGray, nullptr
+				};
+				for (int i = 0; i < 4; i++) {
+					int pat = map[i + 1];
+					if (pat > 4)
+						pat = (pat + _displayCount / 6) % 5; // animated cycling
+					float vb = (3 - i) / 4.0f;
+					float vt = (4 - i) / 4.0f;
+					float ub[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+					float vb4[4] = {vb, vb, vt, vt};
+					if (pat == 4) {
+						_gfx->setWireframe(true, 0);
+						wallPolygon(corners, ub, vb4, 4, 0);
+						_gfx->setWireframe(true, 255);
+					} else if (pat == 0) {
+						// WHITE: no fill needed (wall background is white)
+					} else {
+						_gfx->setStippleData(stripPatterns[pat]);
+						wallPolygon(corners, ub, vb4, 4, 0);
+						_gfx->setStippleData(nullptr);
+					}
 				}
 			}
 		}
 
-		// EGA / both modes: colored lines at band boundaries.
+		// EGA / Mac B&W: colored lines at band boundaries.
 		// DOS non-polyfill draws 3 lines; we animate line colors for bands > 4.
-		for (int i = 1; i <= 3; i++) {
-			int val = map[i];
-			if (val > 4)
-				val = (val + _displayCount / 6) % 5; // animated cycling
-			uint32 c = 120 + val * 20;
-			if (c == 120 && val == 0 && !map[1] && !map[2] && !map[3] && !map[4])
-				c = 100 + (_level * 15);
-			float v = (float)i / 4.0f;
-			wallLine(corners, 0.0f, v, 1.0f, v, c);
+		// Mac color mode uses macFillPoly for band fills instead.
+		if (!macColors) {
+			for (int i = 1; i <= 3; i++) {
+				int val = map[i];
+				if (val > 4)
+					val = (val + _displayCount / 6) % 5; // animated cycling
+				uint32 c = 120 + val * 20;
+				if (c == 120 && val == 0 && !map[1] && !map[2] && !map[3] && !map[4])
+					c = 100 + (_level * 15);
+				float v = (float)i / 4.0f;
+				wallLine(corners, 0.0f, v, 1.0f, v, c);
+			}
 		}
 		break;
 	}
@@ -2136,32 +2359,53 @@ void ColonyEngine::renderCorridor3D() {
 	bool lit = (_corePower[_coreIndex] > 0);
 	bool macMode = (_renderMode == Common::kRenderMacintosh);
 
-	// Mac B&W: walls are pure white (c_dwall=WHITE); EGA: light gray (7)
-	uint32 wallFill = lit ? (macMode ? 255 : 7) : 0;
-	uint32 wallLine = lit ? 0 : (macMode ? 255 : 7);
-
-	// Walls always use wireframe with fill (opaque walls).
-	_gfx->setWireframe(true, wallFill);
+	uint32 wallFill, wallLine, floorColor, ceilColor;
+
+	if (macMode && _hasMacColors) {
+		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.
+			// c_char0 = index 8 in Color256.
+			wallFill = packMacColor(_macColors[8 + _level - 1].fg);
+			wallLine = 0xFF000000; // black outlines
+			floorColor = packMacColor(_macColors[7].fg);   // c_lwall fg (darker gray)
+			ceilColor = packMacColor(_macColors[7].bg);    // c_lwall bg (light gray)
+		} else {
+			wallFill = packMacColor(_macColors[6].bg);     // c_dwall bg
+			wallLine = packMacColor(_macColors[6].fg);     // c_dwall fg
+			floorColor = wallFill;
+			ceilColor = wallFill;
+		}
+	} else {
+		// Mac B&W: walls are pure white (c_dwall=WHITE); EGA: light gray (7)
+		wallFill = lit ? (macMode ? 255 : 7) : 0;
+		wallLine = lit ? 0 : (macMode ? 255 : 7);
+		floorColor = macMode ? (lit ? 0 : 255) : wallLine;
+		ceilColor  = macMode ? (lit ? 255 : 0) : wallLine;
+	}
 
 	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
-	_gfx->clear(wallFill);
+	_gfx->clear(ceilColor);
 
 	uint32 wallColor = wallLine;
-	// Mac: floor = black (c_lwall foreground), ceiling = white (c_lwall background)
-	// EGA: both use wallLine color
-	uint32 floorColor = macMode ? (lit ? 0 : 255) : wallLine;
-	uint32 ceilColor  = macMode ? (lit ? 255 : 0) : wallLine;
 
-	// Draw large floor and ceiling quads
+	// Draw large floor and ceiling quads.
+	// Mac Display(): EraseRect fills ceiling with c_lwall.bg and floor with c_lwall.fg.
+	// Set wireframe fill to each surface's own color so they aren't all wallFill.
+	_gfx->setWireframe(true, (int)floorColor);
 	_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.1f,
 	                100000.0f, -100000.0f, -160.1f,
 	                100000.0f, 100000.0f, -160.1f,
 	                -100000.0f, 100000.0f, -160.1f, floorColor);
 
+	_gfx->setWireframe(true, (int)ceilColor);
 	_gfx->draw3DQuad(-100000.0f, -100000.0f, 160.1f,
 	                100000.0f, -100000.0f, 160.1f,
 	                100000.0f, 100000.0f, 160.1f,
 	                -100000.0f, 100000.0f, 160.1f, ceilColor);
+
+	// Walls always use wireframe with fill (opaque walls).
+	_gfx->setWireframe(true, wallFill);
  
 	// Draw ceiling grid (Cuadricule) - Historically only on ceiling
 	for (int i = 0; i <= 32; i++) {
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index 6037c502291..f23340cab34 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -61,6 +61,7 @@ public:
 	virtual void copyToScreen() = 0;
 	virtual void setWireframe(bool enable, int fillColor = -1) = 0;
 	virtual void setStippleData(const byte *data) {}
+	virtual void setMacColors(uint32 fg, uint32 bg) {}
 	virtual void computeScreenViewport() = 0;
 
 	// Convenience color accessors
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 0813628f353..22ea1d92282 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -66,6 +66,10 @@ public:
 	void setStippleData(const byte *data) override {
 		_stippleData = data;
 	}
+	void setMacColors(uint32 fg, uint32 bg) override {
+		_stippleFgColor = fg;
+		_stippleBgColor = bg;
+	}
 	void computeScreenViewport() override;
 
 private:
@@ -78,6 +82,8 @@ private:
 	bool _wireframe;
 	int _wireframeFillColor; // -1 = no fill (outline only)
 	const byte *_stippleData; // GL_POLYGON_STIPPLE pattern (128 bytes), null = disabled
+	uint32 _stippleFgColor;
+	uint32 _stippleBgColor;
 	Common::Rect _screenViewport;
 };
 
@@ -85,6 +91,8 @@ OpenGLRenderer::OpenGLRenderer(OSystem *system, int width, int height) : _system
 	_wireframe = true;
 	_wireframeFillColor = 0;
 	_stippleData = nullptr;
+	_stippleFgColor = 0;
+	_stippleBgColor = 255;
 	memset(_palette, 0, sizeof(_palette));
 	
 	// Default to white for initial colors if setPalette isn't called yet
@@ -120,13 +128,27 @@ void OpenGLRenderer::setPalette(const byte *palette, uint start, uint count) {
 }
 
 void OpenGLRenderer::useColor(uint32 color) {
-	uint32 index = color & 0xFF;
-	glColor3ub(_palette[index * 3], _palette[index * 3 + 1], _palette[index * 3 + 2]);
+	if (color & 0xFF000000) {
+		glColor3ub((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF);
+	} else {
+		uint32 index = color & 0xFF;
+		glColor3ub(_palette[index * 3], _palette[index * 3 + 1], _palette[index * 3 + 2]);
+	}
 }
 
 void OpenGLRenderer::clear(uint32 color) {
-	uint32 index = color & 0xFF;
-	glClearColor(_palette[index * 3] / 255.0f, _palette[index * 3 + 1] / 255.0f, _palette[index * 3 + 2] / 255.0f, 1.0f);
+	float r, g, b;
+	if (color & 0xFF000000) {
+		r = ((color >> 16) & 0xFF) / 255.0f;
+		g = ((color >> 8) & 0xFF) / 255.0f;
+		b = (color & 0xFF) / 255.0f;
+	} else {
+		uint32 index = color & 0xFF;
+		r = _palette[index * 3] / 255.0f;
+		g = _palette[index * 3 + 1] / 255.0f;
+		b = _palette[index * 3 + 2] / 255.0f;
+	}
+	glClearColor(r, g, b, 1.0f);
 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 }
 
@@ -246,20 +268,20 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 	float fy2 = y2 * 256.0f;
 
 	if (_wireframe) {
-		if (_wireframeFillColor >= 0 || _stippleData) {
+		if (_wireframeFillColor != -1 || _stippleData) {
 			glEnable(GL_POLYGON_OFFSET_FILL);
 			glPolygonOffset(1.1f, 4.0f);
 
 			if (_stippleData) {
 				// Two-pass stipple fill (Mac B&W dither pattern)
-				useColor(255); // White background
+				useColor(_stippleBgColor); // White background
 				glBegin(GL_QUADS);
 				glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
 				glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
 				glEnd();
 				glEnable(GL_POLYGON_STIPPLE);
 				glPolygonStipple(_stippleData);
-				useColor(0); // Black foreground through stipple mask
+				useColor(_stippleFgColor); // Black foreground through stipple mask
 				glBegin(GL_QUADS);
 				glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
 				glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
@@ -289,14 +311,14 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 		glEnable(GL_POLYGON_OFFSET_FILL);
 		glPolygonOffset(1.1f, 4.0f);
 		if (_stippleData) {
-			useColor(255);
+			useColor(_stippleBgColor);
 			glBegin(GL_QUADS);
 			glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
 			glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
 			glEnd();
 			glEnable(GL_POLYGON_STIPPLE);
 			glPolygonStipple(_stippleData);
-			useColor(0);
+			useColor(_stippleFgColor);
 			glBegin(GL_QUADS);
 			glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
 			glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
@@ -315,19 +337,19 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
  
 void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, uint32 color) {
 	if (_wireframe) {
-		if (_wireframeFillColor >= 0 || _stippleData) {
+		if (_wireframeFillColor != -1 || _stippleData) {
 			glEnable(GL_POLYGON_OFFSET_FILL);
 			glPolygonOffset(1.1f, 4.0f);
 
 			if (_stippleData) {
-				useColor(255);
+				useColor(_stippleBgColor);
 				glBegin(GL_QUADS);
 				glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2);
 				glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4);
 				glEnd();
 				glEnable(GL_POLYGON_STIPPLE);
 				glPolygonStipple(_stippleData);
-				useColor(0);
+				useColor(_stippleFgColor);
 				glBegin(GL_QUADS);
 				glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2);
 				glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4);
@@ -356,14 +378,14 @@ void OpenGLRenderer::draw3DQuad(float x1, float y1, float z1, float x2, float y2
 		glEnable(GL_POLYGON_OFFSET_FILL);
 		glPolygonOffset(1.1f, 4.0f);
 		if (_stippleData) {
-			useColor(255);
+			useColor(_stippleBgColor);
 			glBegin(GL_QUADS);
 			glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2);
 			glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4);
 			glEnd();
 			glEnable(GL_POLYGON_STIPPLE);
 			glPolygonStipple(_stippleData);
-			useColor(0);
+			useColor(_stippleFgColor);
 			glBegin(GL_QUADS);
 			glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2);
 			glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4);
@@ -385,19 +407,19 @@ void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *
 		return;
 
 	if (_wireframe) {
-		if (_wireframeFillColor >= 0 || _stippleData) {
+		if (_wireframeFillColor != -1 || _stippleData) {
 			glEnable(GL_POLYGON_OFFSET_FILL);
 			glPolygonOffset(1.1f, 4.0f);
 
 			if (_stippleData) {
-				useColor(255);
+				useColor(_stippleBgColor);
 				glBegin(GL_POLYGON);
 				for (int i = 0; i < count; i++)
 					glVertex3f(x[i], y[i], z[i]);
 				glEnd();
 				glEnable(GL_POLYGON_STIPPLE);
 				glPolygonStipple(_stippleData);
-				useColor(0);
+				useColor(_stippleFgColor);
 				glBegin(GL_POLYGON);
 				for (int i = 0; i < count; i++)
 					glVertex3f(x[i], y[i], z[i]);
@@ -424,14 +446,14 @@ void OpenGLRenderer::draw3DPolygon(const float *x, const float *y, const float *
 		glEnable(GL_POLYGON_OFFSET_FILL);
 		glPolygonOffset(1.1f, 4.0f);
 		if (_stippleData) {
-			useColor(255);
+			useColor(_stippleBgColor);
 			glBegin(GL_POLYGON);
 			for (int i = 0; i < count; i++)
 				glVertex3f(x[i], y[i], z[i]);
 			glEnd();
 			glEnable(GL_POLYGON_STIPPLE);
 			glPolygonStipple(_stippleData);
-			useColor(0);
+			useColor(_stippleFgColor);
 			glBegin(GL_POLYGON);
 			for (int i = 0; i < count; i++)
 				glVertex3f(x[i], y[i], z[i]);


Commit: 6ce1a95141d0d7533d516f9c29e183c996caabc6
    https://github.com/scummvm/scummvm/commit/6ce1a95141d0d7533d516f9c29e183c996caabc6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:24+02:00

Commit Message:
GRAPHICS: MACGUI: allow to skip g_system->copyRectToScreen (for 3D game backends)

Changed paths:
    graphics/macgui/macmenu.cpp
    graphics/macgui/macpopupmenu.cpp
    graphics/macgui/macwindowmanager.h


diff --git a/graphics/macgui/macmenu.cpp b/graphics/macgui/macmenu.cpp
index b441d2dc60f..5d2e5ff5468 100644
--- a/graphics/macgui/macmenu.cpp
+++ b/graphics/macgui/macmenu.cpp
@@ -1119,9 +1119,10 @@ bool MacMenu::draw(ManagedSurface *g, bool forceRedraw) {
 		}
 	}
 
-	if ((_wm->_mode & kWMModalMenuMode) || !_wm->_screen)
-		g_system->copyRectToScreen(_screen.getBasePtr(_bbox.left, _bbox.top), _screen.pitch, _bbox.left, _bbox.top, _bbox.width(), _bbox.height());
-
+	if (!(_wm->_mode & kWMModeNoSystemRedraw)) {
+		if ((_wm->_mode & kWMModalMenuMode) || !_wm->_screen)
+			g_system->copyRectToScreen(_screen.getBasePtr(_bbox.left, _bbox.top), _screen.pitch, _bbox.left, _bbox.top, _bbox.width(), _bbox.height());
+	}
 
 	for (uint i = 0; i < _menustack.size(); i++) {
 		renderSubmenu(_menustack[i], (i == _menustack.size() - 1));
@@ -1130,7 +1131,7 @@ bool MacMenu::draw(ManagedSurface *g, bool forceRedraw) {
 	if (g)
 		g->transBlitFrom(_screen, _wm->_colorGreen);
 
-	if (!(_wm->_mode & kWMModalMenuMode) && g)
+	if (!(_wm->_mode & kWMModeNoSystemRedraw) && !(_wm->_mode & kWMModalMenuMode) && g)
 		g_system->copyRectToScreen(g->getPixels(), g->pitch, 0, 0, g->w, g->h);
 
 	return true;
diff --git a/graphics/macgui/macpopupmenu.cpp b/graphics/macgui/macpopupmenu.cpp
index ba3461c301f..0d95697a8b6 100644
--- a/graphics/macgui/macpopupmenu.cpp
+++ b/graphics/macgui/macpopupmenu.cpp
@@ -49,7 +49,7 @@ bool MacPopUp::draw(ManagedSurface *g, bool forceRedraw) {
 	if (g)
 		g->transBlitFrom(_screen, _wm->_colorGreen);
 
-	if (!(_wm->_mode & kWMModalMenuMode) && g)
+	if (!(_wm->_mode & kWMModeNoSystemRedraw) && !(_wm->_mode & kWMModalMenuMode) && g)
 		g_system->copyRectToScreen(g->getPixels(), g->pitch, 0, 0, g->w, g->h);
 
 	return true;
diff --git a/graphics/macgui/macwindowmanager.h b/graphics/macgui/macwindowmanager.h
index 239f7ed4669..323fb006796 100644
--- a/graphics/macgui/macwindowmanager.h
+++ b/graphics/macgui/macwindowmanager.h
@@ -94,6 +94,7 @@ enum {
 	kWMModeNoCursorOverride     = (1 << 12),
 	kWMModeForceMacBorder       = (1 << 13),
 	kWMModeForceMacFonts        = (1 << 14), // Enforce Mac fonts even when there are viable TTF substitutions
+	kWMModeNoSystemRedraw       = (1 << 15), // Skip g_system->copyRectToScreen (for 3D game backends)
 };
 
 }


Commit: c86598222dd176425d98d7a7d50cc8c4700a56b3
    https://github.com/scummvm/scummvm/commit/c86598222dd176425d98d7a7d50cc8c4700a56b3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:24+02:00

Commit Message:
COLONY: initial mac gui support

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index cbc195a2419..9fa3e0568e6 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -54,6 +54,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_centerX = _width / 2;
 	_centerY = _height / 2;
 	_mouseSensitivity = 1;
+	_mouseLocked = false;
 	_showDashBoard = true;
 	_crosshair = true;
 	_insight = false;
@@ -77,6 +78,10 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	
 	_wireframe = (_renderMode != Common::kRenderMacintosh);
 	_speedShift = 2; // DOS default: speedshift=1, but 2 feels better with our frame rate
+	_wm = nullptr;
+	_macMenu = nullptr;
+	_menuSurface = nullptr;
+	_menuBarHeight = 0;
 	
 	memset(_wall, 0, sizeof(_wall));
 	memset(_mapData, 0, sizeof(_mapData));
@@ -151,6 +156,8 @@ ColonyEngine::~ColonyEngine() {
 	delete _gfx;
 	delete _sound;
 	delete _resMan;
+	delete _menuSurface;
+	delete _wm;
 }
 
 
@@ -523,6 +530,122 @@ void ColonyEngine::loadMacColors() {
 	debug("Loaded %d Mac colors", cnum);
 }
 
+void ColonyEngine::menuCommandsCallback(int action, Common::String &text, void *data) {
+	ColonyEngine *engine = (ColonyEngine *)data;
+	engine->handleMenuAction(action);
+}
+
+void ColonyEngine::handleMenuAction(int action) {
+	switch (action) {
+	case kMenuActionAbout:
+		inform("The Colony\nCopyright 1988\nDavid A. Smith", true);
+		break;
+	case kMenuActionNew:
+		loadMap(1);
+		break;
+	case kMenuActionOpen:
+		_system->lockMouse(false);
+		loadGameDialog();
+		_system->lockMouse(true);
+		_system->warpMouse(_centerX, _centerY);
+		_system->getEventManager()->purgeMouseEvents();
+		break;
+	case kMenuActionSave:
+	case kMenuActionSaveAs:
+		_system->lockMouse(false);
+		saveGameDialog();
+		_system->lockMouse(true);
+		_system->warpMouse(_centerX, _centerY);
+		_system->getEventManager()->purgeMouseEvents();
+		break;
+	case kMenuActionQuit:
+		quitGame();
+		break;
+	case kMenuActionSound:
+		// Sound toggle (TODO: implement sound on/off state)
+		break;
+	case kMenuActionCrosshair:
+		_crosshair = !_crosshair;
+		break;
+	case kMenuActionPolyFill:
+		_wireframe = !_wireframe;
+		break;
+	case kMenuActionCursorShoot:
+		// Toggle cursor-based shooting (not yet implemented)
+		break;
+	default:
+		break;
+	}
+}
+
+void ColonyEngine::initMacMenus() {
+	if (_renderMode != Common::kRenderMacintosh) {
+		_menuBarHeight = 0;
+		return;
+	}
+
+	// Create RGBA surface for the MacWindowManager to render into.
+	Graphics::PixelFormat rgba(4, 8, 8, 8, 8, 24, 16, 8, 0);
+	_menuSurface = new Graphics::ManagedSurface(_width, _height, rgba);
+
+	_wm = new Graphics::MacWindowManager(Graphics::kWMModeNoDesktop | Graphics::kWMNoScummVMWallpaper | Graphics::kWMMode32bpp | Graphics::kWMModeNoSystemRedraw);
+
+	// Override WM color values for 32bpp RGBA rendering.
+	// The defaults are palette indices (0-6) which are meaningless in 32bpp mode.
+	_wm->_colorBlack  = rgba.ARGBToColor(255,   0,   0,   0);
+	_wm->_colorGray80 = rgba.ARGBToColor(255, 128, 128, 128);
+	_wm->_colorGray88 = rgba.ARGBToColor(255, 136, 136, 136);
+	_wm->_colorGrayEE = rgba.ARGBToColor(255, 238, 238, 238);
+	_wm->_colorWhite  = rgba.ARGBToColor(255, 255, 255, 255);
+	_wm->_colorGreen  = rgba.ARGBToColor(  0,   0, 255,   0); // transparent key
+	_wm->_colorGreen2 = rgba.ARGBToColor(  0,   0, 207,   0); // transparent key 2
+
+	_wm->setScreen(_menuSurface);
+
+	_macMenu = _wm->addMenu();
+	_macMenu->setCommandsCallback(menuCommandsCallback, this);
+
+	// Build menus matching original Mac Colony (inits.c lines 43-53, gmain.c DoCommand).
+	// addStaticMenus() auto-adds the Apple menu at index 0, so:
+	//   index 0 = Apple, 1 = File, 2 = Edit, 3 = Options
+	// NOTE: menunum=0 is the loop terminator, so Apple submenu items
+	// must be added manually after addStaticMenus() (see WAGE pattern).
+	static const Graphics::MacMenuData menuItems[] = {
+		{-1, "File",            0, 0, true},
+		{-1, "Edit",            0, 0, true},
+		{-1, "Options",         0, 0, true},
+		// File submenu (index 1)
+		{1, "New Game",                     kMenuActionNew, 'N', true},
+		{1, "Open Game...",                 kMenuActionOpen, 'O', true},
+		{1, "Save Game",                    kMenuActionSave, 'S', true},
+		{1, "Save As...",                   kMenuActionSaveAs, 0, true},
+		{1, nullptr,                        0, 0, false},   // separator
+		{1, "Quit",                         kMenuActionQuit, 'Q', true},
+		// Edit submenu (index 2, disabled — original Mac had these but non-functional)
+		{2, "Undo",                         0, 'Z', false},
+		{2, nullptr,                        0, 0, false},
+		{2, "Cut",                          0, 'X', false},
+		{2, "Copy",                         0, 'C', false},
+		{2, "Paste",                        0, 'V', false},
+		// Options submenu (index 3)
+		{3, "Sound",                        kMenuActionSound, 0, true},
+		{3, "Crosshair",                    kMenuActionCrosshair, 0, true},
+		{3, "Polygon Fill",                 kMenuActionPolyFill, 0, true},
+		{3, "Cursor Shoot",                 kMenuActionCursorShoot, 0, true},
+		// Terminator
+		{0, nullptr,                        0, 0, false}
+	};
+	_macMenu->addStaticMenus(menuItems);
+
+	// Add Apple submenu item manually (menunum=0 can't go through addStaticMenus)
+	_macMenu->addSubMenu(nullptr, 0);
+	_macMenu->addMenuItem(_macMenu->getSubmenu(nullptr, 0), "About The Colony", kMenuActionAbout);
+
+	_macMenu->calcDimensions();
+
+	_menuBarHeight = 20;
+}
+
 void ColonyEngine::initTrig() {
 	// Compute standard sin/cos lookup tables (256 steps = full circle, scaled by 128)
 	for (int i = 0; i < 256; i++) {
@@ -578,12 +701,16 @@ Common::Error ColonyEngine::run() {
 
 	_gfx->setPalette(pal, 0, 256);
 
+	initMacMenus();
+	updateViewportLayout(); // Recalculate for menu bar height
+
 	// Frame limiter: target 60fps, like Freescape engine
 	_frameLimiter = new Graphics::FrameLimiter(_system, 60);
 
 	playIntro();
 
 	loadMap(1); // Try to load the first map
+	_mouseLocked = true;
 	_system->lockMouse(true);
 	_system->warpMouse(_centerX, _centerY);
 
@@ -593,6 +720,27 @@ Common::Error ColonyEngine::run() {
 		_frameLimiter->startFrame();
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
+			// Let MacWindowManager handle menu events first
+			if (_wm) {
+				bool wasMenuActive = _wm->isMenuActive();
+				if (_wm->processEvent(event)) {
+					// WM consumed the event (menu interaction)
+					if (!wasMenuActive && _wm->isMenuActive()) {
+						_system->lockMouse(false);
+					}
+					continue;
+				}
+				if (wasMenuActive && !_wm->isMenuActive()) {
+					_system->lockMouse(_mouseLocked);
+					if (_mouseLocked) {
+						_system->warpMouse(_centerX, _centerY);
+						_system->getEventManager()->purgeMouseEvents();
+						mouseDX = mouseDY = 0;
+						mouseMoved = false;
+					}
+				}
+			}
+
 				if (event.type == Common::EVENT_KEYDOWN) {
 					debug("Key down: %d", event.kbd.keycode);
 					const bool allowInteraction = (event.kbd.flags & Common::KBD_CTRL) == 0;
@@ -676,26 +824,39 @@ Common::Error ColonyEngine::run() {
 						_system->lockMouse(false);
 						openMainMenuDialog();
 						_gfx->computeScreenViewport();
-						_system->lockMouse(true);
-						_system->warpMouse(_centerX, _centerY);
-						_system->getEventManager()->purgeMouseEvents();
+						_system->lockMouse(_mouseLocked);
+						if (_mouseLocked) {
+							_system->warpMouse(_centerX, _centerY);
+							_system->getEventManager()->purgeMouseEvents();
+						}
 						break;
-					// Space: shoot or exit forklift (DOS: ScanCode 57)
+					// Space: toggle mouselook / free cursor
 					case Common::KEYCODE_SPACE:
 						if (_fl == 2)
 							dropCarriedObject();
 						else if (_fl == 1)
 							exitForklift();
-						// else: shoot (TODO: implement shooting)
+						else {
+							_mouseLocked = !_mouseLocked;
+							_system->lockMouse(_mouseLocked);
+							if (_mouseLocked) {
+								_system->warpMouse(_centerX, _centerY);
+								_system->getEventManager()->purgeMouseEvents();
+								mouseDX = mouseDY = 0;
+								mouseMoved = false;
+							}
+						}
 						break;
 					// Escape: also opens ScummVM menu
 					case Common::KEYCODE_ESCAPE:
 						_system->lockMouse(false);
 						openMainMenuDialog();
 						_gfx->computeScreenViewport();
-						_system->lockMouse(true);
-						_system->warpMouse(_centerX, _centerY);
-						_system->getEventManager()->purgeMouseEvents();
+						_system->lockMouse(_mouseLocked);
+						if (_mouseLocked) {
+							_system->warpMouse(_centerX, _centerY);
+							_system->getEventManager()->purgeMouseEvents();
+						}
 						break;
 					default:
 						break;
@@ -710,7 +871,7 @@ Common::Error ColonyEngine::run() {
 			}
 		}
 
-		if (mouseMoved) {
+		if (mouseMoved && _mouseLocked) {
 			if (mouseDX != 0) {
 				_me.look = (uint8)((int)_me.look - (mouseDX * _mouseSensitivity));
 			}
@@ -732,6 +893,14 @@ Common::Error ColonyEngine::run() {
 		drawCrosshair();
 		checkCenter();
 		
+		// Draw Mac menu bar overlay (render directly to our surface, skip WM's
+		// g_system->copyRectToScreen which conflicts with the OpenGL backend)
+		if (_macMenu && _menuSurface) {
+			_menuSurface->fillRect(Common::Rect(_width, _height), _menuSurface->format.ARGBToColor(0, 0, 0, 0));
+			_macMenu->draw(_menuSurface, true);
+			_gfx->drawSurface(&_menuSurface->rawSurface(), 0, 0);
+		}
+
 		_displayCount++; // Mac: count++ after Display()
 		_frameLimiter->delayBeforeSwap();
 		_gfx->copyToScreen();
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 618d5a275ae..e3579ef569c 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -31,6 +31,8 @@
 #include "common/rendermode.h"
 #include "colony/gfx.h"
 #include "colony/sound.h"
+#include "graphics/macgui/macwindowmanager.h"
+#include "graphics/macgui/macmenu.h"
 
 
 namespace Colony {
@@ -176,6 +178,20 @@ enum ObjColor {
 	kColorShadow = 74
 };
 
+// Mac menu action IDs (matching original Mac Colony menu structure)
+enum MenuAction {
+	kMenuActionAbout = 1,
+	kMenuActionNew,
+	kMenuActionOpen,
+	kMenuActionSave,
+	kMenuActionSaveAs,
+	kMenuActionQuit,
+	kMenuActionSound,
+	kMenuActionCrosshair,
+	kMenuActionPolyFill,
+	kMenuActionCursorShoot
+};
+
 static const int kBaseObject = 20;
 static const int kMeNum = 101;
 
@@ -339,6 +355,7 @@ private:
 	int _centerX, _centerY;
 	int _width, _height;
 	int _mouseSensitivity;
+	bool _mouseLocked;
 	bool _showDashBoard;
 	bool _crosshair;
 	bool _insight;
@@ -378,6 +395,15 @@ private:
 	MacColor _macColors[145];
 	bool _hasMacColors;
 
+	// Mac menu bar (MacWindowManager overlay)
+	Graphics::MacWindowManager *_wm;
+	Graphics::MacMenu *_macMenu;
+	Graphics::ManagedSurface *_menuSurface;
+	int _menuBarHeight;
+	void initMacMenus();
+	void handleMenuAction(int action);
+	static void menuCommandsCallback(int action, Common::String &text, void *data);
+
 	int _frntxWall, _frntyWall;
 	int _sidexWall, _sideyWall;
 	int _frntx, _frnty;
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index f23340cab34..b8629c9fa22 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -64,6 +64,9 @@ public:
 	virtual void setMacColors(uint32 fg, uint32 bg) {}
 	virtual void computeScreenViewport() = 0;
 
+	// Overlay a RGBA software surface onto the GL framebuffer (for Mac menu bar).
+	virtual void drawSurface(const Graphics::Surface *surf, int x, int y) {}
+
 	// Convenience color accessors
 	uint32 white() const { return 255; }
 	uint32 black() const { return 0; }
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 22ea1d92282..0310d20be2b 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -71,9 +71,11 @@ public:
 		_stippleBgColor = bg;
 	}
 	void computeScreenViewport() override;
+	void drawSurface(const Graphics::Surface *surf, int x, int y) override;
 
 private:
 	void useColor(uint32 color);
+	GLuint _overlayTexId;
 
 	OSystem *_system;
 	int _width;
@@ -113,12 +115,15 @@ OpenGLRenderer::OpenGLRenderer(OSystem *system, int width, int height) : _system
 	glLoadIdentity();
 
 	computeScreenViewport();
-	
+
 	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 	glClear(GL_COLOR_BUFFER_BIT);
+
+	glGenTextures(1, &_overlayTexId);
 }
 
 OpenGLRenderer::~OpenGLRenderer() {
+	glDeleteTextures(1, &_overlayTexId);
 }
 
 void OpenGLRenderer::setPalette(const byte *palette, uint start, uint count) {
@@ -600,6 +605,77 @@ void OpenGLRenderer::drawPolygon(const int *x, const int *y, int count, uint32 c
 	glEnd();
 }
 
+void OpenGLRenderer::drawSurface(const Graphics::Surface *surf, int x, int y) {
+	if (!surf || surf->w <= 0 || surf->h <= 0)
+		return;
+
+	// The surface is expected to be RGBA8888 (4 bytes per pixel).
+	if (surf->format.bytesPerPixel != 4)
+		return;
+
+	// Save GL state
+	glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);
+
+	// Set up 2D ortho matching the game's coordinate system
+	glMatrixMode(GL_PROJECTION);
+	glPushMatrix();
+	glLoadIdentity();
+
+	float scaleX = (float)_screenViewport.width() / _width;
+	float scaleY = (float)_screenViewport.height() / _height;
+	glOrtho(0, _screenViewport.width(), _screenViewport.height(), 0, -1, 1);
+
+	glMatrixMode(GL_MODELVIEW);
+	glPushMatrix();
+	glLoadIdentity();
+
+	glViewport(_screenViewport.left, _system->getHeight() - _screenViewport.bottom,
+	           _screenViewport.width(), _screenViewport.height());
+
+	glDisable(GL_DEPTH_TEST);
+	glEnable(GL_TEXTURE_2D);
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+	// Upload surface to texture
+	glBindTexture(GL_TEXTURE_2D, _overlayTexId);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0,
+	             GL_RGBA, GL_UNSIGNED_BYTE, surf->getPixels());
+
+	// Draw textured quad covering the specified region
+	float dx = x * scaleX;
+	float dy = y * scaleY;
+	float dw = surf->w * scaleX;
+	float dh = surf->h * scaleY;
+
+	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+	glBegin(GL_QUADS);
+	glTexCoord2f(0.0f, 0.0f); glVertex2f(dx, dy);
+	glTexCoord2f(1.0f, 0.0f); glVertex2f(dx + dw, dy);
+	glTexCoord2f(1.0f, 1.0f); glVertex2f(dx + dw, dy + dh);
+	glTexCoord2f(0.0f, 1.0f); glVertex2f(dx, dy + dh);
+	glEnd();
+
+	// Restore GL state
+	glBindTexture(GL_TEXTURE_2D, 0);
+	glDisable(GL_TEXTURE_2D);
+
+	glMatrixMode(GL_PROJECTION);
+	glPopMatrix();
+	glMatrixMode(GL_MODELVIEW);
+	glPopMatrix();
+
+	glPopAttrib();
+
+	// Restore the game's 2D ortho viewport
+	glViewport(_screenViewport.left, _system->getHeight() - _screenViewport.bottom,
+	           _screenViewport.width(), _screenViewport.height());
+}
+
 void OpenGLRenderer::copyToScreen() {
 	glFlush();
 	_system->updateScreen();
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index f854246eaf2..9cd110243dc 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -48,12 +48,14 @@ void ColonyEngine::updateViewportLayout() {
 			dashWidth = 0;
 	}
 
-	_screenR = makeSafeRect(dashWidth, 0, _width, _height);
+	const int menuTop = _menuBarHeight; // 0 for DOS/EGA, 20 for Mac
+
+	_screenR = makeSafeRect(dashWidth, menuTop, _width, _height);
 	_clip = _screenR;
 	_centerX = (_screenR.left + _screenR.right) >> 1;
 	_centerY = (_screenR.top + _screenR.bottom) >> 1;
 
-	_dashBoardRect = makeSafeRect(0, 0, dashWidth, _height);
+	_dashBoardRect = makeSafeRect(0, menuTop, dashWidth, _height);
 	if (dashWidth == 0) {
 		_compassRect = Common::Rect(0, 0, 0, 0);
 		_headsUpRect = Common::Rect(0, 0, 0, 0);
@@ -62,19 +64,20 @@ void ColonyEngine::updateViewportLayout() {
 	}
 
 	const int pad = 2;
+	const int topPad = menuTop + pad; // Dashboard sub-panels start below menu bar
 	const int unit = MAX(8, (dashWidth - (pad * 2)) / 4);
 	const int blockLeft = pad;
 	const int blockRight = MIN(dashWidth - pad, blockLeft + unit * 4);
 
 	const int compassBottom = _height - MAX(2, unit / 4);
-	const int compassTop = MAX(pad, compassBottom - unit * 4);
+	const int compassTop = MAX(topPad, compassBottom - unit * 4);
 	_compassRect = makeSafeRect(blockLeft, compassTop, blockRight, compassBottom);
 
 	const int headsUpBottom = _compassRect.top - MAX(2, unit / 4) - 4;
 	const int headsUpTop = headsUpBottom - unit * 4;
-	_headsUpRect = makeSafeRect(blockLeft, MAX(pad, headsUpTop), blockRight, MAX(pad, headsUpBottom));
+	_headsUpRect = makeSafeRect(blockLeft, MAX(topPad, headsUpTop), blockRight, MAX(topPad, headsUpBottom));
 
-	_powerRect = makeSafeRect(blockLeft, pad, blockRight, _headsUpRect.top - 4);
+	_powerRect = makeSafeRect(blockLeft, topPad, blockRight, _headsUpRect.top - 4);
 }
 
 void ColonyEngine::drawDashboardStep1() {


Commit: 777dd6e82d0b35e5df59e9555751f985f8dcd415
    https://github.com/scummvm/scummvm/commit/777dd6e82d0b35e5df59e9555751f985f8dcd415
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:25+02:00

Commit Message:
COLONY: support for mac fonts

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 9fa3e0568e6..cad0a18d07d 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -141,11 +141,6 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 
 	_sound = new Sound(this);
 	_resMan = new Common::MacResManager();
-	if (getPlatform() == Common::kPlatformMacintosh) {
-		if (!_resMan->open("Colony")) {
-			_resMan->open("Colony.bin");
-		}
-	}
 	initTrig();
 }
 
@@ -656,6 +651,16 @@ void ColonyEngine::initTrig() {
 }
 
 Common::Error ColonyEngine::run() {
+	// Open Colony resource fork (must happen in run(), not constructor,
+	// because SearchMan doesn't have the game path until now)
+	if (getPlatform() == Common::kPlatformMacintosh) {
+		if (!_resMan->open("Colony")) {
+			if (!_resMan->open("Colony.bin")) {
+				warning("Failed to open Colony resource fork");
+			}
+		}
+	}
+
 	_width = 640;
 	_height = 350;
 
@@ -914,9 +919,13 @@ void ColonyEngine::playIntro() {
 		// Load the Mac "Commando" font (FOND 190, 12pt) from Colony resources.
 		// Original intro.c: TextFont(190); TextSize(12);
 		// FONT resource ID = FOND_ID * 128 + size = 190 * 128 + 12 = 24332
+		// Some builds store it as NFNT instead of FONT.
 		Graphics::MacFONTFont *macFont = nullptr;
 		if (_resMan) {
-			Common::SeekableReadStream *fontStream = _resMan->getResource(MKTAG('F', 'O', 'N', 'T'), 24332);
+			const uint16 fontResID = 24332;
+			Common::SeekableReadStream *fontStream = _resMan->getResource(MKTAG('N', 'F', 'N', 'T'), fontResID);
+			if (!fontStream)
+				fontStream = _resMan->getResource(MKTAG('F', 'O', 'N', 'T'), fontResID);
 			if (fontStream) {
 				macFont = new Graphics::MacFONTFont();
 				if (!macFont->loadFont(*fontStream)) {
@@ -926,7 +935,15 @@ void ColonyEngine::playIntro() {
 				}
 				delete fontStream;
 			} else {
-				warning("playIntro: FONT 24332 not found in Colony resources");
+				// List available font resources for debugging
+				Common::MacResIDArray nfntIDs = _resMan->getResIDArray(MKTAG('N', 'F', 'N', 'T'));
+				Common::MacResIDArray fontIDs = _resMan->getResIDArray(MKTAG('F', 'O', 'N', 'T'));
+				debug("playIntro: FONT/NFNT %d not found. Available NFNT IDs: %d, FONT IDs: %d",
+				      fontResID, nfntIDs.size(), fontIDs.size());
+				for (uint i = 0; i < nfntIDs.size(); i++)
+					debug("  NFNT %d", nfntIDs[i]);
+				for (uint i = 0; i < fontIDs.size(); i++)
+					debug("  FONT %d", fontIDs[i]);
 			}
 		}
 
@@ -939,16 +956,18 @@ void ColonyEngine::playIntro() {
 		_gfx->copyToScreen();
 
 		// 3. "MindScape Presents" logo + PlayMars() + makestars()
-		// B&W Colony has PICT -32748 (254x251) for logo
+		// Color Colony: DoPicture(-32565), B&W Colony: DoPicture(-32748)
 		_gfx->clear(_gfx->black());
-		drawPict(-32748);
+		if (!drawPict(-32565))  // Color Colony
+			drawPict(-32748);   // B&W Colony
 		_sound->play(Sound::kMars);
 		if (makeStars(_screenR, 0)) { _sound->stop(); delete macFont; return; }
 
 		// 4. "The Colony by David A. Smith" logo + makestars()
-		// B&W Colony has PICT -32750 (394x252) for title card
+		// Color Colony: DoPicture(-32564), B&W Colony: DoPicture(-32750)
 		_gfx->clear(_gfx->black());
-		drawPict(-32750);
+		if (!drawPict(-32564))  // Color Colony
+			drawPict(-32750);   // B&W Colony
 		if (makeStars(_screenR, 0)) { _sound->stop(); delete macFont; return; }
 
 		// 5. Empty stars
@@ -1375,19 +1394,19 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 	return false;
 }
 
-void ColonyEngine::drawPict(int resID) {
+bool ColonyEngine::drawPict(int resID) {
 	// Original: DoPicture() in intro.c
 	// Loads a PICT resource from the Colony application, centers it on screen.
 	if (!_resMan || !(_resMan->isMacFile() || _resMan->hasResFork()))
-		return;
+		return false;
 
 	// Try both signed interpretations for negative resource IDs
 	Common::SeekableReadStream *pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
 	if (!pictStream) {
 		pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), resID);
 		if (!pictStream) {
-			warning("drawPict: could not load PICT %d", resID);
-			return;
+			debug("drawPict: PICT %d not found", resID);
+			return false;
 		}
 	}
 
@@ -1395,35 +1414,80 @@ void ColonyEngine::drawPict(int resID) {
 	if (decoder.loadStream(*pictStream)) {
 		const Graphics::Surface *surface = decoder.getSurface();
 		if (surface) {
-			// Center like the original: locate = centered, clip = inset by 1
 			int x = (_width - surface->w) / 2;
 			int y = (_height - surface->h) / 2;
-
-			// Detect if this is a 1-bit B&W PICT (CLUT8 with only values 0/1)
-			// In Mac QuickDraw: bit 0 = white (background), bit 1 = black (foreground)
-			bool isBW = (surface->format == Graphics::PixelFormat::createFormatCLUT8());
-
-			for (int iy = 0; iy < surface->h; iy++) {
-				if (y + iy < 0 || y + iy >= _height) continue;
-				for (int ix = 0; ix < surface->w; ix++) {
-					if (x + ix < 0 || x + ix >= _width) continue;
-					uint32 color = surface->getPixel(ix, iy);
-					if (isBW) {
-						// Mac QuickDraw: 0=white, 1=black
-						// Map to our palette: white=15 (IntWhite), black=0
-						color = (color == 0) ? 15 : 0;
+			bool isCLUT8 = (surface->format == Graphics::PixelFormat::createFormatCLUT8());
+			const Graphics::Palette &pictPal = decoder.getPalette();
+
+			debug("drawPict(%d): %dx%d, format=%dbpp, palette=%d entries",
+			      resID, surface->w, surface->h, surface->format.bytesPerPixel * 8, pictPal.size());
+
+			if (isCLUT8 && pictPal.size() > 0) {
+				// CLUT8 PICT with embedded palette.
+				// Install PICT palette at offset 128 in our engine palette,
+				// then draw pixels remapped to that offset.
+				const int palOffset = 128;
+				int nColors = MIN((int)pictPal.size(), 128); // cap to fit 128-255
+				_gfx->setPalette(pictPal.data(), palOffset, nColors);
+
+				for (int iy = 0; iy < surface->h; iy++) {
+					if (y + iy < 0 || y + iy >= _height) continue;
+					for (int ix = 0; ix < surface->w; ix++) {
+						if (x + ix < 0 || x + ix >= _width) continue;
+						byte idx = *((const byte *)surface->getBasePtr(ix, iy));
+						// Look up the pixel's RGB from the PICT palette
+						byte pr = pictPal.data()[idx * 3 + 0];
+						byte pg = pictPal.data()[idx * 3 + 1];
+						byte pb = pictPal.data()[idx * 3 + 2];
+						// Skip black pixels so the logo sits on the black background
+						if (pr == 0 && pg == 0 && pb == 0)
+							continue;
+						int palIdx = (idx < nColors) ? palOffset + idx : 15;
+						_gfx->setPixel(x + ix, y + iy, palIdx);
+					}
+				}
+			} else if (isCLUT8) {
+				// CLUT8 without palette — assume 1-bit B&W PICT.
+				// Mac QuickDraw: 0=white (background), 1=black (foreground)
+				for (int iy = 0; iy < surface->h; iy++) {
+					if (y + iy < 0 || y + iy >= _height) continue;
+					for (int ix = 0; ix < surface->w; ix++) {
+						if (x + ix < 0 || x + ix >= _width) continue;
+						byte idx = *((const byte *)surface->getBasePtr(ix, iy));
+						if (idx == 0)
+							_gfx->setPixel(x + ix, y + iy, 15); // white
+						// idx != 0 → black, skip (background)
+					}
+				}
+			} else {
+				// RGB surface — convert each pixel through nearest-palette match.
+				// Use palette entries 0-15 (EGA) for matching.
+				for (int iy = 0; iy < surface->h; iy++) {
+					if (y + iy < 0 || y + iy >= _height) continue;
+					for (int ix = 0; ix < surface->w; ix++) {
+						if (x + ix < 0 || x + ix >= _width) continue;
+						uint32 pixel = surface->getPixel(ix, iy);
+						byte r, g, b;
+						surface->format.colorToRGB(pixel, r, g, b);
+						if (r == 0 && g == 0 && b == 0)
+							continue;
+						// Simple luminance-based mapping to grayscale palette
+						int lum = (r * 77 + g * 150 + b * 29) >> 8;
+						int palIdx = (lum * 15 + 127) / 255; // map 0-255 → 0-15
+						if (palIdx > 0)
+							_gfx->setPixel(x + ix, y + iy, palIdx);
 					}
-					// Only draw non-black pixels so the logo sits on the black background
-					if (color != 0)
-						_gfx->setPixel(x + ix, y + iy, color);
 				}
 			}
 			_gfx->copyToScreen();
+			delete pictStream;
+			return true;
 		}
 	} else {
 		warning("drawPict: failed to decode PICT %d", resID);
 	}
 	delete pictStream;
+	return false;
 }
 
 bool ColonyEngine::loadAnimation(const Common::String &name) {
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index e3579ef569c..5e2f2095296 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -498,7 +498,7 @@ private:
 	bool makeStars(const Common::Rect &r, int btn);
 	bool makeBlackHole();
 	bool timeSquare(const Common::String &str, const Graphics::Font *macFont = nullptr);
-	void drawPict(int resID);
+	bool drawPict(int resID);
 	bool loadAnimation(const Common::String &name);
 	void deleteAnimation();
 	void playAnimation();


Commit: c478c5c54153084a94a91ac7ec73899201f81f41
    https://github.com/scummvm/scummvm/commit/c478c5c54153084a94a91ac7ec73899201f81f41
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:25+02:00

Commit Message:
IMAGE: correctly decode PICT images with offsets

Changed paths:
    image/pict.cpp


diff --git a/image/pict.cpp b/image/pict.cpp
index 078c30a4576..a265c246e98 100644
--- a/image/pict.cpp
+++ b/image/pict.cpp
@@ -559,28 +559,40 @@ void PICTDecoder::unpackBits(Common::SeekableReadStream &stream, bool compressed
 		_outputSurface->create(_imageRect.width(), _imageRect.height(), Graphics::PixelFormat::createFormatCLUT8());
 	}
 
+	// Bounds rect: the bitmap's native coordinate space
 	int y1 = stream.readSint16BE();
 	int x1 = stream.readSint16BE();
 	int y2 = stream.readSint16BE();
 	int x2 = stream.readSint16BE();
 
 	stream.skip(8); // srcRect
-	stream.skip(8); // dstRect
+
+	// dstRect: where the bitmap maps to in the PICT's coordinate space.
+	// When bounds differs from picFrame (e.g., bitmap at screen coords
+	// mapped to picFrame origin), we need dstRect for correct placement.
+	int dstTop = stream.readSint16BE();
+	int dstLeft = stream.readSint16BE();
+	stream.skip(4); // dstBottom, dstRight (not needed)
 	stream.skip(2); // mode
 
 	if (hasRegion)
 		stream.skip(stream.readUint16BE() - 2);
 
+	// Compute offset: map bitmap coords (bounds) to output surface coords
+	// via dstRect and _imageRect (picFrame).
+	int yOff = dstTop - _imageRect.top - y1;
+	int xOff = dstLeft - _imageRect.left - x1;
+
 	Common::Rect outputRect(_outputSurface->w, _outputSurface->h);
 
 	if (!compressed) {
 		Common::BitStream8MSB bs(stream);
 
 		for (int y = y1; y < y2; y++) {
-			int yPos = y - _imageRect.top;
+			int yPos = y + yOff;
 
 			for (int x = x1; x < x2; x++) {
-				int xPos = x - _imageRect.left;
+				int xPos = x + xOff;
 
 				uint bit = bs.getBit();
 
@@ -593,7 +605,7 @@ void PICTDecoder::unpackBits(Common::SeekableReadStream &stream, bool compressed
 	}
 
 	for (int y = y1; y < y2; y++) {
-		int yPos = y - _imageRect.top;
+		int yPos = y + yOff;
 		int x = x1;
 
 		byte rowBytes = stream.readByte();
@@ -621,7 +633,7 @@ void PICTDecoder::unpackBits(Common::SeekableReadStream &stream, bool compressed
 			Common::BitStream8MSB bs(ms);
 
 			for (int i = 0; i < 8 * bufLen; i++) {
-				int xPos = x - _imageRect.left;
+				int xPos = x + xOff;
 				uint bit = bs.getBit();
 
 				if (outputRect.contains(xPos, yPos))


Commit: 582f93c08ef0004029afaaafd84d164cfe890237
    https://github.com/scummvm/scummvm/commit/582f93c08ef0004029afaaafd84d164cfe890237
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:25+02:00

Commit Message:
COLONY: rewrite intro sequence to match original with sound sync

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index cad0a18d07d..c9621db37c2 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -22,6 +22,7 @@
 #include "colony/colony.h"
 #include "colony/gfx.h"
 #include "common/config-manager.h"
+#include "common/hashmap.h"
 #include "common/file.h"
 #include "common/system.h"
 #include "common/util.h"
@@ -947,65 +948,109 @@ void ColonyEngine::playIntro() {
 			}
 		}
 
-		// Original: intro() in intro.c
+		// Original: intro() in intro.c, lines 40-119
+		// qt flag propagates through sections — only modifier+click sets it
+		bool qt = false;
+
 		// 1. ScrollInfo() - scrolling story text with BeamMe sound
-		scrollInfo(macFont);
+		qt = scrollInfo(macFont);
 
-		// 2. Wait for sound to finish
-		_gfx->clear(_gfx->black());
-		_gfx->copyToScreen();
+		// 2. Wait for BeamMe sound to finish
+		// Original: if(!qt) while(!SoundDone());
+		while (!qt && !shouldQuit() && _sound->isPlaying())
+			_system->delayMillis(10);
 
-		// 3. "MindScape Presents" logo + PlayMars() + makestars()
-		// Color Colony: DoPicture(-32565), B&W Colony: DoPicture(-32748)
-		_gfx->clear(_gfx->black());
-		if (!drawPict(-32565))  // Color Colony
-			drawPict(-32748);   // B&W Colony
-		_sound->play(Sound::kMars);
-		if (makeStars(_screenR, 0)) { _sound->stop(); delete macFont; return; }
+		// Original: if(Button()) qt=OptionKey(); — check for skip
+		if (!qt) {
+			Common::Event event;
+			while (_system->getEventManager()->pollEvent(event)) {
+				if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
+					int mods = _system->getEventManager()->getModifierState();
+					if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
+						qt = true;
+				}
+			}
+		}
 
-		// 4. "The Colony by David A. Smith" logo + makestars()
-		// Color Colony: DoPicture(-32564), B&W Colony: DoPicture(-32750)
-		_gfx->clear(_gfx->black());
-		if (!drawPict(-32564))  // Color Colony
-			drawPict(-32750);   // B&W Colony
-		if (makeStars(_screenR, 0)) { _sound->stop(); delete macFont; return; }
+		if (!qt) {
+			// 3. Logo 1 + PlayMars + makestars
+			// Original: FillRect black; DoPicture; PlayMars(); makestars()
+			_gfx->clear(_gfx->black());
+			if (!drawPict(-32565))  // Color Colony
+				drawPict(-32748);   // B&W Colony
+			_sound->play(Sound::kMars);
+			qt = makeStars(_screenR, 0);
+
+			if (!qt) {
+				// 4. Logo 2 + makestars (inside the same !qt block as original)
+				// Original: FillRect black; DoPicture(-32564); makestars()
+				_gfx->clear(_gfx->black());
+				if (!drawPict(-32564))  // Color Colony
+					drawPict(-32750);   // B&W Colony
+				qt = makeStars(_screenR, 0);
+			}
 
-		// 5. Empty stars
-		_gfx->clear(_gfx->black());
-		_gfx->copyToScreen();
-		if (makeStars(_screenR, 0)) { _sound->stop(); delete macFont; return; }
+			if (!qt) {
+				// 5. Empty starfield
+				// Original: FillRect black; makestars()
+				_gfx->clear(_gfx->black());
+				_gfx->copyToScreen();
+				qt = makeStars(_screenR, 0);
+			}
 
-		// 6. TimeSquare("...BLACK HOLE COLLISION...")
-		if (timeSquare("...BLACK HOLE COLLISION...", macFont)) { _sound->stop(); delete macFont; return; }
+			if (!qt) {
+				// 6. TimeSquare("...BLACK HOLE COLLISION...")
+				qt = timeSquare("...BLACK HOLE COLLISION...", macFont);
+			}
 
-		// 7. Makeblackhole()
-		_gfx->clear(_gfx->black());
-		_gfx->copyToScreen();
-		if (makeBlackHole()) { _sound->stop(); delete macFont; return; }
+			if (!qt) {
+				// 7. Makeblackhole()
+				_gfx->clear(_gfx->black());
+				_gfx->copyToScreen();
+				qt = makeBlackHole();
+			}
+
+			if (!qt) {
+				// 8. TimeSquare("...FUEL HAS BEEN DEPLETED...")
+				qt = timeSquare("...FUEL HAS BEEN DEPLETED...", macFont);
+			}
 
-		// 8. TimeSquare("...FUEL HAS BEEN DEPLETED...")
-		if (timeSquare("...FUEL HAS BEEN DEPLETED...", macFont)) { _sound->stop(); delete macFont; return; }
+			// Original: SetPort(&metaPort); before next TimeSquare (no !qt guard)
+			if (!qt) {
+				// 9. TimeSquare("...PREPARE FOR CRASH LANDING...")
+				qt = timeSquare("...PREPARE FOR CRASH LANDING...", macFont);
+			}
 
-		// 9. TimeSquare("...PREPARE FOR CRASH LANDING...")
-		if (timeSquare("...PREPARE FOR CRASH LANDING...", macFont)) { _sound->stop(); delete macFont; return; }
+			if (!qt) {
+				// 10. makeplanet() + EndCSound()
+				// Simplified: starfield + delay (makeplanet draws a rotating planet)
+				_gfx->clear(_gfx->black());
+				_gfx->copyToScreen();
+				qt = makeStars(_screenR, 0);
+				_sound->stop(); // EndCSound()
+			}
+		}
 
-		// 10. makeplanet() + EndCSound() — simplified: just a delay
+		// 11. Final crash — always runs (even if qt)
+		// Original: FillRect black; if(!qt) while(!SoundDone());
 		_gfx->clear(_gfx->black());
 		_gfx->copyToScreen();
-		_system->delayMillis(500);
+		while (!qt && !shouldQuit() && _sound->isPlaying())
+			_system->delayMillis(10);
 
-		// 11. Final crash: DoExplodeSound() + InvertRect flashing
-		_gfx->clear(_gfx->black());
-		_gfx->copyToScreen();
+		// Original: DoExplodeSound(); while(!SoundDone()) InvertRect(&rScreen); StopSound();
 		_sound->play(Sound::kExplode);
-		for (int i = 0; i < 16; i++) {
-			_gfx->clear(i % 2 ? _gfx->white() : _gfx->black());
+		while (!shouldQuit() && _sound->isPlaying()) {
+			_gfx->clear(_gfx->white());
+			_gfx->copyToScreen();
+			_system->delayMillis(50);
+			_gfx->clear(_gfx->black());
 			_gfx->copyToScreen();
 			_system->delayMillis(50);
 		}
+		_sound->stop();
 		_gfx->clear(_gfx->black());
 		_gfx->copyToScreen();
-		_sound->stop();
 		delete macFont;
 		macFont = nullptr;
 
@@ -1023,12 +1068,13 @@ void ColonyEngine::playIntro() {
 	}
 }
 
-void ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
-	// Original: ScrollInfo() in intro.c
-	// Displays story text in blue gradient on black, plays DoBeammeSound(),
-	// scrolls text up from bottom, waits for click, scrolls off top.
+bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
+	// Original: ScrollInfo() in intro.c, lines 138-221
+	// Renders story text in blue gradient to offscreen half-width buffer,
+	// scrolls it up from below screen with DoBeammeSound(),
+	// waits for click, then scrolls it off the top.
 	// Mac original: TextFont(190 = Commando); TextSize(12);
-	// Text starts bright blue (0xFFFF) and fades by -4096 per line.
+	// Text blue starts at 0xFFFF and fades by -4096 per visible line.
 	const char *story[] = {
 		"",
 		"Mankind has left the",
@@ -1051,10 +1097,12 @@ void ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 		_sound->play(Sound::kBeamMe);
 
 	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+
 	Graphics::DosFont dosFont;
 	const Graphics::Font *font = macFont ? macFont : (const Graphics::Font *)&dosFont;
 
-	// Original uses 19px line height, centers vertically
+	// Original uses 19px line height, centers vertically within height
 	int lineHeight = 19;
 	int totalHeight = lineHeight * storyLength;
 	int ht = (_height - totalHeight) / 2;
@@ -1064,7 +1112,6 @@ void ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 	byte pal[14 * 3]; // storyLength entries
 	memset(pal, 0, sizeof(pal));
 	for (int i = 0; i < storyLength; i++) {
-		// Blue intensity: starts at 255 (0xFFFF >> 8), decreases by ~16 per line
 		int blue = 255 - i * 16;
 		if (blue < 0) blue = 0;
 		pal[i * 3 + 0] = 0;     // R
@@ -1073,27 +1120,101 @@ void ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 	}
 	_gfx->setPalette(pal, 200, storyLength);
 
-	// Draw text with per-line blue gradient
-	for (int i = 0; i < storyLength; i++) {
-		if (strlen(story[i]) > 0)
-			_gfx->drawString(font, story[i], _width / 2, ht + lineHeight * i, 200 + i, Graphics::kTextAlignCenter);
-	}
-	_gfx->copyToScreen();
+	// Phase 1: Scroll text up from below screen
+	// Original: scrollRect starts at bottom (stayRect.bottom..stayRect.bottom*2),
+	// moves up by inc=4 each frame until text is visible at its correct position.
+	// We simulate by drawing text with a y-offset that starts at _height and decreases to 0.
+	int inc = 4;
+	bool qt = false;
 
-	// Wait for click (original: while(!Button()); while(Button()&&!qt);)
-	bool waiting = true;
-	while (waiting && !shouldQuit()) {
+	for (int scrollOff = _height; scrollOff > 0 && !qt; scrollOff -= inc) {
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
-				waiting = false;
+			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
+				// Original: if(Button()) if(qt=OptionKey()) { StopSound(); break; }
+				// Only modifier+click/key skips entire intro
+				int mods = _system->getEventManager()->getModifierState();
+				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META)) {
+					qt = true;
+					_sound->stop();
+					break;
+				}
+			}
 		}
-		_system->delayMillis(10);
+		if (shouldQuit()) { qt = true; break; }
+
+		_gfx->clear(_gfx->black());
+		for (int i = 0; i < storyLength; i++) {
+			int drawY = ht + lineHeight * i + scrollOff;
+			if (strlen(story[i]) > 0 && drawY >= 0 && drawY < _height)
+				_gfx->drawString(font, story[i], _width / 2, drawY, 200 + i, Graphics::kTextAlignCenter);
+		}
+		_gfx->copyToScreen();
+		_system->delayMillis(16);
 	}
 
-	_sound->stop();
+	// Draw final position (scrollOff = 0)
+	if (!qt) {
+		_gfx->clear(_gfx->black());
+		for (int i = 0; i < storyLength; i++) {
+			if (strlen(story[i]) > 0)
+				_gfx->drawString(font, story[i], _width / 2, ht + lineHeight * i, 200 + i, Graphics::kTextAlignCenter);
+		}
+		_gfx->copyToScreen();
+	}
+
+	// Wait for click (original: while(!Button()); while(Button()&&!qt);)
+	if (!qt) {
+		bool waiting = true;
+		while (waiting && !shouldQuit()) {
+			Common::Event event;
+			while (_system->getEventManager()->pollEvent(event)) {
+				if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
+					// Check if modifier held — if so, skip entire intro
+					int mods = _system->getEventManager()->getModifierState();
+					if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
+						qt = true;
+					waiting = false;
+				}
+			}
+			_system->delayMillis(10);
+		}
+	}
+
+	// Phase 2: Scroll text off the top of the screen
+	// Original: scrollRect continues moving up, text slides upward
+	if (!qt) {
+		for (int scrollOff = 0; scrollOff > -_height && !qt; scrollOff -= inc) {
+			Common::Event event;
+			while (_system->getEventManager()->pollEvent(event)) {
+				if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
+					int mods = _system->getEventManager()->getModifierState();
+					if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META)) {
+						qt = true;
+						_sound->stop();
+						break;
+					}
+				}
+			}
+			if (shouldQuit()) { qt = true; break; }
+
+			_gfx->clear(_gfx->black());
+			for (int i = 0; i < storyLength; i++) {
+				int drawY = ht + lineHeight * i + scrollOff;
+				if (strlen(story[i]) > 0 && drawY >= -lineHeight && drawY < _height)
+					_gfx->drawString(font, story[i], _width / 2, drawY, 200 + i, Graphics::kTextAlignCenter);
+			}
+			_gfx->copyToScreen();
+			_system->delayMillis(16);
+		}
+	}
+
+	// Original does NOT stop the sound here — BeamMe continues playing
+	// and intro() waits for it with while(!SoundDone()) after ScrollInfo returns.
+	// Only stop if skipping (qt already stops in the modifier+click handlers above).
 	_gfx->clear(_gfx->black());
 	_gfx->copyToScreen();
+	return qt;
 }
 
 bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
@@ -1150,11 +1271,15 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 	_gfx->copyToScreen();
 
 	// Animate: original loops ~200 frames or until Mars sound repeats 2x
+	// Original: only modifier+click (OptionKey()) returns true when btn=0
 	for (int k = 0; k < 120; k++) {
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONUP)
-				return true;
+			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
+				int mods = _system->getEventManager()->getModifierState();
+				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
+					return true;
+			}
 		}
 		if (shouldQuit()) return true;
 
@@ -1188,13 +1313,17 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 	}
 
 	// Fade-out phase: stars fly off without resetting
+	// Original: only modifier+click (OptionKey()) returns true when btn=0
 	int nstars = 2 * ((MAXSTAR - 0x030) / deltapd);
 	if (nstars > 200) nstars = 200;
 	for (int k = 0; k < nstars; k++) {
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONUP)
-				return true;
+			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
+				int mods = _system->getEventManager()->getModifierState();
+				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
+					return true;
+			}
 		}
 		if (shouldQuit()) return true;
 
@@ -1288,8 +1417,12 @@ bool ColonyEngine::makeBlackHole() {
 
 			Common::Event event;
 			while (_system->getEventManager()->pollEvent(event)) {
-				if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONUP)
-					return true;
+				// Original: if(Button()) if(OptionKey()) return(TRUE);
+				if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
+					int mods = _system->getEventManager()->getModifierState();
+					if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
+						return true;
+				}
 			}
 			if (shouldQuit()) return true;
 		}
@@ -1340,52 +1473,71 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 	_gfx->copyToScreen();
 
 	// Phase 1: Scroll text in from the right to center
+	// Original: if(Button()) if(qt=OptionKey()) break;
 	int targetX = (_width - swidth) / 2;
 	for (int x = _width; x > targetX; x -= 2) {
-		// Redraw center band
 		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), 0);
-		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft); // Red text
+		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft);
 		_gfx->copyToScreen();
 
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
-				return true;
+			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
+				int mods = _system->getEventManager()->getModifierState();
+				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
+					return true;
+			}
 		}
 		if (shouldQuit()) return true;
 		_system->delayMillis(8);
 	}
 
-	// Phase 2: Klaxon flash – original does 6 iterations of:
+	// Phase 2: Klaxon flash — original: EndCSound(); then 6 iterations of:
+	//   if(Button()) if(qt=OptionKey()) break;
 	//   while(!SoundDone()); StopSound(); PlayKlaxon(); InvertRect(&invrt);
-	_sound->stop();
+	_sound->stop(); // EndCSound()
 	for (int i = 0; i < 6; i++) {
-		_sound->play(Sound::kKlaxon);
-		// Invert the center band area
-		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), i % 2 ? 15 : 0);
-		_gfx->drawString(font, str, targetX, centery + 2, i % 2 ? 0 : 15, Graphics::kTextAlignLeft);
-		_gfx->copyToScreen();
-		_system->delayMillis(300);
-
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
-				return true;
+			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
+				int mods = _system->getEventManager()->getModifierState();
+				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
+					return true;
+			}
 		}
 		if (shouldQuit()) return true;
+
+		// Wait for previous klaxon to finish
+		while (_sound->isPlaying() && !shouldQuit())
+			_system->delayMillis(10);
+		_sound->stop();
+
+		_sound->play(Sound::kKlaxon);
+
+		// InvertRect(&invrt) — toggle the text band
+		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), i % 2 ? 0 : 15);
+		_gfx->drawString(font, str, targetX, centery + 2, i % 2 ? 15 : 0, Graphics::kTextAlignLeft);
+		_gfx->copyToScreen();
 	}
+	// Wait for last klaxon
+	while (_sound->isPlaying() && !shouldQuit())
+		_system->delayMillis(10);
+	_sound->stop();
 
 	// Phase 3: PlayMars(), scroll text off to the left
 	_sound->play(Sound::kMars);
 	for (int x = targetX; x > -swidth; x -= 2) {
 		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), 0);
-		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft); // Red text
+		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft);
 		_gfx->copyToScreen();
 
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
-				return true;
+			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
+				int mods = _system->getEventManager()->getModifierState();
+				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
+					return true;
+			}
 		}
 		if (shouldQuit()) return true;
 		_system->delayMillis(8);
@@ -1395,12 +1547,12 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 }
 
 bool ColonyEngine::drawPict(int resID) {
-	// Original: DoPicture() in intro.c
-	// Loads a PICT resource from the Colony application, centers it on screen.
+	// Original: DoPicture() in intro.c, lines 861-886
+	// Loads a PICT resource, centers it in the screen rect, draws with srcCopy.
+	// Original applies clip rect inset by 1 pixel on all sides.
 	if (!_resMan || !(_resMan->isMacFile() || _resMan->hasResFork()))
 		return false;
 
-	// Try both signed interpretations for negative resource IDs
 	Common::SeekableReadStream *pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
 	if (!pictStream) {
 		pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), resID);
@@ -1414,68 +1566,105 @@ bool ColonyEngine::drawPict(int resID) {
 	if (decoder.loadStream(*pictStream)) {
 		const Graphics::Surface *surface = decoder.getSurface();
 		if (surface) {
+			// Center PICT on screen (original: locate = centered within rScreen)
 			int x = (_width - surface->w) / 2;
 			int y = (_height - surface->h) / 2;
 			bool isCLUT8 = (surface->format == Graphics::PixelFormat::createFormatCLUT8());
 			const Graphics::Palette &pictPal = decoder.getPalette();
 
-			debug("drawPict(%d): %dx%d, format=%dbpp, palette=%d entries",
-			      resID, surface->w, surface->h, surface->format.bytesPerPixel * 8, pictPal.size());
+			// Original DoPicture clips 1 pixel inset from locate rect
+			// clip.top = locate.top+1, clip.left = locate.left+1, etc.
+			int clipX1 = x + 1;
+			int clipY1 = y + 1;
+			int clipX2 = x + surface->w - 1;
+			int clipY2 = y + surface->h - 1;
+
+			debug("drawPict(%d): %dx%d at (%d,%d), format=%dbpp, palette=%d entries",
+			      resID, surface->w, surface->h, x, y,
+			      surface->format.bytesPerPixel * 8, pictPal.size());
 
 			if (isCLUT8 && pictPal.size() > 0) {
-				// CLUT8 PICT with embedded palette.
-				// Install PICT palette at offset 128 in our engine palette,
-				// then draw pixels remapped to that offset.
+				// CLUT8 PICT with embedded palette (8-bit color).
+				// Install PICT palette at offset 128, draw all pixels with srcCopy.
 				const int palOffset = 128;
-				int nColors = MIN((int)pictPal.size(), 128); // cap to fit 128-255
+				int nColors = MIN((int)pictPal.size(), 128);
 				_gfx->setPalette(pictPal.data(), palOffset, nColors);
 
 				for (int iy = 0; iy < surface->h; iy++) {
-					if (y + iy < 0 || y + iy >= _height) continue;
+					int sy = y + iy;
+					if (sy < clipY1 || sy >= clipY2) continue;
 					for (int ix = 0; ix < surface->w; ix++) {
-						if (x + ix < 0 || x + ix >= _width) continue;
+						int sx = x + ix;
+						if (sx < clipX1 || sx >= clipX2) continue;
 						byte idx = *((const byte *)surface->getBasePtr(ix, iy));
-						// Look up the pixel's RGB from the PICT palette
-						byte pr = pictPal.data()[idx * 3 + 0];
-						byte pg = pictPal.data()[idx * 3 + 1];
-						byte pb = pictPal.data()[idx * 3 + 2];
-						// Skip black pixels so the logo sits on the black background
-						if (pr == 0 && pg == 0 && pb == 0)
-							continue;
-						int palIdx = (idx < nColors) ? palOffset + idx : 15;
-						_gfx->setPixel(x + ix, y + iy, palIdx);
+						int palIdx = (idx < nColors) ? palOffset + idx : 0;
+						_gfx->setPixel(sx, sy, palIdx);
 					}
 				}
 			} else if (isCLUT8) {
-				// CLUT8 without palette — assume 1-bit B&W PICT.
-				// Mac QuickDraw: 0=white (background), 1=black (foreground)
+				// CLUT8 without palette — 1-bit B&W PICT.
+				// Mac QuickDraw srcCopy: pixel 0 = background (white),
+				// pixel 1 = foreground (black). Screen is already black,
+				// so we only need to draw the white pixels.
 				for (int iy = 0; iy < surface->h; iy++) {
-					if (y + iy < 0 || y + iy >= _height) continue;
+					int sy = y + iy;
+					if (sy < clipY1 || sy >= clipY2) continue;
 					for (int ix = 0; ix < surface->w; ix++) {
-						if (x + ix < 0 || x + ix >= _width) continue;
+						int sx = x + ix;
+						if (sx < clipX1 || sx >= clipX2) continue;
 						byte idx = *((const byte *)surface->getBasePtr(ix, iy));
 						if (idx == 0)
-							_gfx->setPixel(x + ix, y + iy, 15); // white
-						// idx != 0 → black, skip (background)
+							_gfx->setPixel(sx, sy, 15); // white
 					}
 				}
 			} else {
-				// RGB surface — convert each pixel through nearest-palette match.
-				// Use palette entries 0-15 (EGA) for matching.
+				// RGB surface — build a color palette from unique pixel colors
+				// and install it at offset 128, preserving actual PICT colors.
+				const int palOffset = 128;
+				const int maxColors = 128;
+				byte builtPal[128 * 3];
+				memset(builtPal, 0, sizeof(builtPal));
+				int nUniqueColors = 0;
+
+				Common::HashMap<uint32, byte> colorMap;
+
+				for (int iy = 0; iy < surface->h && nUniqueColors < maxColors; iy++) {
+					for (int ix = 0; ix < surface->w && nUniqueColors < maxColors; ix++) {
+						uint32 pixel = surface->getPixel(ix, iy);
+						byte pr, pg, pb;
+						surface->format.colorToRGB(pixel, pr, pg, pb);
+						if (pr == 0 && pg == 0 && pb == 0)
+							continue;
+						uint32 key = ((uint32)pr << 16) | ((uint32)pg << 8) | pb;
+						if (!colorMap.contains(key)) {
+							builtPal[nUniqueColors * 3 + 0] = pr;
+							builtPal[nUniqueColors * 3 + 1] = pg;
+							builtPal[nUniqueColors * 3 + 2] = pb;
+							colorMap[key] = nUniqueColors;
+							nUniqueColors++;
+						}
+					}
+				}
+
+				if (nUniqueColors > 0)
+					_gfx->setPalette(builtPal, palOffset, nUniqueColors);
+
 				for (int iy = 0; iy < surface->h; iy++) {
-					if (y + iy < 0 || y + iy >= _height) continue;
+					int sy = y + iy;
+					if (sy < clipY1 || sy >= clipY2) continue;
 					for (int ix = 0; ix < surface->w; ix++) {
-						if (x + ix < 0 || x + ix >= _width) continue;
+						int sx = x + ix;
+						if (sx < clipX1 || sx >= clipX2) continue;
 						uint32 pixel = surface->getPixel(ix, iy);
-						byte r, g, b;
-						surface->format.colorToRGB(pixel, r, g, b);
-						if (r == 0 && g == 0 && b == 0)
+						byte pr, pg, pb;
+						surface->format.colorToRGB(pixel, pr, pg, pb);
+						if (pr == 0 && pg == 0 && pb == 0)
 							continue;
-						// Simple luminance-based mapping to grayscale palette
-						int lum = (r * 77 + g * 150 + b * 29) >> 8;
-						int palIdx = (lum * 15 + 127) / 255; // map 0-255 → 0-15
-						if (palIdx > 0)
-							_gfx->setPixel(x + ix, y + iy, palIdx);
+						uint32 key = ((uint32)pr << 16) | ((uint32)pg << 8) | pb;
+						if (colorMap.contains(key))
+							_gfx->setPixel(sx, sy, palOffset + colorMap[key]);
+						else
+							_gfx->setPixel(sx, sy, 15);
 					}
 				}
 			}
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 5e2f2095296..46805295f66 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -321,7 +321,7 @@ public:
 	void quadrant();
 	int checkwall(int xnew, int ynew, Locate *pobject);
 	void cCommand(int xnew, int ynew, bool allowInteraction);
-	void scrollInfo(const Graphics::Font *macFont = nullptr);
+	bool scrollInfo(const Graphics::Font *macFont = nullptr);
 	void checkCenter();
 	void fallThroughHole();
 
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index 61cfaae3010..56dbda8ca92 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -31,17 +31,26 @@ Sound::Sound(ColonyEngine *vm) : _vm(vm) {
 	_speaker = new Audio::PCSpeaker();
 	_speaker->init();
 
+	// Open Zounds resource file (contains most sounds)
 	_resMan = new Common::MacResManager();
 	if (!_resMan->open("Zounds")) {
 		if (!_resMan->open("CData/Zounds")) {
 			debug("Could not open Zounds resource file");
 		}
 	}
+
+	// Open Colony application binary (contains snd resources for
+	// EXPLODE, EAT, THEYSHOOT, MESHOOT, CHIME that aren't in Zounds)
+	_appResMan = new Common::MacResManager();
+	if (!_appResMan->open("Colony")) {
+		debug("Could not open Colony resource file for sounds");
+	}
 }
 
 Sound::~Sound() {
 	delete _speaker;
 	delete _resMan;
+	delete _appResMan;
 }
 
 void Sound::stop() {
@@ -51,6 +60,10 @@ void Sound::stop() {
 		_vm->_mixer->stopHandle(_handle);
 }
 
+bool Sound::isPlaying() const {
+	return _vm->_mixer->isSoundHandleActive(_handle) || _speaker->isPlaying();
+}
+
 void Sound::play(int soundID) {
 	stop();
 
@@ -236,25 +249,26 @@ void Sound::playPCSpeaker(int soundID) {
 }
 
 bool Sound::playMacSound(int soundID) {
+	// Primary resource IDs from original sound.c
 	int resID = -1;
 	switch (soundID) {
 	case kKlaxon: resID = 27317; break;
 	case kAirlock: resID = 5141; break;
 	case kOuch: resID = 9924; break;
 	case kChime: resID = 24694; break;
-	case kBang: resID = 24433; break;
-	case kShoot: resID = 24010; break;
+	case kBang: resID = 24433; break;   // MESHOOT
+	case kShoot: resID = 24010; break;  // THEYSHOOT
 	case kEat: resID = 11783; break;
-	case kBonk: resID = 7970; break;
-	case kBzzz: resID = 11642; break;
+	case kBonk: resID = 7970; break;    // UGH
+	case kBzzz: resID = 11642; break;   // FLOOR
 	case kExplode: resID = 1432; break;
 	case kElevator: resID = 12019; break;
-	case kPShot: resID = 27539; break;
+	case kPShot: resID = 27539; break;  // PLANETSHOT
 	case kTest: resID = 25795; break;
 	case kDit: resID = 1516; break;
 	case kSink: resID = 2920; break;
 	case kClatter: resID = 11208; break;
-	case kStop: resID = 29382; break;
+	case kStop: resID = 29382; break;   // FULLSTOP
 	case kTeleport: resID = 9757; break;
 	case kSlug: resID = 8347; break;
 	case kTunnel2: resID = 17354; break;
@@ -271,16 +285,35 @@ bool Sound::playMacSound(int soundID) {
 	if (resID != -1 && playResource(resID))
 		return true;
 
+	// Fallback resource IDs for sounds missing from this binary version.
+	// MARS (23390) not in Zounds or Colony — use MARS2 (32291) from Zounds.
+	// BEAMME (5342) not in Zounds or Colony — use 32015 from Zounds.
+	int altResID = -1;
+	switch (soundID) {
+	case kMars: altResID = 32291; break;   // MARS2
+	case kBeamMe: altResID = 32015; break; // alternate BeamMe
+	default: break;
+	}
+
+	if (altResID != -1 && playResource(altResID))
+		return true;
+
 	// Fallback to DOS sounds if Mac resource is missing
 	playPCSpeaker(soundID);
 	return false;
 }
 
 bool Sound::playResource(int resID) {
-	if (!_resMan || !_resMan->isMacFile())
-		return false;
+	Common::SeekableReadStream *snd = nullptr;
+
+	// Search Zounds first (has most sounds)
+	if (_resMan && _resMan->isMacFile())
+		snd = _resMan->getResource(MKTAG('s', 'n', 'd', ' '), resID);
+
+	// Fall back to Colony application binary (has EXPLODE, EAT, etc.)
+	if (!snd && _appResMan && _appResMan->isMacFile())
+		snd = _appResMan->getResource(MKTAG('s', 'n', 'd', ' '), resID);
 
-	Common::SeekableReadStream *snd = _resMan->getResource(MKTAG('s', 'n', 'd', ' '), resID);
 	if (!snd)
 		return false;
 
diff --git a/engines/colony/sound.h b/engines/colony/sound.h
index c8d9579c344..d75cfe01b11 100644
--- a/engines/colony/sound.h
+++ b/engines/colony/sound.h
@@ -38,6 +38,7 @@ public:
 
 	void play(int soundID);
 	void stop();
+	bool isPlaying() const;
 
 	enum {
 		kKlaxon,
@@ -78,6 +79,7 @@ private:
 	ColonyEngine *_vm;
 	Audio::PCSpeaker *_speaker;
 	Common::MacResManager *_resMan;
+	Common::MacResManager *_appResMan;
 	Audio::SoundHandle _handle;
 
 	void playPCSpeaker(int soundID);


Commit: 470fdf22858e9d3f7dea4a23a2ada4a058e86269
    https://github.com/scummvm/scummvm/commit/470fdf22858e9d3f7dea4a23a2ada4a058e86269
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:25+02:00

Commit Message:
COLONY: mac sounds correctly loaded

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c9621db37c2..9ea1bdcd968 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -660,6 +660,7 @@ Common::Error ColonyEngine::run() {
 				warning("Failed to open Colony resource fork");
 			}
 		}
+		_sound->init();
 	}
 
 	_width = 640;
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index 56dbda8ca92..cb0b6a1f818 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -27,11 +27,14 @@
 
 namespace Colony {
 
-Sound::Sound(ColonyEngine *vm) : _vm(vm) {
+Sound::Sound(ColonyEngine *vm) : _vm(vm), _resMan(nullptr), _appResMan(nullptr) {
 	_speaker = new Audio::PCSpeaker();
 	_speaker->init();
+}
 
-	// Open Zounds resource file (contains most sounds)
+void Sound::init() {
+	// Open Zounds resource file (contains most sounds).
+	// Must be called from run() after SearchMan has the game data path.
 	_resMan = new Common::MacResManager();
 	if (!_resMan->open("Zounds")) {
 		if (!_resMan->open("CData/Zounds")) {
@@ -43,7 +46,9 @@ Sound::Sound(ColonyEngine *vm) : _vm(vm) {
 	// EXPLODE, EAT, THEYSHOOT, MESHOOT, CHIME that aren't in Zounds)
 	_appResMan = new Common::MacResManager();
 	if (!_appResMan->open("Colony")) {
-		debug("Could not open Colony resource file for sounds");
+		if (!_appResMan->open("(Color) Colony")) {
+			debug("Could not open Colony resource file for sounds");
+		}
 	}
 }
 
diff --git a/engines/colony/sound.h b/engines/colony/sound.h
index d75cfe01b11..c16bea3b449 100644
--- a/engines/colony/sound.h
+++ b/engines/colony/sound.h
@@ -36,6 +36,7 @@ public:
 	Sound(ColonyEngine *vm);
 	~Sound();
 
+	void init();
 	void play(int soundID);
 	void stop();
 	bool isPlaying() const;


Commit: eaad9cc0e011e4749a0669f8902aafeebd69ff5c
    https://github.com/scummvm/scummvm/commit/eaad9cc0e011e4749a0669f8902aafeebd69ff5c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:26+02:00

Commit Message:
COLONY: fix OpenGL depth test and wireframe color type mismatch

Changed paths:
    engines/colony/render.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 3eeff1caa42..04b1bb55fd6 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -274,13 +274,13 @@ static const byte *setupMacPattern(Renderer *gfx, int pattern, uint32 fg, uint32
 	if (stipple) {
 		gfx->setMacColors(fg, bg);
 		gfx->setStippleData(stipple);
-		gfx->setWireframe(true, (int)bg);
+		gfx->setWireframe(true, bg);
 	} else if (pattern == 4) {
 		// BLACK: solid fg fill
-		gfx->setWireframe(true, (int)fg);
+		gfx->setWireframe(true, fg);
 	} else {
 		// WHITE (0): solid bg fill + fg outline
-		gfx->setWireframe(true, (int)bg);
+		gfx->setWireframe(true, bg);
 	}
 	return stipple;
 }
@@ -1721,7 +1721,7 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 			} else {
 				fillColor = 0; // B&W: black
 			}
-			_gfx->setWireframe(true, (int)fillColor);
+			_gfx->setWireframe(true, fillColor);
 			if (cnum == 'b') {
 				// Right arrow head: (0,3)-(3,0)-(3,6)
 				float hu[3] = {u[0], u[1], u[6]};
@@ -1745,7 +1745,7 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 			uint32 wallFill = _hasMacColors
 				? packMacColor(_macColors[8 + _level - 1].fg)
 				: (uint32)255;
-			_gfx->setWireframe(true, (int)wallFill);
+			_gfx->setWireframe(true, wallFill);
 		}
 		// Outline in black (or vBLACK for EGA)
 		uint32 lineColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh)
@@ -2277,9 +2277,9 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 					float vb4[4] = {vb, vb, vt, vt};
 					if (val == 4) {
 						// BLACK: solid black fill
-						_gfx->setWireframe(true, (int)(uint32)0xFF000000);
+						_gfx->setWireframe(true, (uint32)0xFF000000);
 						wallPolygon(corners, ub, vb4, 4, 0xFF000000);
-						_gfx->setWireframe(true, (int)packMacColor(_macColors[8 + _level - 1].fg));
+						_gfx->setWireframe(true, packMacColor(_macColors[8 + _level - 1].fg));
 					} else {
 						macFillPoly(ub, vb4, 4, 26 + val); // c_color0 + val
 					}
@@ -2392,13 +2392,13 @@ void ColonyEngine::renderCorridor3D() {
 	// Draw large floor and ceiling quads.
 	// Mac Display(): EraseRect fills ceiling with c_lwall.bg and floor with c_lwall.fg.
 	// Set wireframe fill to each surface's own color so they aren't all wallFill.
-	_gfx->setWireframe(true, (int)floorColor);
+	_gfx->setWireframe(true, floorColor);
 	_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.1f,
 	                100000.0f, -100000.0f, -160.1f,
 	                100000.0f, 100000.0f, -160.1f,
 	                -100000.0f, 100000.0f, -160.1f, floorColor);
 
-	_gfx->setWireframe(true, (int)ceilColor);
+	_gfx->setWireframe(true, ceilColor);
 	_gfx->draw3DQuad(-100000.0f, -100000.0f, 160.1f,
 	                100000.0f, -100000.0f, 160.1f,
 	                100000.0f, 100000.0f, 160.1f,
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index b8629c9fa22..60bdb72ce72 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -59,7 +59,7 @@ public:
 
 	// Buffer management
 	virtual void copyToScreen() = 0;
-	virtual void setWireframe(bool enable, int fillColor = -1) = 0;
+	virtual void setWireframe(bool enable, int64_t fillColor = -1) = 0;
 	virtual void setStippleData(const byte *data) {}
 	virtual void setMacColors(uint32 fg, uint32 bg) {}
 	virtual void computeScreenViewport() = 0;
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 0310d20be2b..c2ff109830d 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -59,7 +59,7 @@ public:
 	void drawQuad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, uint32 color) override;
 	void drawPolygon(const int *x, const int *y, int count, uint32 color) override;
 	void copyToScreen() override;
-	void setWireframe(bool enable, int fillColor = -1) override {
+	void setWireframe(bool enable, int64_t fillColor = -1) override {
 		_wireframe = enable;
 		_wireframeFillColor = fillColor;
 	}
@@ -82,7 +82,7 @@ private:
 	int _height;
 	byte _palette[256 * 3];
 	bool _wireframe;
-	int _wireframeFillColor; // -1 = no fill (outline only)
+	int64_t _wireframeFillColor; // -1 = no fill (outline only)
 	const byte *_stippleData; // GL_POLYGON_STIPPLE pattern (128 bytes), null = disabled
 	uint32 _stippleFgColor;
 	uint32 _stippleBgColor;
@@ -643,8 +643,11 @@ void OpenGLRenderer::drawSurface(const Graphics::Surface *surf, int x, int y) {
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+	// The surface uses PixelFormat(4,8,8,8,8,24,16,8,0) = R at bit 24, A at bit 0.
+	// GL_UNSIGNED_INT_8_8_8_8 reads a uint32 and maps bits 24..31→R, 16..23→G,
+	// 8..15→B, 0..7→A, matching our pixel layout regardless of endianness.
 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0,
-	             GL_RGBA, GL_UNSIGNED_BYTE, surf->getPixels());
+	             GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, surf->getPixels());
 
 	// Draw textured quad covering the specified region
 	float dx = x * scaleX;


Commit: 7927bb2c9bae73bff574f7e6051f51fa7b249d4d
    https://github.com/scummvm/scummvm/commit/7927bb2c9bae73bff574f7e6051f51fa7b249d4d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:26+02:00

Commit Message:
COLONY: add Mac color dashboard with PICT-based power/compass panels

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 9ea1bdcd968..1312f25ec81 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -148,6 +148,9 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 
 ColonyEngine::~ColonyEngine() {
 	deleteAnimation();
+	if (_pictPower) { _pictPower->free(); delete _pictPower; }
+	if (_pictPowerNoArmor) { _pictPowerNoArmor->free(); delete _pictPowerNoArmor; }
+	if (_pictCompass) { _pictCompass->free(); delete _pictCompass; }
 	delete _frameLimiter;
 	delete _gfx;
 	delete _sound;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 46805295f66..3141d839982 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -418,6 +418,14 @@ private:
 	Common::Rect _headsUpRect;
 	Common::Rect _powerRect;
 
+	// Cached decoded PICT surfaces for dashboard panels (Mac color mode)
+	Graphics::Surface *_pictPower = nullptr;      // PICT -32755 (normal) or -32760 (trouble)
+	Graphics::Surface *_pictPowerNoArmor = nullptr; // PICT -32761 (no armor, color)
+	Graphics::Surface *_pictCompass = nullptr;     // PICT -32757
+	int _pictPowerID = 0;   // Track which PICT is cached
+	Graphics::Surface *loadPictSurface(int resID);
+	void drawPictAt(Graphics::Surface *surf, int destX, int destY);
+
 	uint8 wallAt(int x, int y) const;
 	const uint8 *mapFeatureAt(int x, int y, int direction) const;
 	bool _visibleCell[32][32];
@@ -464,6 +472,8 @@ private:
 	int tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject);
 	void updateViewportLayout();
 	void drawDashboardStep1();
+	void drawDashboardMac();
+	void drawMiniMap(uint32 lineColor);
 	void drawCrosshair();
 	bool clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const;
 	void wallLine(const float corners[4][3], float u1, float v1, float u2, float v2, uint32 color);
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 04b1bb55fd6..5a55af919d2 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -2407,14 +2407,19 @@ void ColonyEngine::renderCorridor3D() {
 	// Walls always use wireframe with fill (opaque walls).
 	_gfx->setWireframe(true, wallFill);
  
-	// Draw ceiling grid (Cuadricule) - Historically only on ceiling
-	for (int i = 0; i <= 32; i++) {
-		float d = i * 256.0f;
-		float maxD = 32.0f * 256.0f;
-		float zCeil = 160.0f;
-		
-		_gfx->draw3DLine(d, 0.0f, zCeil, d, maxD, zCeil, wallColor);
-		_gfx->draw3DLine(0.0f, d, zCeil, maxD, d, zCeil, wallColor);
+	// Draw ceiling grid (Cuadricule) - DOS wireframe mode only.
+	// 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)) {
+		for (int i = 0; i <= 32; i++) {
+			float d = i * 256.0f;
+			float maxD = 32.0f * 256.0f;
+			float zCeil = 160.0f;
+
+			_gfx->draw3DLine(d, 0.0f, zCeil, d, maxD, zCeil, wallColor);
+			_gfx->draw3DLine(0.0f, d, zCeil, maxD, d, zCeil, wallColor);
+		}
 	}
 
 	for (int y = 0; y < 32; y++) {
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 9cd110243dc..09dc261969a 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -28,10 +28,89 @@
 #include "common/events.h"
 #include "graphics/palette.h"
 #include "graphics/fonts/dosfont.h"
+#include "image/pict.h"
 #include <math.h>
 
 namespace Colony {
 
+// Pack RGB into 32-bit ARGB with 0xFF000000 marker for direct RGB rendering.
+static uint32 packRGB(byte r, byte g, byte b) {
+	return 0xFF000000 | ((uint32)r << 16) | ((uint32)g << 8) | b;
+}
+
+// Pack Mac 16-bit RGB into 32-bit ARGB.
+static uint32 packMacColorUI(const uint16 rgb[3]) {
+	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
+}
+
+// Load a PICT resource from the Mac resource fork, returning a new RGB surface.
+// Caller owns the returned surface. Returns nullptr on failure.
+Graphics::Surface *ColonyEngine::loadPictSurface(int resID) {
+	if (!_resMan || !(_resMan->isMacFile() || _resMan->hasResFork()))
+		return nullptr;
+
+	Common::SeekableReadStream *pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
+	if (!pictStream) {
+		pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), resID);
+		if (!pictStream)
+			return nullptr;
+	}
+
+	::Image::PICTDecoder decoder;
+	Graphics::Surface *result = nullptr;
+	if (decoder.loadStream(*pictStream)) {
+		const Graphics::Surface *src = decoder.getSurface();
+		if (src) {
+			// Convert to a persistent RGB surface (decoder surface is transient)
+			result = new Graphics::Surface();
+			result->create(src->w, src->h, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+			for (int y = 0; y < src->h; y++) {
+				for (int x = 0; x < src->w; x++) {
+					byte r, g, b;
+					if (src->format == Graphics::PixelFormat::createFormatCLUT8()) {
+						// For CLUT8, use the decoder's palette
+						byte idx = *((const byte *)src->getBasePtr(x, y));
+						const Graphics::Palette &pal = decoder.getPalette();
+						if (idx < (int)pal.size()) {
+							pal.get(idx, r, g, b);
+						} else {
+							r = g = b = 0;
+						}
+					} else {
+						uint32 pixel = src->getPixel(x, y);
+						src->format.colorToRGB(pixel, r, g, b);
+					}
+					result->setPixel(x, y, result->format.ARGBToColor(255, r, g, b));
+				}
+			}
+			debug("loadPictSurface(%d): %dx%d", resID, result->w, result->h);
+		}
+	}
+	delete pictStream;
+	return result;
+}
+
+// Draw a PICT surface at a specific destination position using packRGB.
+// Matches original DrawPicture(pic, &rect) where rect is positioned at (destX, destY).
+void ColonyEngine::drawPictAt(Graphics::Surface *surf, int destX, int destY) {
+	if (!surf)
+		return;
+	for (int y = 0; y < surf->h; y++) {
+		int sy = destY + y;
+		if (sy < 0 || sy >= _height)
+			continue;
+		for (int x = 0; x < surf->w; x++) {
+			int sx = destX + x;
+			if (sx < 0 || sx >= _width)
+				continue;
+			uint32 pixel = surf->getPixel(x, y);
+			byte a, r, g, b;
+			surf->format.colorToARGB(pixel, a, r, g, b);
+			_gfx->setPixel(sx, sy, packRGB(r, g, b));
+		}
+	}
+}
+
 void ColonyEngine::updateViewportLayout() {
 	auto makeSafeRect = [](int left, int top, int right, int bottom) {
 		if (right < left)
@@ -43,7 +122,13 @@ void ColonyEngine::updateViewportLayout() {
 
 	int dashWidth = 0;
 	if (_showDashBoard) {
-		dashWidth = CLIP<int>(_width / 6, 72, 140);
+		// Mac color mode: original moveWindow was 70px wide (2*CCENTER),
+		// infoWindow ~72px. theWindow started at x=96. Use tighter sizing.
+		const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+		if (macColor)
+			dashWidth = CLIP<int>(_width / 7, 70, 96);
+		else
+			dashWidth = CLIP<int>(_width / 6, 72, 140);
 		if (_width - dashWidth < 160)
 			dashWidth = 0;
 	}
@@ -63,38 +148,88 @@ void ColonyEngine::updateViewportLayout() {
 		return;
 	}
 
+	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
 	const int pad = 2;
-	const int topPad = menuTop + pad; // Dashboard sub-panels start below menu bar
-	const int unit = MAX(8, (dashWidth - (pad * 2)) / 4);
-	const int blockLeft = pad;
-	const int blockRight = MIN(dashWidth - pad, blockLeft + unit * 4);
+	const int topPad = menuTop + pad;
+
+	if (macColor) {
+		// Original Mac layout: two separate floating windows in a 96px-wide sidebar.
+		// screenR.left = 96. Both windows ~70px wide (2*CCENTER), centered in sidebar.
+		// moveWindow: compRect = (0,0, 70, 105) — compass+map panel
+		// infoWindow: sized from PICT resource, positioned above moveWindow
+		// Visible Mac gray desktop between and around windows.
+
+		// Load PICT surfaces (cached after first load).
+		// Available PICTs: -32755 (power, armor normal), -32757 (compass).
+		// Color Colony variants (-32760, -32761) may not exist — fall back to -32755.
+		if (!_pictCompass)
+			_pictCompass = loadPictSurface(-32757);
+		if (!_pictPower) {
+			int wantID = _armor ? -32755 : -32761;
+			_pictPower = loadPictSurface(wantID);
+			if (!_pictPower && wantID != -32755)
+				_pictPower = loadPictSurface(-32755); // fall back to normal
+			_pictPowerID = _pictPower ? wantID : 0;
+		}
 
-	const int compassBottom = _height - MAX(2, unit / 4);
-	const int compassTop = MAX(topPad, compassBottom - unit * 4);
-	_compassRect = makeSafeRect(blockLeft, compassTop, blockRight, compassBottom);
+		// Use PICT dimensions for panel sizes, fall back to original constants
+		const int moveW = _pictCompass ? _pictCompass->w : 70;  // 2*CCENTER
+		const int moveH = _pictCompass ? _pictCompass->h : 105; // 3*CCENTER
+		const int infoW = _pictPower ? _pictPower->w : moveW;
+		const int infoH = _pictPower ? _pictPower->h : 200;
+
+		// Center panels horizontally in the sidebar (original windows were centered)
+		const int centerX = dashWidth / 2;
+
+		// Position moveWindow at the bottom of the sidebar
+		const int moveLeft = MAX(0, centerX - moveW / 2);
+		const int moveTop = _height - pad - moveH;
+		// compRect split: floorRect = (8,8)-(62,62), compass dish area below
+		const int floorBottom = moveTop + moveH * 62 / 105;
+		_headsUpRect = makeSafeRect(moveLeft, moveTop, moveLeft + moveW, floorBottom);
+		_compassRect = makeSafeRect(moveLeft, floorBottom, moveLeft + moveW, moveTop + moveH);
+
+		// Position infoWindow above moveWindow with a gap
+		const int infoLeft = MAX(0, centerX - infoW / 2);
+		const int infoTop = MAX(topPad, moveTop - pad - infoH);
+		_powerRect = makeSafeRect(infoLeft, infoTop, infoLeft + infoW, infoTop + infoH);
+	} else {
+		const int blockLeft = pad;
+		const int blockRight = dashWidth - pad;
+		const int unit = MAX(8, (dashWidth - (pad * 2)) / 4);
 
-	const int headsUpBottom = _compassRect.top - MAX(2, unit / 4) - 4;
-	const int headsUpTop = headsUpBottom - unit * 4;
-	_headsUpRect = makeSafeRect(blockLeft, MAX(topPad, headsUpTop), blockRight, MAX(topPad, headsUpBottom));
+		const int compassBottom = _height - MAX(2, unit / 4);
+		const int compassTop = MAX(topPad, compassBottom - unit * 4);
+		_compassRect = makeSafeRect(blockLeft, compassTop, blockRight, compassBottom);
 
-	_powerRect = makeSafeRect(blockLeft, topPad, blockRight, _headsUpRect.top - 4);
+		const int headsUpBottom = _compassRect.top - MAX(2, unit / 4) - 4;
+		const int headsUpTop = headsUpBottom - unit * 4;
+		_headsUpRect = makeSafeRect(blockLeft, MAX(topPad, headsUpTop), blockRight, MAX(topPad, headsUpBottom));
+
+		_powerRect = makeSafeRect(blockLeft, topPad, blockRight, _headsUpRect.top - 4);
+	}
 }
 
 void ColonyEngine::drawDashboardStep1() {
 	if (_dashBoardRect.width() <= 0 || _dashBoardRect.height() <= 0)
 		return;
 
-	const int kFWALLType = 48;
+	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+
+	if (macColor) {
+		drawDashboardMac();
+		return;
+	}
 
-	_gfx->fillRect(_dashBoardRect, 0); // Black background underlying
-	_gfx->fillDitherRect(_dashBoardRect, 0, 15); // Dithered 50% gray background (Black/White)
-	_gfx->drawRect(_dashBoardRect, 1); // Blue outer frame
+	// --- DOS/EGA path (unchanged) ---
+	_gfx->fillRect(_dashBoardRect, 0);
+	_gfx->fillDitherRect(_dashBoardRect, 0, 15);
+	_gfx->drawRect(_dashBoardRect, 1);
 
 	const int shiftX = MAX(1, _dashBoardRect.width() / 8);
 	const int shiftY = MAX(1, _dashBoardRect.width() / 8);
 	const Common::Rect r = _dashBoardRect;
 
-	// Bevel lines
 	_gfx->drawLine(r.left, r.top, r.left + shiftX, r.top + shiftY, 0);
 	_gfx->drawLine(r.right - 1, r.top, r.right - 1 - shiftX, r.top + shiftY, 0);
 	_gfx->drawLine(r.left, r.bottom - 1, r.left + shiftX, r.bottom - 1 - shiftY, 0);
@@ -110,12 +245,12 @@ void ColonyEngine::drawDashboardStep1() {
 		const int rx = MAX(2, (_compassRect.width() - 6) >> 1);
 		const int ry = MAX(2, (_compassRect.height() - 6) >> 1);
 
-		_gfx->fillEllipse(cx, cy, rx, ry, 15); // White background
-		_gfx->drawEllipse(cx, cy, rx, ry, 1);  // Blue frame
+		_gfx->fillEllipse(cx, cy, rx, ry, 15);
+		_gfx->drawEllipse(cx, cy, rx, ry, 1);
 
 		const int ex = cx + ((_cost[_me.look] * rx) >> 8);
 		const int ey = cy - ((_sint[_me.look] * ry) >> 8);
-		_gfx->drawLine(cx, cy, ex, ey, 0); // Black mark
+		_gfx->drawLine(cx, cy, ex, ey, 0);
 		_gfx->drawLine(cx - 2, cy, cx + 2, cy, 0);
 		_gfx->drawLine(cx, cy - 2, cx, cy + 2, 0);
 	}
@@ -124,50 +259,203 @@ void ColonyEngine::drawDashboardStep1() {
 		_gfx->drawLine(_screenR.left - 1, _screenR.top, _screenR.left - 1, _screenR.bottom - 1, 15);
 
 	if (_headsUpRect.width() > 4 && _headsUpRect.height() > 4) {
-		_gfx->fillRect(_headsUpRect, 15); // White background
-		_gfx->drawRect(_headsUpRect, 1); // Blue frame
-
-		const Common::Rect miniMapClip(_headsUpRect.left + 1, _headsUpRect.top + 1, _headsUpRect.right - 1, _headsUpRect.bottom - 1);
-		auto drawMiniMapLine = [&](int x1, int y1, int x2, int y2, uint32 color) {
-			if (clipLineToRect(x1, y1, x2, y2, miniMapClip))
-				_gfx->drawLine(x1, y1, x2, y2, color);
-		};
-
-		const int lExtBase = _dashBoardRect.width() >> 1;
-		int lExt = lExtBase + (lExtBase >> 1);
-		if (lExt & 1)
-			lExt--;
-		const int sExt = lExt >> 1;
-		const int xloc = (lExt * ((_me.xindex << 8) - _me.xloc)) >> 8;
-		const int yloc = (lExt * ((_me.yindex << 8) - _me.yloc)) >> 8;
-		const int ccenterx = (_headsUpRect.left + _headsUpRect.right) >> 1;
-		const int ccentery = (_headsUpRect.top + _headsUpRect.bottom) >> 1;
-		const int tsin = _sint[_me.look];
-		const int tcos = _cost[_me.look];
-
-		int xcorner[6];
-		int ycorner[6];
-		xcorner[0] = ccenterx + (((long)xloc * tsin - (long)yloc * tcos) >> 8);
-		ycorner[0] = ccentery - (((long)yloc * tsin + (long)xloc * tcos) >> 8);
-		xcorner[1] = ccenterx + (((long)(xloc + lExt) * tsin - (long)yloc * tcos) >> 8);
-		ycorner[1] = ccentery - (((long)yloc * tsin + (long)(xloc + lExt) * tcos) >> 8);
-		xcorner[2] = ccenterx + (((long)(xloc + lExt) * tsin - (long)(yloc + lExt) * tcos) >> 8);
-		ycorner[2] = ccentery - (((long)(yloc + lExt) * tsin + (long)(xloc + lExt) * tcos) >> 8);
-		xcorner[3] = ccenterx + (((long)xloc * tsin - (long)(yloc + lExt) * tcos) >> 8);
-		ycorner[3] = ccentery - (((long)(yloc + lExt) * tsin + (long)xloc * tcos) >> 8);
-		xcorner[4] = ccenterx + (((long)(xloc + sExt) * tsin - (long)(yloc + sExt) * tcos) >> 8);
-		ycorner[4] = ccentery - (((long)(yloc + sExt) * tsin + (long)(xloc + sExt) * tcos) >> 8);
-		xcorner[5] = ccenterx + (((long)(xloc + sExt) * tsin - (long)yloc * tcos) >> 8);
-		ycorner[5] = ccentery - (((long)yloc * tsin + (long)(xloc + sExt) * tcos) >> 8);
-
-		const int dx = xcorner[1] - xcorner[0];
-		const int dy = ycorner[0] - ycorner[1];
-		drawMiniMapLine(xcorner[0] - dx, ycorner[0] + dy, xcorner[1] + dx, ycorner[1] - dy, 0); // Black for walls
-		drawMiniMapLine(xcorner[1] + dy, ycorner[1] + dx, xcorner[2] - dy, ycorner[2] - dx, 0);
-		drawMiniMapLine(xcorner[2] + dx, ycorner[2] - dy, xcorner[3] - dx, ycorner[3] + dy, 0);
-		drawMiniMapLine(xcorner[3] - dy, ycorner[3] - dx, xcorner[0] + dy, ycorner[0] + dx, 0);
-
-		auto drawMarker = [&](int x, int y, int halfSize, uint32 color) {
+		_gfx->fillRect(_headsUpRect, 15);
+		_gfx->drawRect(_headsUpRect, 1);
+		drawMiniMap(0);
+	}
+
+	if (_powerRect.width() > 4 && _powerRect.height() > 4) {
+		_gfx->fillRect(_powerRect, 15);
+		_gfx->drawRect(_powerRect, 1);
+		const int barY = _powerRect.bottom - MAX(3, _powerRect.width() / 8);
+		_gfx->drawLine(_powerRect.left + 1, barY, _powerRect.right - 2, barY, 0);
+	}
+}
+
+// --- Mac Color path ---
+// Uses actual PICT resources from the Mac resource fork as panel backgrounds,
+// matching the original power.c DrawInfo() and compass.c DrawCompass() exactly.
+// Original Mac had two floating windows (infoWindow + moveWindow) over gray desktop.
+
+void ColonyEngine::drawDashboardMac() {
+	const uint32 colBlack = packRGB(0, 0, 0);
+	const uint32 colWhite = packRGB(255, 255, 255);
+	const uint32 colWinBg = packMacColorUI(_macColors[7].bg); // Mac desktop gray
+	const uint32 colBlue = packRGB(0, 0, 255); // power.c: ForeColor(blueColor)
+
+	// Dashboard background — Mac desktop gray, visible between floating windows
+	_gfx->fillRect(_dashBoardRect, colWinBg);
+
+	// Viewport separator
+	if (_screenR.left > 0)
+		_gfx->drawLine(_screenR.left - 1, _screenR.top, _screenR.left - 1, _screenR.bottom - 1, colBlack);
+
+	// ===== Power panel (infoWindow) =====
+	// power.c DrawInfo(): FillRect(&clr,white) → DrawPicture(PICT) → ForeColor(blue) → bars
+	// armor && !trouble → PICT -32755; armor && trouble → PICT -32760;
+	// !armor && depth>=8 → PICT -32761; !armor && depth<8 → PICT -32752
+	if (_powerRect.width() > 4 && _powerRect.height() > 4) {
+		// power.c: FillRect(&clr, white) — white background under PICT
+		_gfx->fillRect(_powerRect, colWhite);
+
+		// Select correct PICT based on armor/trouble state
+		auto qlog = [](int32 x) -> int { int i = 0; while (x > 0) { x >>= 1; i++; } return i; };
+		const int ePower[3] = { qlog(_corePower[0]), qlog(_corePower[1]), qlog(_corePower[2]) };
+		const bool trouble = (ePower[1] < 6);
+		int wantPictID;
+		if (_armor > 0)
+			wantPictID = trouble ? -32760 : -32755;
+		else
+			wantPictID = -32761;
+
+		// Reload PICT if state changed (fall back to -32755 if variant missing)
+		if (_pictPowerID != wantPictID) {
+			if (_pictPower) { _pictPower->free(); delete _pictPower; _pictPower = nullptr; }
+			_pictPower = loadPictSurface(wantPictID);
+			if (!_pictPower && wantPictID != -32755)
+				_pictPower = loadPictSurface(-32755);
+			_pictPowerID = wantPictID;
+		}
+
+		// power.c: SetRect(&info, -2, -2, xSize-2, ySize-2); DrawPicture(inf, &info)
+		if (_pictPower)
+			drawPictAt(_pictPower, _powerRect.left - 2, _powerRect.top - 2);
+
+		// 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
+
+			for (int i = 0; i < 3; i++) {
+				// power.c: lft = 3 + info.left + i*23
+				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);
+				}
+			}
+		}
+	}
+
+	// ===== Compass + Floor map (moveWindow) =====
+	// compass.c DrawCompass(): DrawPicture(PICT -32757, &compRect) then patXor drawings
+	// compRect = (0,0, 2*CCENTER, 3*CCENTER) = (0,0, 70, 105)
+	const Common::Rect moveRect(_headsUpRect.left, _headsUpRect.top,
+	                            _compassRect.right, _compassRect.bottom);
+	if (moveRect.width() > 4 && moveRect.height() > 4) {
+		// compass.c: SetRect(&compRect, -2, -2, xSize-2, ySize-2); DrawPicture(comp, &compRect)
+		if (_pictCompass)
+			drawPictAt(_pictCompass, moveRect.left - 2, moveRect.top - 2);
+		else
+			_gfx->fillRect(moveRect, colWinBg);
+
+		// compass.c: compRect reset to (0,0, 70, 105)
+		// PenMode(patXor) — all subsequent drawing XORs onto the PICT background
+
+		// Compass dish (compass.c lines 59-70):
+		// r.top=2*CCENTER-4=66; r.bottom=2*CCENTER+28=98;
+		// r.left=CCENTER-16=19; r.right=CCENTER+16=51;
+		// FillOval(&r, black) with patXor
+		// Needle from (CCENTER, 2*CCENTER+12) = (35, 82) using cost[ang]>>3
+		{
+			const int ox = moveRect.left; // origin offset
+			const int oy = moveRect.top;
+			// Use absolute coordinates from original: dish at (35, 82), oval 19-51, 66-98
+			const int dishCX = ox + 35;  // CCENTER
+			const int dishCY = oy + 82;  // 2*CCENTER+12
+
+			// FillOval at (19,66)-(51,98) with patXor black — inverts PICT background
+			_gfx->fillEllipse(dishCX, dishCY, 16, 16, colBlack);
+
+			// Needle: compass.c line 69-70
+			// LineTo(CCENTER+(cost[ang]>>3), (2*CCENTER+12)-(sint[ang]>>3))
+			const int ex = dishCX + (_cost[_me.look] >> 3);
+			const int ey = dishCY - (_sint[_me.look] >> 3);
+			_gfx->drawLine(dishCX, dishCY, ex, ey, colWhite);
+		}
+
+		// Floor map (compass.c lines 72-213):
+		// floorRect = (7,7)-(63,63) → ++/-- → (8,8)-(62,62) = 54x54 inner map area
+		// ClipRect(&floorRect) for all map drawing
+		// Eye icon at (CCENTER-10,CCENTER-5)-(CCENTER+10,CCENTER+5) = (25,30)-(45,40)
+		// Pupil at (CCENTER-5,CCENTER-5)-(CCENTER+5,CCENTER+5) = (30,30)-(40,40)
+		{
+			drawMiniMap(colBlack);
+
+			// Eye icon (compass.c lines 84-88) — drawn with patXor
+			const int ox = moveRect.left;
+			const int oy = moveRect.top;
+			_gfx->drawEllipse(ox + 35, oy + 35, 10, 5, colBlack);  // FrameOval outer eye
+			_gfx->fillEllipse(ox + 35, oy + 35, 5, 5, colBlack);   // FillOval pupil
+		}
+	}
+}
+
+// Draws the mini floor map into _headsUpRect (shared by Mac and DOS paths)
+void ColonyEngine::drawMiniMap(uint32 lineColor) {
+	const int kFWALLType = 48;
+	const Common::Rect miniMapClip(_headsUpRect.left + 1, _headsUpRect.top + 1, _headsUpRect.right - 1, _headsUpRect.bottom - 1);
+	auto drawMiniMapLine = [&](int x1, int y1, int x2, int y2, uint32 color) {
+		if (clipLineToRect(x1, y1, x2, y2, miniMapClip))
+			_gfx->drawLine(x1, y1, x2, y2, color);
+	};
+
+	const int lExtBase = _dashBoardRect.width() >> 1;
+	int lExt = lExtBase + (lExtBase >> 1);
+	if (lExt & 1)
+		lExt--;
+	const int sExt = lExt >> 1;
+	const int xloc = (lExt * ((_me.xindex << 8) - _me.xloc)) >> 8;
+	const int yloc = (lExt * ((_me.yindex << 8) - _me.yloc)) >> 8;
+	const int ccenterx = (_headsUpRect.left + _headsUpRect.right) >> 1;
+	const int ccentery = (_headsUpRect.top + _headsUpRect.bottom) >> 1;
+	const int tsin = _sint[_me.look];
+	const int tcos = _cost[_me.look];
+
+	int xcorner[6];
+	int ycorner[6];
+	xcorner[0] = ccenterx + (((long)xloc * tsin - (long)yloc * tcos) >> 8);
+	ycorner[0] = ccentery - (((long)yloc * tsin + (long)xloc * tcos) >> 8);
+	xcorner[1] = ccenterx + (((long)(xloc + lExt) * tsin - (long)yloc * tcos) >> 8);
+	ycorner[1] = ccentery - (((long)yloc * tsin + (long)(xloc + lExt) * tcos) >> 8);
+	xcorner[2] = ccenterx + (((long)(xloc + lExt) * tsin - (long)(yloc + lExt) * tcos) >> 8);
+	ycorner[2] = ccentery - (((long)(yloc + lExt) * tsin + (long)(xloc + lExt) * tcos) >> 8);
+	xcorner[3] = ccenterx + (((long)xloc * tsin - (long)(yloc + lExt) * tcos) >> 8);
+	ycorner[3] = ccentery - (((long)(yloc + lExt) * tsin + (long)xloc * tcos) >> 8);
+	xcorner[4] = ccenterx + (((long)(xloc + sExt) * tsin - (long)(yloc + sExt) * tcos) >> 8);
+	ycorner[4] = ccentery - (((long)(yloc + sExt) * tsin + (long)(xloc + sExt) * tcos) >> 8);
+	xcorner[5] = ccenterx + (((long)(xloc + sExt) * tsin - (long)yloc * tcos) >> 8);
+	ycorner[5] = ccentery - (((long)yloc * tsin + (long)(xloc + sExt) * tcos) >> 8);
+
+	const int dx = xcorner[1] - xcorner[0];
+	const int dy = ycorner[0] - ycorner[1];
+	drawMiniMapLine(xcorner[0] - dx, ycorner[0] + dy, xcorner[1] + dx, ycorner[1] - dy, lineColor);
+	drawMiniMapLine(xcorner[1] + dy, ycorner[1] + dx, xcorner[2] - dy, ycorner[2] - dx, lineColor);
+	drawMiniMapLine(xcorner[2] + dx, ycorner[2] - dy, xcorner[3] - dx, ycorner[3] + dy, lineColor);
+	drawMiniMapLine(xcorner[3] - dy, ycorner[3] - dx, xcorner[0] + dy, ycorner[0] + dx, lineColor);
+
+	// compass.c: food markers use FrameOval ±3px, robot markers ±5px.
+	// Scale proportionally to panel width (original floorRect was 54px wide).
+	const bool macMarkers = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const int foodR = macMarkers ? MAX(2, _headsUpRect.width() / 18) : 1;   // ~3px at 54px
+	const int robotR = macMarkers ? MAX(3, _headsUpRect.width() / 11) : 2;  // ~5px at 54px
+
+	auto drawMarker = [&](int x, int y, int halfSize, uint32 color) {
+		if (x < _headsUpRect.left + 1 || x >= _headsUpRect.right - 1 ||
+		    y < _headsUpRect.top + 1 || y >= _headsUpRect.bottom - 1)
+			return;
+		if (macMarkers) {
+			// compass.c: FrameOval — circle outline
+			_gfx->drawEllipse(x, y, halfSize, halfSize, color);
+		} else {
 			const int l = MAX<int>(_headsUpRect.left + 1, x - halfSize);
 			const int t = MAX<int>(_headsUpRect.top + 1, y - halfSize);
 			const int r = MIN<int>(_headsUpRect.right - 1, x + halfSize + 1);
@@ -175,64 +463,51 @@ void ColonyEngine::drawDashboardStep1() {
 			if (l >= r || t >= b)
 				return;
 			_gfx->drawRect(Common::Rect(l, t, r, b), color);
-		};
-
-		auto hasRobotAt = [&](int x, int y) -> bool {
-			if (x < 0 || x >= 32 || y < 0 || y >= 32)
-				return false;
-			return _robotArray[x][y] != 0;
-		};
-		auto hasFoodAt = [&](int x, int y) -> bool {
-			if (x < 0 || x >= 32 || y < 0 || y >= 32)
-				return false;
-			const uint8 num = _foodArray[x][y];
-			if (num == 0)
-				return false;
-			if (num <= _objects.size())
-				return _objects[num - 1].type < kFWALLType;
-			return true;
-		};
+		}
+	};
 
-		if (hasFoodAt(_me.xindex, _me.yindex))
-			drawMarker(xcorner[4], ycorner[4], 1, 0);
+	auto hasRobotAt = [&](int x, int y) -> bool {
+		if (x < 0 || x >= 32 || y < 0 || y >= 32)
+			return false;
+		return _robotArray[x][y] != 0;
+	};
+	auto hasFoodAt = [&](int x, int y) -> bool {
+		if (x < 0 || x >= 32 || y < 0 || y >= 32)
+			return false;
+		const uint8 num = _foodArray[x][y];
+		if (num == 0)
+			return false;
+		if (num <= _objects.size())
+			return _objects[num - 1].type < kFWALLType;
+		return true;
+	};
 
-		if (_me.yindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x01)) {
-			if (hasFoodAt(_me.xindex, _me.yindex - 1))
-				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 1, 0);
-			if (hasRobotAt(_me.xindex, _me.yindex - 1))
-				drawMarker(xcorner[4] + dy, ycorner[4] + dx, 2, 0);
-		}
-		if (_me.xindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x02)) {
-			if (hasFoodAt(_me.xindex - 1, _me.yindex))
-				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 1, 0);
-			if (hasRobotAt(_me.xindex - 1, _me.yindex))
-				drawMarker(xcorner[4] - dx, ycorner[4] + dy, 2, 0);
-		}
-		if (_me.yindex < 30 && !(_wall[_me.xindex][_me.yindex + 1] & 0x01)) {
-			if (hasFoodAt(_me.xindex, _me.yindex + 1))
-				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 1, 0);
-			if (hasRobotAt(_me.xindex, _me.yindex + 1))
-				drawMarker(xcorner[4] - dy, ycorner[4] - dx, 2, 0);
-		}
-		if (_me.xindex < 30 && !(_wall[_me.xindex + 1][_me.yindex] & 0x02)) {
-			if (hasFoodAt(_me.xindex + 1, _me.yindex))
-				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 1, 0);
-			if (hasRobotAt(_me.xindex + 1, _me.yindex))
-				drawMarker(xcorner[4] + dx, ycorner[4] - dy, 2, 0);
-		}
+	if (hasFoodAt(_me.xindex, _me.yindex))
+		drawMarker(xcorner[4], ycorner[4], foodR, lineColor);
 
-		// Eye icon in the center
-		const int px = MAX(4, _dashBoardRect.width() / 10);
-		const int py = MAX(2, _dashBoardRect.width() / 24);
-		_gfx->drawEllipse(ccenterx, ccentery, px, py, 0); // Outer frame
-		_gfx->fillEllipse(ccenterx, ccentery, px / 2, py, 0); // Inner pupil
+	if (_me.yindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x01)) {
+		if (hasFoodAt(_me.xindex, _me.yindex - 1))
+			drawMarker(xcorner[4] + dy, ycorner[4] + dx, foodR, lineColor);
+		if (hasRobotAt(_me.xindex, _me.yindex - 1))
+			drawMarker(xcorner[4] + dy, ycorner[4] + dx, robotR, lineColor);
 	}
-
-	if (_powerRect.width() > 4 && _powerRect.height() > 4) {
-		_gfx->fillRect(_powerRect, 15); // White background
-		_gfx->drawRect(_powerRect, 1); // Blue frame
-		const int barY = _powerRect.bottom - MAX(3, _powerRect.width() / 8);
-		_gfx->drawLine(_powerRect.left + 1, barY, _powerRect.right - 2, barY, 0); // Black divider
+	if (_me.xindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x02)) {
+		if (hasFoodAt(_me.xindex - 1, _me.yindex))
+			drawMarker(xcorner[4] - dx, ycorner[4] + dy, foodR, lineColor);
+		if (hasRobotAt(_me.xindex - 1, _me.yindex))
+			drawMarker(xcorner[4] - dx, ycorner[4] + dy, robotR, lineColor);
+	}
+	if (_me.yindex < 30 && !(_wall[_me.xindex][_me.yindex + 1] & 0x01)) {
+		if (hasFoodAt(_me.xindex, _me.yindex + 1))
+			drawMarker(xcorner[4] - dy, ycorner[4] - dx, foodR, lineColor);
+		if (hasRobotAt(_me.xindex, _me.yindex + 1))
+			drawMarker(xcorner[4] - dy, ycorner[4] - dx, robotR, lineColor);
+	}
+	if (_me.xindex < 30 && !(_wall[_me.xindex + 1][_me.yindex] & 0x02)) {
+		if (hasFoodAt(_me.xindex + 1, _me.yindex))
+			drawMarker(xcorner[4] + dx, ycorner[4] - dy, foodR, lineColor);
+		if (hasRobotAt(_me.xindex + 1, _me.yindex))
+			drawMarker(xcorner[4] + dx, ycorner[4] - dy, robotR, lineColor);
 	}
 }
 
@@ -240,9 +515,17 @@ void ColonyEngine::drawCrosshair() {
 	if (!_crosshair || _screenR.width() <= 0 || _screenR.height() <= 0)
 		return;
 
-	uint32 color = (_weapons > 0) ? 15 : 7;
-	if (_corePower[_coreIndex] > 0)
-		color = 0;
+	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	uint32 color;
+	if (macColor) {
+		// Mac: black when powered, gray when no weapons, white when armed but no power
+		color = (_corePower[_coreIndex] > 0) ? packRGB(0, 0, 0)
+			: (_weapons > 0) ? packRGB(255, 255, 255) : packRGB(128, 128, 128);
+	} else {
+		color = (_weapons > 0) ? 15 : 7;
+		if (_corePower[_coreIndex] > 0)
+			color = 0;
+	}
 
 	const int cx = _centerX;
 	const int cy = _centerY;


Commit: cb4fbc4242c30a2c8f80c140e8a859736692c088
    https://github.com/scummvm/scummvm/commit/cb4fbc4242c30a2c8f80c140e8a859736692c088
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:26+02:00

Commit Message:
COLONY: load color images

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 1312f25ec81..db271d62568 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -142,6 +142,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 
 	_sound = new Sound(this);
 	_resMan = new Common::MacResManager();
+	_colorResMan = new Common::MacResManager();
 	initTrig();
 }
 
@@ -154,6 +155,7 @@ ColonyEngine::~ColonyEngine() {
 	delete _frameLimiter;
 	delete _gfx;
 	delete _sound;
+	delete _colorResMan;
 	delete _resMan;
 	delete _menuSurface;
 	delete _wm;
@@ -663,6 +665,10 @@ Common::Error ColonyEngine::run() {
 				warning("Failed to open Colony resource fork");
 			}
 		}
+		// Try to open Color Colony for additional color PICT resources
+		if (!_colorResMan->open("(Color) Colony")) {
+			debug("Color Colony resource fork not found (optional)");
+		}
 		_sound->init();
 	}
 
@@ -1554,16 +1560,19 @@ bool ColonyEngine::drawPict(int resID) {
 	// Original: DoPicture() in intro.c, lines 861-886
 	// Loads a PICT resource, centers it in the screen rect, draws with srcCopy.
 	// Original applies clip rect inset by 1 pixel on all sides.
-	if (!_resMan || !(_resMan->isMacFile() || _resMan->hasResFork()))
-		return false;
+	Common::SeekableReadStream *pictStream = nullptr;
+
+	// Try Color Colony resource fork first
+	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);
 
-	Common::SeekableReadStream *pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
 	if (!pictStream) {
-		pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), resID);
-		if (!pictStream) {
-			debug("drawPict: PICT %d not found", resID);
-			return false;
-		}
+		debug("drawPict: PICT %d not found", resID);
+		return false;
 	}
 
 	::Image::PICTDecoder decoder;
@@ -1587,89 +1596,27 @@ bool ColonyEngine::drawPict(int resID) {
 			      resID, surface->w, surface->h, x, y,
 			      surface->format.bytesPerPixel * 8, pictPal.size());
 
-			if (isCLUT8 && pictPal.size() > 0) {
-				// CLUT8 PICT with embedded palette (8-bit color).
-				// Install PICT palette at offset 128, draw all pixels with srcCopy.
-				const int palOffset = 128;
-				int nColors = MIN((int)pictPal.size(), 128);
-				_gfx->setPalette(pictPal.data(), palOffset, nColors);
-
-				for (int iy = 0; iy < surface->h; iy++) {
-					int sy = y + iy;
-					if (sy < clipY1 || sy >= clipY2) continue;
-					for (int ix = 0; ix < surface->w; ix++) {
-						int sx = x + ix;
-						if (sx < clipX1 || sx >= clipX2) continue;
-						byte idx = *((const byte *)surface->getBasePtr(ix, iy));
-						int palIdx = (idx < nColors) ? palOffset + idx : 0;
-						_gfx->setPixel(sx, sy, palIdx);
-					}
-				}
-			} else if (isCLUT8) {
-				// CLUT8 without palette — 1-bit B&W PICT.
-				// Mac QuickDraw srcCopy: pixel 0 = background (white),
-				// pixel 1 = foreground (black). Screen is already black,
-				// so we only need to draw the white pixels.
-				for (int iy = 0; iy < surface->h; iy++) {
-					int sy = y + iy;
-					if (sy < clipY1 || sy >= clipY2) continue;
-					for (int ix = 0; ix < surface->w; ix++) {
-						int sx = x + ix;
-						if (sx < clipX1 || sx >= clipX2) continue;
+			// Draw PICT pixels using direct RGB (packRGB) for full color support.
+			for (int iy = 0; iy < surface->h; iy++) {
+				int sy = y + iy;
+				if (sy < clipY1 || sy >= clipY2) continue;
+				for (int ix = 0; ix < surface->w; ix++) {
+					int sx = x + ix;
+					if (sx < clipX1 || sx >= clipX2) continue;
+					byte r, g, b;
+					if (isCLUT8) {
 						byte idx = *((const byte *)surface->getBasePtr(ix, iy));
-						if (idx == 0)
-							_gfx->setPixel(sx, sy, 15); // white
-					}
-				}
-			} else {
-				// RGB surface — build a color palette from unique pixel colors
-				// and install it at offset 128, preserving actual PICT colors.
-				const int palOffset = 128;
-				const int maxColors = 128;
-				byte builtPal[128 * 3];
-				memset(builtPal, 0, sizeof(builtPal));
-				int nUniqueColors = 0;
-
-				Common::HashMap<uint32, byte> colorMap;
-
-				for (int iy = 0; iy < surface->h && nUniqueColors < maxColors; iy++) {
-					for (int ix = 0; ix < surface->w && nUniqueColors < maxColors; ix++) {
-						uint32 pixel = surface->getPixel(ix, iy);
-						byte pr, pg, pb;
-						surface->format.colorToRGB(pixel, pr, pg, pb);
-						if (pr == 0 && pg == 0 && pb == 0)
-							continue;
-						uint32 key = ((uint32)pr << 16) | ((uint32)pg << 8) | pb;
-						if (!colorMap.contains(key)) {
-							builtPal[nUniqueColors * 3 + 0] = pr;
-							builtPal[nUniqueColors * 3 + 1] = pg;
-							builtPal[nUniqueColors * 3 + 2] = pb;
-							colorMap[key] = nUniqueColors;
-							nUniqueColors++;
+						if (pictPal.size() > 0 && idx < (int)pictPal.size()) {
+							pictPal.get(idx, r, g, b);
+						} else {
+							// B&W PICT: 0=white, 1=black
+							r = g = b = (idx == 0) ? 255 : 0;
 						}
-					}
-				}
-
-				if (nUniqueColors > 0)
-					_gfx->setPalette(builtPal, palOffset, nUniqueColors);
-
-				for (int iy = 0; iy < surface->h; iy++) {
-					int sy = y + iy;
-					if (sy < clipY1 || sy >= clipY2) continue;
-					for (int ix = 0; ix < surface->w; ix++) {
-						int sx = x + ix;
-						if (sx < clipX1 || sx >= clipX2) continue;
+					} else {
 						uint32 pixel = surface->getPixel(ix, iy);
-						byte pr, pg, pb;
-						surface->format.colorToRGB(pixel, pr, pg, pb);
-						if (pr == 0 && pg == 0 && pb == 0)
-							continue;
-						uint32 key = ((uint32)pr << 16) | ((uint32)pg << 8) | pb;
-						if (colorMap.contains(key))
-							_gfx->setPixel(sx, sy, palOffset + colorMap[key]);
-						else
-							_gfx->setPixel(sx, sy, 15);
+						surface->format.colorToRGB(pixel, r, g, b);
 					}
+					_gfx->setPixel(sx, sy, 0xFF000000 | ((uint32)r << 16) | ((uint32)g << 8) | b);
 				}
 			}
 			_gfx->copyToScreen();
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 3141d839982..502f504ce57 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -495,6 +495,7 @@ private:
 	Common::Rect _backgroundLocate;
 	bool _backgroundActive;
 	Common::MacResManager *_resMan;
+	Common::MacResManager *_colorResMan;
 	byte _topBG[8];
 	byte _bottomBG[8];
 	int16 _divideBG;
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 09dc261969a..9246bebcfc3 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -44,18 +44,24 @@ static uint32 packMacColorUI(const uint16 rgb[3]) {
 }
 
 // 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.
 // Caller owns the returned surface. Returns nullptr on failure.
 Graphics::Surface *ColonyEngine::loadPictSurface(int resID) {
-	if (!_resMan || !(_resMan->isMacFile() || _resMan->hasResFork()))
-		return nullptr;
+	Common::SeekableReadStream *pictStream = nullptr;
+
+	// Try Color Colony resource fork first
+	if (_colorResMan && _colorResMan->hasResFork()) {
+		pictStream = _colorResMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
+	}
 
-	Common::SeekableReadStream *pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
-	if (!pictStream) {
-		pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), resID);
-		if (!pictStream)
-			return nullptr;
+	// 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);
 	}
 
+	if (!pictStream)
+		return nullptr;
+
 	::Image::PICTDecoder decoder;
 	Graphics::Surface *result = nullptr;
 	if (decoder.loadStream(*pictStream)) {


Commit: 8230c950a008fd54ca46246262fe2b45a5998e4e
    https://github.com/scummvm/scummvm/commit/8230c950a008fd54ca46246262fe2b45a5998e4e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:27+02:00

Commit Message:
COLONY: refine Mac dashboard layout positioning and dithered background

Changed paths:
    engines/colony/ui.cpp


diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 9246bebcfc3..f90a18a62d5 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -159,41 +159,46 @@ void ColonyEngine::updateViewportLayout() {
 	const int topPad = menuTop + pad;
 
 	if (macColor) {
-		// Original Mac layout: two separate floating windows in a 96px-wide sidebar.
-		// screenR.left = 96. Both windows ~70px wide (2*CCENTER), centered in sidebar.
-		// moveWindow: compRect = (0,0, 70, 105) — compass+map panel
+		// 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.
+		// 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
-		// Visible Mac gray desktop between and around windows.
+		const int CCENTER = 35;
 
-		// Load PICT surfaces (cached after first load).
-		// Available PICTs: -32755 (power, armor normal), -32757 (compass).
-		// Color Colony variants (-32760, -32761) may not exist — fall back to -32755.
+		// Load PICT surfaces (cached after first load)
 		if (!_pictCompass)
 			_pictCompass = loadPictSurface(-32757);
 		if (!_pictPower) {
 			int wantID = _armor ? -32755 : -32761;
 			_pictPower = loadPictSurface(wantID);
 			if (!_pictPower && wantID != -32755)
-				_pictPower = loadPictSurface(-32755); // fall back to normal
+				_pictPower = loadPictSurface(-32755);
 			_pictPowerID = _pictPower ? wantID : 0;
 		}
 
-		// Use PICT dimensions for panel sizes, fall back to original constants
-		const int moveW = _pictCompass ? _pictCompass->w : 70;  // 2*CCENTER
-		const int moveH = _pictCompass ? _pictCompass->h : 105; // 3*CCENTER
+		// moveWindow dimensions from original constants
+		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 : 200;
+		const int infoH = _pictPower ? _pictPower->h : moveH;
 
-		// Center panels horizontally in the sidebar (original windows were centered)
+		// Center panels horizontally in sidebar
 		const int centerX = dashWidth / 2;
 
 		// Position moveWindow at the bottom of the sidebar
 		const int moveLeft = MAX(0, centerX - moveW / 2);
 		const int moveTop = _height - pad - moveH;
-		// compRect split: floorRect = (8,8)-(62,62), compass dish area below
-		const int floorBottom = moveTop + moveH * 62 / 105;
-		_headsUpRect = makeSafeRect(moveLeft, moveTop, moveLeft + moveW, floorBottom);
-		_compassRect = makeSafeRect(moveLeft, floorBottom, moveLeft + moveW, moveTop + moveH);
+
+		// _headsUpRect = floorRect (8,8)-(62,62) relative to moveWindow
+		// This is the minimap clipping area — must NOT overlap compass dish
+		_headsUpRect = makeSafeRect(moveLeft + 8, moveTop + 8,
+		                            moveLeft + 2 * CCENTER - 8, moveTop + 2 * CCENTER - 8);
+
+		// _compassRect = entire moveWindow (used for compass dish drawing)
+		_compassRect = makeSafeRect(moveLeft, moveTop, moveLeft + moveW, moveTop + moveH);
 
 		// Position infoWindow above moveWindow with a gap
 		const int infoLeft = MAX(0, centerX - infoW / 2);
@@ -352,56 +357,41 @@ void ColonyEngine::drawDashboardMac() {
 	}
 
 	// ===== Compass + Floor map (moveWindow) =====
-	// compass.c DrawCompass(): DrawPicture(PICT -32757, &compRect) then patXor drawings
+	// compass.c DrawCompass(): All coordinates relative to moveWindow origin.
 	// compRect = (0,0, 2*CCENTER, 3*CCENTER) = (0,0, 70, 105)
-	const Common::Rect moveRect(_headsUpRect.left, _headsUpRect.top,
-	                            _compassRect.right, _compassRect.bottom);
-	if (moveRect.width() > 4 && moveRect.height() > 4) {
+	// CCENTER = 35
+	if (_compassRect.width() > 4 && _compassRect.height() > 4) {
+		const int ox = _compassRect.left; // moveWindow origin X
+		const int oy = _compassRect.top;  // moveWindow origin Y
+
 		// compass.c: SetRect(&compRect, -2, -2, xSize-2, ySize-2); DrawPicture(comp, &compRect)
 		if (_pictCompass)
-			drawPictAt(_pictCompass, moveRect.left - 2, moveRect.top - 2);
+			drawPictAt(_pictCompass, ox - 2, oy - 2);
 		else
-			_gfx->fillRect(moveRect, colWinBg);
-
-		// compass.c: compRect reset to (0,0, 70, 105)
-		// PenMode(patXor) — all subsequent drawing XORs onto the PICT background
-
-		// Compass dish (compass.c lines 59-70):
-		// r.top=2*CCENTER-4=66; r.bottom=2*CCENTER+28=98;
-		// r.left=CCENTER-16=19; r.right=CCENTER+16=51;
-		// FillOval(&r, black) with patXor
-		// Needle from (CCENTER, 2*CCENTER+12) = (35, 82) using cost[ang]>>3
-		{
-			const int ox = moveRect.left; // origin offset
-			const int oy = moveRect.top;
-			// Use absolute coordinates from original: dish at (35, 82), oval 19-51, 66-98
-			const int dishCX = ox + 35;  // CCENTER
-			const int dishCY = oy + 82;  // 2*CCENTER+12
-
-			// FillOval at (19,66)-(51,98) with patXor black — inverts PICT background
-			_gfx->fillEllipse(dishCX, dishCY, 16, 16, colBlack);
-
-			// Needle: compass.c line 69-70
-			// LineTo(CCENTER+(cost[ang]>>3), (2*CCENTER+12)-(sint[ang]>>3))
-			const int ex = dishCX + (_cost[_me.look] >> 3);
-			const int ey = dishCY - (_sint[_me.look] >> 3);
-			_gfx->drawLine(dishCX, dishCY, ex, ey, colWhite);
-		}
+			_gfx->fillRect(_compassRect, colWinBg);
 
 		// Floor map (compass.c lines 72-213):
-		// floorRect = (7,7)-(63,63) → ++/-- → (8,8)-(62,62) = 54x54 inner map area
-		// ClipRect(&floorRect) for all map drawing
-		// Eye icon at (CCENTER-10,CCENTER-5)-(CCENTER+10,CCENTER+5) = (25,30)-(45,40)
-		// Pupil at (CCENTER-5,CCENTER-5)-(CCENTER+5,CCENTER+5) = (30,30)-(40,40)
-		{
-			drawMiniMap(colBlack);
+		// floorRect = (8,8)-(62,62) — clipped to _headsUpRect
+		// Eye icon at center (CCENTER,CCENTER) = (35,35)
+		drawMiniMap(colBlack);
 
-			// Eye icon (compass.c lines 84-88) — drawn with patXor
-			const int ox = moveRect.left;
-			const int oy = moveRect.top;
-			_gfx->drawEllipse(ox + 35, oy + 35, 10, 5, colBlack);  // FrameOval outer eye
-			_gfx->fillEllipse(ox + 35, oy + 35, 5, 5, colBlack);   // FillOval pupil
-		}
+		// Eye icon (compass.c lines 84-88)
+		// FrameOval (CCENTER-10, CCENTER-5)-(CCENTER+10, CCENTER+5) = (25,30)-(45,40)
+		// FillOval (CCENTER-5, CCENTER-5)-(CCENTER+5, CCENTER+5) = (30,30)-(40,40)
+		_gfx->drawEllipse(ox + 35, oy + 35, 10, 5, colBlack);
+		_gfx->fillEllipse(ox + 35, oy + 35, 5, 5, colBlack);
+
+		// Compass dish (compass.c lines 59-70):
+		// FillOval (CCENTER-16, 2*CCENTER-4)-(CCENTER+16, 2*CCENTER+28) = (19,66)-(51,98)
+		// Needle center at (CCENTER, 2*CCENTER+12) = (35, 82)
+		const int dishCX = ox + 35;
+		const int dishCY = oy + 82;
+		_gfx->fillEllipse(dishCX, dishCY, 16, 16, colBlack);
+
+		// Needle: LineTo(CCENTER+(cost[ang]>>3), (2*CCENTER+12)-(sint[ang]>>3))
+		const int ex = dishCX + (_cost[_me.look] >> 3);
+		const int ey = dishCY - (_sint[_me.look] >> 3);
+		_gfx->drawLine(dishCX, dishCY, ex, ey, colWhite);
 	}
 }
 
@@ -414,15 +404,30 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 			_gfx->drawLine(x1, y1, x2, y2, color);
 	};
 
-	const int lExtBase = _dashBoardRect.width() >> 1;
-	int lExt = lExtBase + (lExtBase >> 1);
-	if (lExt & 1)
-		lExt--;
-	const int sExt = lExt >> 1;
-	const int xloc = (lExt * ((_me.xindex << 8) - _me.xloc)) >> 8;
-	const int yloc = (lExt * ((_me.yindex << 8) - _me.yloc)) >> 8;
-	const int ccenterx = (_headsUpRect.left + _headsUpRect.right) >> 1;
-	const int ccentery = (_headsUpRect.top + _headsUpRect.bottom) >> 1;
+	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+
+	int lExt, sExt, xloc, yloc, ccenterx, ccentery;
+	if (macColor) {
+		// compass.c: CSIZE=64, CCENTER=35
+		// xloc = ((Me.xindex << 8) - Me.xloc) >> 2
+		// Center at (CCENTER, CCENTER) relative to moveWindow
+		lExt = 64; // CSIZE
+		sExt = 32; // CSIZE/2
+		xloc = ((_me.xindex << 8) - _me.xloc) >> 2;
+		yloc = ((_me.yindex << 8) - _me.yloc) >> 2;
+		ccenterx = _compassRect.left + 35; // CCENTER in screen coords
+		ccentery = _compassRect.top + 35;
+	} else {
+		const int lExtBase = _dashBoardRect.width() >> 1;
+		lExt = lExtBase + (lExtBase >> 1);
+		if (lExt & 1)
+			lExt--;
+		sExt = lExt >> 1;
+		xloc = (lExt * ((_me.xindex << 8) - _me.xloc)) >> 8;
+		yloc = (lExt * ((_me.yindex << 8) - _me.yloc)) >> 8;
+		ccenterx = (_headsUpRect.left + _headsUpRect.right) >> 1;
+		ccentery = (_headsUpRect.top + _headsUpRect.bottom) >> 1;
+	}
 	const int tsin = _sint[_me.look];
 	const int tcos = _cost[_me.look];
 
@@ -449,16 +454,14 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 	drawMiniMapLine(xcorner[3] - dy, ycorner[3] - dx, xcorner[0] + dy, ycorner[0] + dx, lineColor);
 
 	// compass.c: food markers use FrameOval ±3px, robot markers ±5px.
-	// Scale proportionally to panel width (original floorRect was 54px wide).
-	const bool macMarkers = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
-	const int foodR = macMarkers ? MAX(2, _headsUpRect.width() / 18) : 1;   // ~3px at 54px
-	const int robotR = macMarkers ? MAX(3, _headsUpRect.width() / 11) : 2;  // ~5px at 54px
+	const int foodR = macColor ? 3 : 1;
+	const int robotR = macColor ? 5 : 2;
 
 	auto drawMarker = [&](int x, int y, int halfSize, uint32 color) {
 		if (x < _headsUpRect.left + 1 || x >= _headsUpRect.right - 1 ||
 		    y < _headsUpRect.top + 1 || y >= _headsUpRect.bottom - 1)
 			return;
-		if (macMarkers) {
+		if (macColor) {
 			// compass.c: FrameOval — circle outline
 			_gfx->drawEllipse(x, y, halfSize, halfSize, color);
 		} else {


Commit: 9e08e49ceb94bc0d9ade9b2fc93aed0c1dfdd37a
    https://github.com/scummvm/scummvm/commit/9e08e49ceb94bc0d9ade9b2fc93aed0c1dfdd37a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:27+02:00

Commit Message:
COLONY: add Mac color animation table and resolveAnimColor lookup

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index db271d62568..a2dfbb183f1 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -43,7 +43,153 @@
 
 namespace Colony {
 
+// Mac color indices from colordef.h enum (cColor[] table in Color256).
+enum {
+	mc_dwall = 6, mc_lwall = 7,
+	mc_char0 = 8,    // char0..char6 = 8..14
+	mc_bulkhead = 15, mc_door = 16,
+	mc_desk = 58, mc_desktop = 59, mc_screen = 62,
+	mc_proj = 72, mc_console = 79, mc_powerbase = 81,
+	mc_box1 = 84, mc_forklift = 86, mc_flglass = 87,
+	mc_cryo = 90, mc_ccore = 111,
+	mc_teleport = 93, mc_teledoor = 94,
+	mc_vanity = 96, mc_mirror = 103,
+	mc_airlock = 25, mc_elevator = 23
+};
+
+// Mac Toolbox BackColor() constants.
+enum {
+	kMacWhite = 30, kMacBlack = 33,
+	kMacYellow = 69, kMacMagenta = 137,
+	kMacRed = 205, kMacCyan = 273,
+	kMacGreen = 341, kMacBlue = 409
+};
+
+// BMColor arrays from ganimate.c — per-animation color maps.
+// Index 0 = background top, 1 = background image, 2+ = per-sprite fill.
+// Positive = cColor[] index, negative = -MacSystemColor, 0 = level-based.
+static const int16 kBMC_Desk[] = {
+	0, mc_desktop,
+	-kMacRed, -kMacCyan, -kMacCyan, -kMacCyan, -kMacCyan,
+	-kMacWhite, -kMacWhite, -kMacMagenta, -kMacYellow, mc_desk,
+	mc_desk, mc_desk, mc_desk, mc_desk, mc_desk,
+	-kMacWhite, mc_screen, -kMacMagenta, -kMacCyan, -kMacCyan,
+	-kMacBlue, -kMacWhite, -kMacRed, -kMacWhite, -kMacYellow
+};
+static const int16 kBMC_Vanity[] = {
+	0, mc_vanity,
+	mc_mirror, -kMacRed, -kMacCyan, -kMacWhite, -kMacYellow,
+	-kMacGreen, -kMacBlue, -kMacRed, -kMacMagenta, -kMacRed,
+	mc_vanity, -kMacWhite, -kMacYellow, mc_mirror
+};
+static const int16 kBMC_Reactor[] = {
+	0, mc_console,
+	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
+	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
+	-kMacYellow, -kMacYellow, -kMacRed, -kMacRed, -kMacRed,
+	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacRed,
+	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacCyan,
+	-kMacMagenta, -kMacWhite
+};
+static const int16 kBMC_Security[] = {
+	0, mc_console,
+	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
+	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
+	-kMacYellow, -kMacYellow, -kMacRed, -kMacRed, -kMacRed,
+	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacRed,
+	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacWhite,
+	-kMacRed, -kMacCyan, -kMacCyan, -kMacCyan, -kMacCyan
+};
+static const int16 kBMC_Teleport[] = {
+	0, mc_teleport, 0, mc_teledoor
+};
+static const int16 kBMC_Creatures[] = {
+	-kMacWhite, 0, -kMacWhite, -kMacCyan, mc_proj,
+	-kMacBlue, -kMacMagenta, -kMacMagenta
+};
+static const int16 kBMC_Controls[] = {
+	0, mc_console,
+	-kMacRed, -kMacYellow, -kMacYellow, -kMacBlue, -kMacYellow, -kMacGreen, mc_screen
+};
+static const int16 kBMC_Lift[] = {
+	0, mc_flglass,
+	mc_teleport, mc_box1, mc_cryo, mc_ccore, 0,
+	-kMacRed, -kMacRed, -kMacCyan, -kMacCyan
+};
+static const int16 kBMC_Powersuit[] = {
+	0, mc_powerbase,
+	-kMacMagenta, -kMacMagenta, -kMacYellow, -kMacYellow, mc_powerbase, -kMacWhite
+};
+static const int16 kBMC_Forklift[] = {
+	0, mc_forklift, mc_forklift, mc_forklift
+};
+static const int16 kBMC_Door[] = {
+	0, mc_bulkhead, 0, mc_door, -kMacYellow
+};
+static const int16 kBMC_Bulkhead[] = {
+	0, mc_bulkhead, 0, mc_bulkhead, -kMacYellow
+};
+static const int16 kBMC_Airlock[] = {
+	0, mc_bulkhead, mc_bulkhead, -kMacRed, mc_airlock
+};
+static const int16 kBMC_Elevator[] = {
+	0, mc_bulkhead, 0, mc_elevator, mc_elevator, -kMacYellow
+};
+static const int16 kBMC_Elevator2[] = {
+	0, mc_bulkhead, 0, -kMacMagenta, mc_elevator, mc_elevator,
+	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow
+};
+
+struct AnimColorEntry {
+	const char *name;
+	const int16 *colors;
+	int count;
+};
+
+static const AnimColorEntry kAnimColors[] = {
+	{ "desk",        kBMC_Desk,       ARRAYSIZE(kBMC_Desk) },
+	{ "vanity",      kBMC_Vanity,     ARRAYSIZE(kBMC_Vanity) },
+	{ "reactor",     kBMC_Reactor,    ARRAYSIZE(kBMC_Reactor) },
+	{ "security",    kBMC_Security,   ARRAYSIZE(kBMC_Security) },
+	{ "teleporter",  kBMC_Teleport,   ARRAYSIZE(kBMC_Teleport) },
+	{ "teleporter2", kBMC_Teleport,   ARRAYSIZE(kBMC_Teleport) },
+	{ "slides",      kBMC_Creatures,  ARRAYSIZE(kBMC_Creatures) },
+	{ "slideshow",   kBMC_Creatures,  ARRAYSIZE(kBMC_Creatures) },
+	{ "teleshow",    kBMC_Creatures,  ARRAYSIZE(kBMC_Creatures) },
+	{ "controls",    kBMC_Controls,   ARRAYSIZE(kBMC_Controls) },
+	{ "lift",        kBMC_Lift,       ARRAYSIZE(kBMC_Lift) },
+	{ "lifter",      kBMC_Lift,       ARRAYSIZE(kBMC_Lift) },
+	{ "suit",        kBMC_Powersuit,  ARRAYSIZE(kBMC_Powersuit) },
+	{ "spacesuit",   kBMC_Powersuit,  ARRAYSIZE(kBMC_Powersuit) },
+	{ "forklift",    kBMC_Forklift,   ARRAYSIZE(kBMC_Forklift) },
+	{ "door",        kBMC_Door,       ARRAYSIZE(kBMC_Door) },
+	{ "bulkhead",    kBMC_Bulkhead,   ARRAYSIZE(kBMC_Bulkhead) },
+	{ "airlock",     kBMC_Airlock,    ARRAYSIZE(kBMC_Airlock) },
+	{ "elev",        kBMC_Elevator,   ARRAYSIZE(kBMC_Elevator) },
+	{ "elevator",    kBMC_Elevator,   ARRAYSIZE(kBMC_Elevator) },
+	{ "elevator2",   kBMC_Elevator2,  ARRAYSIZE(kBMC_Elevator2) },
+	{ nullptr, nullptr, 0 }
+};
+
+// Convert Mac Toolbox BackColor constant to ARGB.
+static uint32 macSysColorToARGB(int sysColor) {
+	switch (sysColor) {
+	case kMacWhite:   return 0xFFFFFFFF;
+	case kMacBlack:   return 0xFF000000;
+	case kMacYellow:  return 0xFFFFFF00;
+	case kMacMagenta: return 0xFFFF00FF;
+	case kMacRed:     return 0xFFFF0000;
+	case kMacCyan:    return 0xFF00FFFF;
+	case kMacGreen:   return 0xFF00FF00;
+	case kMacBlue:    return 0xFF0000FF;
+	default:          return 0xFFFFFFFF;
+	}
+}
 
+static uint32 packMacColorBG(const uint16 rgb[3]) {
+	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
+	       ((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
+}
 
 ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd), _randomSource("colony") {
 	_level = 0;
@@ -1635,6 +1781,19 @@ bool ColonyEngine::loadAnimation(const Common::String &name) {
 	for (int i = 0; i < 6; i++)
 		_animDisplay[i] = 1;
 
+	// Look up per-animation BMColor map (from ganimate.c).
+	_animBMColors.clear();
+	Common::String nameLower = name;
+	nameLower.toLowercase();
+	for (const AnimColorEntry *e = kAnimColors; e->name; e++) {
+		if (nameLower == e->name) {
+			_animBMColors.resize(e->count);
+			for (int i = 0; i < e->count; i++)
+				_animBMColors[i] = e->colors[i];
+			break;
+		}
+	}
+
 	Common::String fileName = name + ".pic";
 	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
 	if (!file) {
@@ -1917,6 +2076,27 @@ void ColonyEngine::updateAnimation() {
 	}
 }
 
+// Resolve a BMColor entry to an ARGB color.
+// bmEntry > 0: cColor index → use _macColors[idx].bg
+// bmEntry < 0: negated Mac system color constant
+// bmEntry == 0: level-based character color (depends on corepower)
+uint32 ColonyEngine::resolveAnimColor(int16 bmEntry) const {
+	if (bmEntry < 0) {
+		return macSysColorToARGB(-bmEntry);
+	} else if (bmEntry > 0) {
+		if (bmEntry < 145)
+			return packMacColorBG(_macColors[bmEntry].bg);
+		return 0xFFFFFFFF;
+	} else {
+		// Zero = level-based (original gamesprt.c DrawlSprite/DrawBackGround):
+		//   if(corepower[coreindex]) RGBBackColor(&cColor[c_char0+level-1].f);
+		//   else RGBBackColor(&cColor[c_dwall].b);
+		if (_corePower[_coreIndex] > 0 && _level >= 1 && _level <= 7)
+			return packMacColorBG(_macColors[mc_char0 + _level - 1].fg);
+		return packMacColorBG(_macColors[mc_dwall].bg);
+	}
+}
+
 void ColonyEngine::drawAnimation() {
 	_gfx->clear(0);
 
@@ -1925,25 +2105,53 @@ void ColonyEngine::drawAnimation() {
 	ox = (ox / 8) * 8;
 	int oy = _screenR.top + (_screenR.height() - 264) / 2;
 
-	// Fill background patterns (416x264 area)
-	for (int y = 0; y < 264; y++) {
-		byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
-		byte row = pat[y % 8];
-		for (int x = 0; x < 416; x++) {
-			bool set = (row & (0x80 >> (x % 8))) != 0;
-			// Pattern bit: 1->Black(0), 0->White(15) based on original inversion
-			// Actually Invert in readanim: ~data.
-			// Let's assume set means "white" (15) and unset "black" (0) or vice versa.
-			// In original: BackColor(Black). Pattern 1s draw ForeColor. 0s draw BackColor.
-			// If we want "not black", we likely want some white pixels.
-			// Let's try: set -> 15 (White), !set -> 0 (Black).
-			_gfx->setPixel(ox + x, oy + y, set ? 15 : 0);
+	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh
+	                       && !_animBMColors.empty());
+
+	// Fill background patterns (416x264 area).
+	// Color mode: QuickDraw pattern bit 1 → ForeColor (black), bit 0 → BackColor.
+	// Original DrawBackGround():
+	//   Top: BMColor[0]<0 → system color; ==0 → powered:c_char0+level-1.f, else:c_dwall.b
+	//   Bottom: powered → c_lwall.f; unpowered → inherits top BackColor
+	// B&W/DOS: preserve existing palette-index behavior (bit 1 → 15, bit 0 → 0).
+	if (useColor) {
+		const bool powered = (_corePower[_coreIndex] > 0);
+		uint32 topBG = resolveAnimColor(_animBMColors[0]);
+		// Bottom: only uses c_lwall.f when powered; unpowered inherits top color
+		uint32 botBG = powered ? packMacColorBG(_macColors[mc_lwall].fg) : topBG;
+		for (int y = 0; y < 264; y++) {
+			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
+			byte row = pat[y % 8];
+			uint32 bg = (y < _divideBG) ? topBG : botBG;
+			for (int x = 0; x < 416; x++) {
+				bool set = (row & (0x80 >> (x % 8))) != 0;
+				_gfx->setPixel(ox + x, oy + y, set ? (uint32)0xFF000000 : bg);
+			}
+		}
+	} else {
+		for (int y = 0; y < 264; y++) {
+			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
+			byte row = pat[y % 8];
+			for (int x = 0; x < 416; x++) {
+				bool set = (row & (0x80 >> (x % 8))) != 0;
+				_gfx->setPixel(ox + x, oy + y, set ? 15 : 0);
+			}
 		}
 	}
 
-	// Draw background if active
+	// Draw background image if active.
+	// Original: BMColor[1] only applied when corepower[coreindex] > 0.
 	if (_backgroundActive && _backgroundFG) {
-		drawAnimationImage(_backgroundFG, _backgroundMask, ox + _backgroundLocate.left, oy + _backgroundLocate.top);
+		uint32 bgFill = 0xFFFFFFFF; // B&W default
+		if (useColor && _animBMColors.size() > 1) {
+			if (_corePower[_coreIndex] > 0)
+				bgFill = resolveAnimColor(_animBMColors[1]);
+			else
+				bgFill = resolveAnimColor(_animBMColors[0]); // unpowered: inherits top
+		}
+		drawAnimationImage(_backgroundFG, _backgroundMask,
+		                   ox + _backgroundLocate.left, oy + _backgroundLocate.top,
+		                   bgFill);
 	}
 
 	// Draw complex sprites
@@ -1970,28 +2178,51 @@ void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
 	int x = ox + ls->xloc + ls->objects[cnum].xloc + s->clip.left;
 	int y = oy + ls->yloc + ls->objects[cnum].yloc + s->clip.top;
 
-	drawAnimationImage(s->fg, s->mask, x, y);
+	// Resolve fill color from BMColor[index+2] (ganimate.c DrawlSprite).
+	uint32 fillColor = 0xFFFFFFFF; // B&W default: white
+	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh
+	                       && !_animBMColors.empty());
+	if (useColor) {
+		int bmIdx = index + 2;
+		if (bmIdx < (int)_animBMColors.size())
+			fillColor = resolveAnimColor(_animBMColors[bmIdx]);
+		else
+			fillColor = resolveAnimColor(0); // fallback to level-based
+	}
+
+	drawAnimationImage(s->fg, s->mask, x, y, fillColor);
 }
 
-void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y) {
+void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y, uint32 fillColor) {
 	if (!img || !img->data)
 		return;
 
+	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh);
+	// Mac QuickDraw srcBic+srcOr rendering:
+	//   mask bit=1 → opaque (part of sprite)
+	//   fg bit=1   → ForeColor (black outline)
+	//   fg bit=0   → BackColor (fillColor from BMColor)
+	// B&W/DOS fallback preserves existing palette-index behavior.
+	const uint32 fgColor = useColor ? (uint32)0xFF000000 : 15;
+	const uint32 bgColor = useColor ? fillColor : 0;
+
 	for (int iy = 0; iy < img->height; iy++) {
 		for (int ix = 0; ix < img->width; ix++) {
 			int byteIdx = iy * img->rowBytes + (ix / 8);
 			int bitIdx = 7 - (ix % 8);
-			
+
 			bool maskSet = true;
 			if (mask && mask->data) {
-				maskSet = (mask->data[byteIdx] & (1 << bitIdx)) != 0;
+				int mByteIdx = iy * mask->rowBytes + (ix / 8);
+				int mBitIdx = 7 - (ix % 8);
+				maskSet = (mask->data[mByteIdx] & (1 << mBitIdx)) != 0;
 			}
 
 			if (!maskSet)
 				continue;
 
 			bool fgSet = (img->data[byteIdx] & (1 << bitIdx)) != 0;
-			uint32 color = fgSet ? 15 : 0;
+			uint32 color = fgSet ? fgColor : bgColor;
 
 			_gfx->setPixel(x + ix, y + iy, color);
 		}
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 502f504ce57..ab9dab2f23b 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -500,6 +500,7 @@ private:
 	byte _bottomBG[8];
 	int16 _divideBG;
 	Common::String _animationName;
+	Common::Array<int16> _animBMColors;
 	bool _animationRunning;
 	int _animationResult;
 	bool _doorOpen;
@@ -516,7 +517,8 @@ private:
 	void updateAnimation();
 	void drawAnimation();
 	void drawComplexSprite(int index, int ox, int oy);
-	void drawAnimationImage(Image *img, Image *mask, int x, int y);
+	void drawAnimationImage(Image *img, Image *mask, int x, int y, uint32 fillColor = 0xFFFFFFFF);
+	uint32 resolveAnimColor(int16 bmEntry) const;
 	Image *loadImage(Common::SeekableReadStream &file);
 	void unpackBytes(Common::SeekableReadStream &file, byte *dst, uint32 len);
 	Common::Rect readRect(Common::SeekableReadStream &file);


Commit: 72ba36e237a353f635fdb01db73ec0eb8d59529a
    https://github.com/scummvm/scummvm/commit/72ba36e237a353f635fdb01db73ec0eb8d59529a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:27+02:00

Commit Message:
COLONY: fix animation color index offsets in HUD drawing

Changed paths:
    engines/colony/ui.cpp


diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index f90a18a62d5..e2f5dd04cad 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -200,9 +200,10 @@ void ColonyEngine::updateViewportLayout() {
 		// _compassRect = entire moveWindow (used for compass dish drawing)
 		_compassRect = makeSafeRect(moveLeft, moveTop, moveLeft + moveW, moveTop + moveH);
 
-		// Position infoWindow above moveWindow with a gap
+		// Position infoWindow right at the top of the sidebar (below menu bar).
+		// Original: infoWindow starts immediately below the Mac menu bar.
 		const int infoLeft = MAX(0, centerX - infoW / 2);
-		const int infoTop = MAX(topPad, moveTop - pad - infoH);
+		const int infoTop = menuTop;
 		_powerRect = makeSafeRect(infoLeft, infoTop, infoLeft + infoW, infoTop + infoH);
 	} else {
 		const int blockLeft = pad;
@@ -294,8 +295,9 @@ void ColonyEngine::drawDashboardMac() {
 	const uint32 colWinBg = packMacColorUI(_macColors[7].bg); // Mac desktop gray
 	const uint32 colBlue = packRGB(0, 0, 255); // power.c: ForeColor(blueColor)
 
-	// Dashboard background — Mac desktop gray, visible between floating windows
-	_gfx->fillRect(_dashBoardRect, colWinBg);
+	// Dashboard background — Mac desktop dither pattern (classic 50% gray checkerboard).
+	// Original Mac desktop: alternating black/white pixels between floating windows.
+	_gfx->fillDitherRect(_dashBoardRect, colBlack, colWhite);
 
 	// Viewport separator
 	if (_screenR.left > 0)


Commit: 300e51bed33309900999cb07c74d36b7ba75d64c
    https://github.com/scummvm/scummvm/commit/300e51bed33309900999cb07c74d36b7ba75d64c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:28+02:00

Commit Message:
COLONY: fullscreen key for mac color

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index a2dfbb183f1..3a42b0716bf 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -224,6 +224,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	}
 	
 	_wireframe = (_renderMode != Common::kRenderMacintosh);
+	_fullscreen = false;
 	_speedShift = 2; // DOS default: speedshift=1, but 2 feels better with our frame rate
 	_wm = nullptr;
 	_macMenu = nullptr;
@@ -818,8 +819,16 @@ Common::Error ColonyEngine::run() {
 		_sound->init();
 	}
 
-	_width = 640;
-	_height = 350;
+	// 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) {
+		_width = 640;
+		_height = 480;
+	} else {
+		_width = 640;
+		_height = 350;
+	}
 
 	if (_widescreen) {
 		_width = _height * 16 / 9;
@@ -992,6 +1001,14 @@ Common::Error ColonyEngine::run() {
 							_system->getEventManager()->purgeMouseEvents();
 						}
 						break;
+					// F11: toggle fullscreen (hide Mac menu bar)
+					case Common::KEYCODE_F11:
+						if (_macMenu) {
+							_fullscreen = !_fullscreen;
+							_menuBarHeight = _fullscreen ? 0 : 20;
+							updateViewportLayout();
+						}
+						break;
 					// Space: toggle mouselook / free cursor
 					case Common::KEYCODE_SPACE:
 						if (_fl == 2)
@@ -1057,7 +1074,7 @@ Common::Error ColonyEngine::run() {
 		
 		// Draw Mac menu bar overlay (render directly to our surface, skip WM's
 		// g_system->copyRectToScreen which conflicts with the OpenGL backend)
-		if (_macMenu && _menuSurface) {
+		if (_macMenu && _menuSurface && !_fullscreen) {
 			_menuSurface->fillRect(Common::Rect(_width, _height), _menuSurface->format.ARGBToColor(0, 0, 0, 0));
 			_macMenu->draw(_menuSurface, true);
 			_gfx->drawSurface(&_menuSurface->rawSurface(), 0, 0);
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index ab9dab2f23b..1c13ff9b4da 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -364,6 +364,7 @@ private:
 	int _weapons;
 	bool _wireframe;
 	bool _widescreen;
+	bool _fullscreen;
 	int _speedShift; // 1-5, movement speed = 1 << (_speedShift - 1)
 
 	Common::RandomSource _randomSource;


Commit: a04555134795bfdfd1bd2d60feed5173cc716e51
    https://github.com/scummvm/scummvm/commit/a04555134795bfdfd1bd2d60feed5173cc716e51
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:28+02:00

Commit Message:
COLONY: simplify main loop, add power drain counter, clean up HUD

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 3a42b0716bf..5dcb03c4c91 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -1088,6 +1088,62 @@ Common::Error ColonyEngine::run() {
 	return Common::kNoError;
 }
 
+bool ColonyEngine::checkSkipRequested() {
+	// Non-blocking check for Shift+S skip during intro/animation sequences.
+	// Drains pending events, handles screen refresh and quit events.
+	// Returns true if Shift+S was pressed or shouldQuit() is true.
+	Common::Event event;
+	while (_system->getEventManager()->pollEvent(event)) {
+		switch (event.type) {
+		case Common::EVENT_QUIT:
+		case Common::EVENT_RETURN_TO_LAUNCHER:
+			return true;
+		case Common::EVENT_KEYDOWN:
+			if (event.kbd.keycode == Common::KEYCODE_s &&
+			    (event.kbd.flags & Common::KBD_SHIFT))
+				return true;
+			break;
+		case Common::EVENT_SCREEN_CHANGED:
+			_gfx->computeScreenViewport();
+			break;
+		default:
+			break;
+		}
+	}
+	return shouldQuit();
+}
+
+bool ColonyEngine::waitForInput() {
+	// Blocking wait for any key press or mouse click.
+	// Handles screen refresh, quit events, and mouse movement while waiting.
+	// Returns true if Shift+S was pressed (for intro skip propagation).
+	while (!shouldQuit()) {
+		Common::Event event;
+		while (_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+			case Common::EVENT_QUIT:
+			case Common::EVENT_RETURN_TO_LAUNCHER:
+				return false;
+			case Common::EVENT_KEYDOWN:
+				if (event.kbd.keycode == Common::KEYCODE_s &&
+				    (event.kbd.flags & Common::KBD_SHIFT))
+					return true;
+				return false;
+			case Common::EVENT_LBUTTONDOWN:
+				return false;
+			case Common::EVENT_SCREEN_CHANGED:
+				_gfx->computeScreenViewport();
+				break;
+			default:
+				break;
+			}
+		}
+		_system->updateScreen();
+		_system->delayMillis(10);
+	}
+	return false;
+}
+
 void ColonyEngine::playIntro() {
 	if (getPlatform() == Common::kPlatformMacintosh) {
 		// Load the Mac "Commando" font (FOND 190, 12pt) from Colony resources.
@@ -1134,16 +1190,8 @@ void ColonyEngine::playIntro() {
 			_system->delayMillis(10);
 
 		// Original: if(Button()) qt=OptionKey(); — check for skip
-		if (!qt) {
-			Common::Event event;
-			while (_system->getEventManager()->pollEvent(event)) {
-				if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
-					int mods = _system->getEventManager()->getModifierState();
-					if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
-						qt = true;
-				}
-			}
-		}
+		if (!qt)
+			qt = checkSkipRequested();
 
 		if (!qt) {
 			// 3. Logo 1 + PlayMars + makestars
@@ -1301,20 +1349,7 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 	bool qt = false;
 
 	for (int scrollOff = _height; scrollOff > 0 && !qt; scrollOff -= inc) {
-		Common::Event event;
-		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
-				// Original: if(Button()) if(qt=OptionKey()) { StopSound(); break; }
-				// Only modifier+click/key skips entire intro
-				int mods = _system->getEventManager()->getModifierState();
-				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META)) {
-					qt = true;
-					_sound->stop();
-					break;
-				}
-			}
-		}
-		if (shouldQuit()) { qt = true; break; }
+		if (checkSkipRequested()) { qt = true; _sound->stop(); break; }
 
 		_gfx->clear(_gfx->black());
 		for (int i = 0; i < storyLength; i++) {
@@ -1337,39 +1372,14 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 	}
 
 	// Wait for click (original: while(!Button()); while(Button()&&!qt);)
-	if (!qt) {
-		bool waiting = true;
-		while (waiting && !shouldQuit()) {
-			Common::Event event;
-			while (_system->getEventManager()->pollEvent(event)) {
-				if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
-					// Check if modifier held — if so, skip entire intro
-					int mods = _system->getEventManager()->getModifierState();
-					if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
-						qt = true;
-					waiting = false;
-				}
-			}
-			_system->delayMillis(10);
-		}
-	}
+	if (!qt)
+		qt = waitForInput();
 
 	// Phase 2: Scroll text off the top of the screen
 	// Original: scrollRect continues moving up, text slides upward
 	if (!qt) {
 		for (int scrollOff = 0; scrollOff > -_height && !qt; scrollOff -= inc) {
-			Common::Event event;
-			while (_system->getEventManager()->pollEvent(event)) {
-				if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
-					int mods = _system->getEventManager()->getModifierState();
-					if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META)) {
-						qt = true;
-						_sound->stop();
-						break;
-					}
-				}
-			}
-			if (shouldQuit()) { qt = true; break; }
+			if (checkSkipRequested()) { qt = true; _sound->stop(); break; }
 
 			_gfx->clear(_gfx->black());
 			for (int i = 0; i < storyLength; i++) {
@@ -1446,15 +1456,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 	// Animate: original loops ~200 frames or until Mars sound repeats 2x
 	// Original: only modifier+click (OptionKey()) returns true when btn=0
 	for (int k = 0; k < 120; k++) {
-		Common::Event event;
-		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
-				int mods = _system->getEventManager()->getModifierState();
-				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
-					return true;
-			}
-		}
-		if (shouldQuit()) return true;
+		if (checkSkipRequested()) return true;
 
 		for (int i = 0; i < NSTARS; i++) {
 			// Erase previous
@@ -1490,15 +1492,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 	int nstars = 2 * ((MAXSTAR - 0x030) / deltapd);
 	if (nstars > 200) nstars = 200;
 	for (int k = 0; k < nstars; k++) {
-		Common::Event event;
-		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
-				int mods = _system->getEventManager()->getModifierState();
-				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
-					return true;
-			}
-		}
-		if (shouldQuit()) return true;
+		if (checkSkipRequested()) return true;
 
 		for (int i = 0; i < NSTARS; i++) {
 			int d = dist[i];
@@ -1588,16 +1582,7 @@ bool ColonyEngine::makeBlackHole() {
 			_gfx->copyToScreen();
 			_system->delayMillis(16);
 
-			Common::Event event;
-			while (_system->getEventManager()->pollEvent(event)) {
-				// Original: if(Button()) if(OptionKey()) return(TRUE);
-				if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
-					int mods = _system->getEventManager()->getModifierState();
-					if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
-						return true;
-				}
-			}
-			if (shouldQuit()) return true;
+			if (checkSkipRequested()) return true;
 		}
 	}
 	_gfx->copyToScreen();
@@ -1653,15 +1638,7 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft);
 		_gfx->copyToScreen();
 
-		Common::Event event;
-		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
-				int mods = _system->getEventManager()->getModifierState();
-				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
-					return true;
-			}
-		}
-		if (shouldQuit()) return true;
+		if (checkSkipRequested()) return true;
 		_system->delayMillis(8);
 	}
 
@@ -1670,15 +1647,7 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 	//   while(!SoundDone()); StopSound(); PlayKlaxon(); InvertRect(&invrt);
 	_sound->stop(); // EndCSound()
 	for (int i = 0; i < 6; i++) {
-		Common::Event event;
-		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
-				int mods = _system->getEventManager()->getModifierState();
-				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
-					return true;
-			}
-		}
-		if (shouldQuit()) return true;
+		if (checkSkipRequested()) return true;
 
 		// Wait for previous klaxon to finish
 		while (_sound->isPlaying() && !shouldQuit())
@@ -1704,15 +1673,7 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft);
 		_gfx->copyToScreen();
 
-		Common::Event event;
-		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_KEYDOWN) {
-				int mods = _system->getEventManager()->getModifierState();
-				if (mods & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_SHIFT | Common::KBD_META))
-					return true;
-			}
-		}
-		if (shouldQuit()) return true;
+		if (checkSkipRequested()) return true;
 		_system->delayMillis(8);
 	}
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 1c13ff9b4da..d5e49aca52f 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -322,6 +322,8 @@ public:
 	int checkwall(int xnew, int ynew, Locate *pobject);
 	void cCommand(int xnew, int ynew, bool allowInteraction);
 	bool scrollInfo(const Graphics::Font *macFont = nullptr);
+	bool checkSkipRequested();
+	bool waitForInput();
 	void checkCenter();
 	void fallThroughHole();
 
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 5a55af919d2..fe1b2f74d2e 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -1832,7 +1832,7 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
 		if (macMode) {
-			drawHolePoly(u, v, 4, 100); // c_hotplate
+			drawHolePoly(u, v, 4, 31); // c_hotplate
 		} else {
 			// DOS non-polyfill: X pattern (two diagonals)
 			wallLine(corners, 0.0f, 0.0f, 1.0f, 1.0f, holeColor);
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index e2f5dd04cad..065d53f4327 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -1256,17 +1256,8 @@ void ColonyEngine::printMessage(const char *text[], bool hold) {
 
 	_gfx->copyToScreen();
 
-	if (hold) {
-		bool waiting = true;
-		while (waiting && !shouldQuit()) {
-			Common::Event event;
-			while (_system->getEventManager()->pollEvent(event)) {
-				if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
-					waiting = false;
-			}
-			_system->delayMillis(10);
-		}
-	}
+	if (hold)
+		waitForInput();
 }
 
 void ColonyEngine::makeMessageRect(Common::Rect &rr) {
@@ -1375,15 +1366,7 @@ void ColonyEngine::doText(int entry, int center) {
 	_gfx->copyToScreen();
 
 	// Wait for key
-	bool waiting = true;
-	while (waiting && !shouldQuit()) {
-		Common::Event event;
-		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
-				waiting = false;
-		}
-		_system->delayMillis(10);
-	}
+	waitForInput();
 
 	free(page);
 }


Commit: f45f6dc6ae7d530adb934ce00bacdf2a147b20ea
    https://github.com/scummvm/scummvm/commit/f45f6dc6ae7d530adb934ce00bacdf2a147b20ea
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:28+02:00

Commit Message:
COLONY: add Mac menu bar checkbox sync and cursor initialization

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 5dcb03c4c91..27415064c3b 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -1429,10 +1429,14 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 			_gfx->setPixel(xx, yy, 15);
 	}
 
-	// Initialize moving stars
+	// Initialize moving stars — original uses PenMode(patXor) so stars
+	// don't damage the logo underneath (XOR drawing the same line twice
+	// restores the original pixels).
 	int xang[NSTARS], yang[NSTARS], dist[NSTARS];
 	int xsave1[NSTARS], ysave1[NSTARS], xsave2[NSTARS], ysave2[NSTARS];
 
+	_gfx->setXorMode(true);
+
 	for (int i = 0; i < NSTARS; i++) {
 		int d = dist[i] = _randomSource.getRandomNumber(MAXSTAR);
 		if (d <= 0x030) d = dist[i] = MAXSTAR;
@@ -1454,13 +1458,12 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 	_gfx->copyToScreen();
 
 	// Animate: original loops ~200 frames or until Mars sound repeats 2x
-	// Original: only modifier+click (OptionKey()) returns true when btn=0
 	for (int k = 0; k < 120; k++) {
-		if (checkSkipRequested()) return true;
+		if (checkSkipRequested()) { _gfx->setXorMode(false); return true; }
 
 		for (int i = 0; i < NSTARS; i++) {
-			// Erase previous
-			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 0);
+			// Erase previous — XOR the same line again to restore underlying pixels
+			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
 
 			int s = xang[i];
 			int c = yang[i];
@@ -1481,18 +1484,18 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 			xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
 			ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
 
+			// Draw new star position
 			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
 		}
 		_gfx->copyToScreen();
 		_system->delayMillis(16);
 	}
 
-	// Fade-out phase: stars fly off without resetting
-	// Original: only modifier+click (OptionKey()) returns true when btn=0
+	// Fade-out phase: stars fly off without resetting (trails accumulate via XOR)
 	int nstars = 2 * ((MAXSTAR - 0x030) / deltapd);
 	if (nstars > 200) nstars = 200;
 	for (int k = 0; k < nstars; k++) {
-		if (checkSkipRequested()) return true;
+		if (checkSkipRequested()) { _gfx->setXorMode(false); return true; }
 
 		for (int i = 0; i < NSTARS; i++) {
 			int d = dist[i];
@@ -1517,6 +1520,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 		_system->delayMillis(8);
 	}
 
+	_gfx->setXorMode(false);
 	return false;
 }
 
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index 60bdb72ce72..347c9ecad9b 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -60,6 +60,7 @@ public:
 	// Buffer management
 	virtual void copyToScreen() = 0;
 	virtual void setWireframe(bool enable, int64_t fillColor = -1) = 0;
+	virtual void setXorMode(bool enable) {}
 	virtual void setStippleData(const byte *data) {}
 	virtual void setMacColors(uint32 fg, uint32 bg) {}
 	virtual void computeScreenViewport() = 0;
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index c2ff109830d..a535999c15f 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -63,6 +63,14 @@ public:
 		_wireframe = enable;
 		_wireframeFillColor = fillColor;
 	}
+	void setXorMode(bool enable) override {
+		if (enable) {
+			glEnable(GL_COLOR_LOGIC_OP);
+			glLogicOp(GL_XOR);
+		} else {
+			glDisable(GL_COLOR_LOGIC_OP);
+		}
+	}
 	void setStippleData(const byte *data) override {
 		_stippleData = data;
 	}


Commit: b18fd2a1a5c5ffd84dedebc6e4082fd66db37d21
    https://github.com/scummvm/scummvm/commit/b18fd2a1a5c5ffd84dedebc6e4082fd66db37d21
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:28+02:00

Commit Message:
COLONY: avoid z-fighting in objects

Changed paths:
    engines/colony/render.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index fe1b2f74d2e..038c0ab6162 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -2389,25 +2389,26 @@ void ColonyEngine::renderCorridor3D() {
 
 	uint32 wallColor = wallLine;
 
+	// --- Phase 1: Background (floor + ceiling) ---
+	// No depth test or write — these are pure background, everything overwrites them.
+	_gfx->setDepthState(false, false);
+
 	// Draw large floor and ceiling quads.
 	// Mac Display(): EraseRect fills ceiling with c_lwall.bg and floor with c_lwall.fg.
 	// Set wireframe fill to each surface's own color so they aren't all wallFill.
 	_gfx->setWireframe(true, floorColor);
-	_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.1f,
-	                100000.0f, -100000.0f, -160.1f,
-	                100000.0f, 100000.0f, -160.1f,
-	                -100000.0f, 100000.0f, -160.1f, floorColor);
+	_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.0f,
+	                100000.0f, -100000.0f, -160.0f,
+	                100000.0f, 100000.0f, -160.0f,
+	                -100000.0f, 100000.0f, -160.0f, floorColor);
 
 	_gfx->setWireframe(true, ceilColor);
-	_gfx->draw3DQuad(-100000.0f, -100000.0f, 160.1f,
-	                100000.0f, -100000.0f, 160.1f,
-	                100000.0f, 100000.0f, 160.1f,
-	                -100000.0f, 100000.0f, 160.1f, ceilColor);
+	_gfx->draw3DQuad(-100000.0f, -100000.0f, 160.0f,
+	                100000.0f, -100000.0f, 160.0f,
+	                100000.0f, 100000.0f, 160.0f,
+	                -100000.0f, 100000.0f, 160.0f, ceilColor);
 
-	// Walls always use wireframe with fill (opaque walls).
-	_gfx->setWireframe(true, wallFill);
- 
-	// Draw ceiling grid (Cuadricule) - DOS wireframe mode only.
+	// Ceiling grid (Cuadricule) - DOS wireframe mode only.
 	// 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.
@@ -2422,6 +2423,12 @@ void ColonyEngine::renderCorridor3D() {
 		}
 	}
 
+	// --- Phase 2: Walls ---
+	// Depth test + write enabled. Pushed-back depth range so features/objects beat walls.
+	_gfx->setDepthState(true, true);
+	_gfx->setDepthRange(0.01, 1.0);
+	_gfx->setWireframe(true, wallFill);
+
 	for (int y = 0; y < 32; y++) {
 		for (int x = 0; x < 32; x++) {
 			uint8 w = _wall[x][y];
@@ -2433,9 +2440,17 @@ void ColonyEngine::renderCorridor3D() {
 			}
 		}
 	}
-	
+
+	// --- Phase 3: Wall & cell features ---
+	// Closer depth range than walls — features always beat their own wall surface.
+	// Depth test still active so far-away features are hidden behind nearer walls.
+	_gfx->setDepthRange(0.005, 1.0);
 	drawWallFeatures3D();
 
+	// --- Phase 4: Objects ---
+	// Full depth range — objects beat walls and features at the same distance.
+	_gfx->setDepthRange(0.0, 1.0);
+
 	// F7 toggles object fill.
 	// EGA: default is filled (wall background); F7 = outline-only (see-through).
 	// Mac: default is per-surface fill; F7 = "Fast mode" (outline-only).
@@ -2457,8 +2472,11 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		draw3DPrism(obj, kConsolePart, false);
 		break;
 	case kObjCChair:
-		for (int i = 0; i < 5; i++)
+		for (int i = 0; i < 5; i++) {
+			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kCChairParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjPlant:
 		// DOS MakePlant draw order: top pot, then green leaf lines, then pot on top.
@@ -2470,17 +2488,25 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 	case kObjCouch:
 	case kObjChair: {
 		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
-		for (int i = 0; i < 4; i++)
+		for (int i = 0; i < 4; i++) {
+			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
 			draw3DPrism(obj, parts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	}
 	case kObjTV:
-		for (int i = 0; i < 2; i++)
-			draw3DPrism(obj, kTVParts[i], false);
+		_gfx->setDepthRange(0.002, 1.0); // body base layer (pushed back)
+		draw3DPrism(obj, kTVParts[0], false);
+		_gfx->setDepthRange(0.0, 1.0);   // screen on top of body face
+		draw3DPrism(obj, kTVParts[1], false);
 		break;
 	case kObjDrawer:
-		for (int i = 0; i < 2; i++)
+		for (int i = 0; i < 2; i++) {
+			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kDrawerParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjFWall:
 		draw3DPrism(obj, kFWallPart, false);
@@ -2492,19 +2518,28 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		draw3DPrism(obj, kScreenPart, false);
 		break;
 	case kObjTable:
-		for (int i = 0; i < 2; i++)
+		for (int i = 0; i < 2; i++) {
+			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kTableParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjBed:
 	case kObjBBed: {
 		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
-		for (int i = 0; i < 3; i++)
+		for (int i = 0; i < 3; i++) {
+			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
 			draw3DPrism(obj, parts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	}
 	case kObjDesk:
-		for (int i = 0; i < 10; i++)
+		for (int i = 0; i < 10; i++) {
+			_gfx->setDepthRange((9 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kDeskParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjBox1:
 		draw3DPrism(obj, kBox1Part, false);
@@ -2513,59 +2548,95 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		draw3DPrism(obj, kBenchPart, false);
 		break;
 	case kObjCBench:
-		for (int i = 0; i < 2; i++)
+		for (int i = 0; i < 2; i++) {
+			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kCBenchParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjBox2:
+		_gfx->setDepthRange(0.002, 1.0);
 		draw3DPrism(obj, kBox2Parts[1], false); // base first
+		_gfx->setDepthRange(0.0, 1.0);
 		draw3DPrism(obj, kBox2Parts[0], false); // top second
 		break;
 	case kObjReactor:
-		for (int i = 0; i < 3; i++)
+		for (int i = 0; i < 3; i++) {
+			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kReactorParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjPowerSuit:
-		for (int i = 0; i < 5; i++)
+		for (int i = 0; i < 5; i++) {
+			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kPowerSuitParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjTeleport:
 		draw3DPrism(obj, kTelePart, false);
 		break;
 	case kObjCryo:
+		_gfx->setDepthRange(0.002, 1.0);
 		draw3DPrism(obj, kCryoParts[1], false); // base first
+		_gfx->setDepthRange(0.0, 1.0);
 		draw3DPrism(obj, kCryoParts[0], false); // top second
 		break;
 	case kObjProjector:
 		// Projector sits on table — draw table first, then projector parts
-		for (int i = 0; i < 2; i++)
-			draw3DPrism(obj, kTableParts[i], false);
+		_gfx->setDepthRange(0.008, 1.0);
+		draw3DPrism(obj, kTableParts[0], false); // table base
+		_gfx->setDepthRange(0.006, 1.0);
+		draw3DPrism(obj, kTableParts[1], false); // table top
+		_gfx->setDepthRange(0.004, 1.0);
 		draw3DPrism(obj, kProjectorParts[1], false); // stand
+		_gfx->setDepthRange(0.002, 1.0);
 		draw3DPrism(obj, kProjectorParts[0], false); // body
+		_gfx->setDepthRange(0.0, 1.0);
 		draw3DPrism(obj, kProjectorParts[2], false); // lens
 		break;
 	case kObjTub:
-		for (int i = 0; i < 2; i++)
+		for (int i = 0; i < 2; i++) {
+			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kTubParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjSink:
-		for (int i = 0; i < 3; i++)
+		for (int i = 0; i < 3; i++) {
+			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kSinkParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjToilet:
-		for (int i = 0; i < 4; i++)
+		for (int i = 0; i < 4; i++) {
+			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kToiletParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjPToilet:
-		for (int i = 0; i < 5; i++)
+		for (int i = 0; i < 5; i++) {
+			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kPToiletParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjForkLift:
-		// Default draw order: forks, arms, treads, cab (back-to-front)
+		// Draw order: forks, arms, treads, cab (back-to-front)
+		_gfx->setDepthRange(0.010, 1.0);
 		draw3DPrism(obj, kForkliftParts[3], false); // FLLL (left fork)
+		_gfx->setDepthRange(0.008, 1.0);
 		draw3DPrism(obj, kForkliftParts[2], false); // FLUL (left arm)
+		_gfx->setDepthRange(0.006, 1.0);
 		draw3DPrism(obj, kForkliftParts[5], false); // FLLR (right fork)
+		_gfx->setDepthRange(0.004, 1.0);
 		draw3DPrism(obj, kForkliftParts[4], false); // FLUR (right arm)
+		_gfx->setDepthRange(0.002, 1.0);
 		draw3DPrism(obj, kForkliftParts[1], false); // treads
+		_gfx->setDepthRange(0.0, 1.0);
 		draw3DPrism(obj, kForkliftParts[0], false); // cab
 		break;
 	// === Robot types (1-20) ===
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index 347c9ecad9b..b039d477d1e 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -63,6 +63,8 @@ public:
 	virtual void setXorMode(bool enable) {}
 	virtual void setStippleData(const byte *data) {}
 	virtual void setMacColors(uint32 fg, uint32 bg) {}
+	virtual void setDepthState(bool testEnabled, bool writeEnabled) {}
+	virtual void setDepthRange(float nearVal, float farVal) {}
 	virtual void computeScreenViewport() = 0;
 
 	// Overlay a RGBA software surface onto the GL framebuffer (for Mac menu bar).
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index a535999c15f..87bc89e68c8 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -78,6 +78,16 @@ public:
 		_stippleFgColor = fg;
 		_stippleBgColor = bg;
 	}
+	void setDepthState(bool testEnabled, bool writeEnabled) override {
+		if (testEnabled)
+			glEnable(GL_DEPTH_TEST);
+		else
+			glDisable(GL_DEPTH_TEST);
+		glDepthMask(writeEnabled ? GL_TRUE : GL_FALSE);
+	}
+	void setDepthRange(float nearVal, float farVal) override {
+		glDepthRange(nearVal, farVal);
+	}
 	void computeScreenViewport() override;
 	void drawSurface(const Graphics::Surface *surf, int x, int y) override;
 
@@ -286,15 +296,14 @@ void OpenGLRenderer::draw3DWall(int x1, int y1, int x2, int y2, uint32 color) {
 			glPolygonOffset(1.1f, 4.0f);
 
 			if (_stippleData) {
-				// Two-pass stipple fill (Mac B&W dither pattern)
-				useColor(_stippleBgColor); // White background
+				useColor(_stippleBgColor);
 				glBegin(GL_QUADS);
 				glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
 				glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
 				glEnd();
 				glEnable(GL_POLYGON_STIPPLE);
 				glPolygonStipple(_stippleData);
-				useColor(_stippleFgColor); // Black foreground through stipple mask
+				useColor(_stippleFgColor);
 				glBegin(GL_QUADS);
 				glVertex3f(fx1, fy1, -160.0f); glVertex3f(fx2, fy2, -160.0f);
 				glVertex3f(fx2, fy2, 160.0f);  glVertex3f(fx1, fy1, 160.0f);
@@ -494,6 +503,8 @@ void OpenGLRenderer::draw3DLine(float x1, float y1, float z1, float x2, float y2
 void OpenGLRenderer::end3D() {
 	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 	glDisable(GL_DEPTH_TEST);
+	glDepthMask(GL_TRUE);
+	glDepthRange(0.0, 1.0);
 	glDisable(GL_SCISSOR_TEST);
 	
 	glViewport(0, 0, _system->getWidth(), _system->getHeight());


Commit: 5487317be66884dabf79fa76469f4550509e5c25
    https://github.com/scummvm/scummvm/commit/5487317be66884dabf79fa76469f4550509e5c25
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:29+02:00

Commit Message:
COLONY: implement DOS EGA color table from ROBOCOLR.C

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 038c0ab6162..fc9efda9cfb 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -36,75 +36,108 @@ static const int g_indexTable[4][10] = {
 	{0, 0,  0, 1,  1,  0,  0, -1,  2, 1}
 };
 
-// Color lookup: maps ObjColor constant → LINECOLOR (EGA palette index)
-// From DOS ROBOCOLR.C lsColor[index][LINECOLOR] (field 4).
-// Default DOS mode is polyfill=False: objects are wireframe-only, no polygon fills.
-// The fill color stays as the wall background; only outline color varies per surface.
-static uint8 lookupLineColor(int colorIdx) {
-	switch (colorIdx) {
-	case kColorClear:     return 0;  // Transparent (skipped before drawing)
-	case kColorBlack:     return 0;  // vBLACK
-	case kColorDkGray:    return 8;  // vDKGRAY
-	case kColorLtGreen:   return 10; // vLTGREEN
-	case kColorBath:      return 0;  // vBLACK
-	case kColorWater:     return 1;  // vBLUE
-	case kColorSilver:    return 1;  // vBLUE
-	case kColorReactor:   return 0;  // vBLACK (LINEFILLCOLOR for fill mode)
-	case kColorBlanket:   return 2;  // vGREEN
-	case kColorSheet:     return 0;  // vBLACK (LINEFILLCOLOR for fill mode)
-	case kColorBed:       return 6;  // vBROWN
-	case kColorBox:       return 6;  // vBROWN
-	case kColorBench:     return 0;  // vBLACK
-	case kColorChair:     return 1;  // vBLUE
-	case kColorChairBase: return 1;  // vBLUE
-	case kColorCouch:     return 4;  // vRED
-	case kColorConsole:   return 4;  // vRED
-	case kColorTV:        return 6;  // vBROWN
-	case kColorTVScreen:  return 8;  // vDKGRAY
-	case kColorDrawer:    return 6;  // vBROWN
-	case kColorDesk:      return 6;  // vBROWN
-	case kColorDeskTop:   return 6;  // vBROWN
-	case kColorDeskChair: return 2;  // vGREEN
-	case kColorMac:       return 0;  // vBLACK
-	case kColorMacScreen: return 8;  // vDKGRAY
-	case kColorCryo:      return 1;  // vBLUE
-	case kColorCryoGlass: return 1;  // vBLUE
-	case kColorCryoBase:  return 1;  // vBLUE
-	case kColorForklift:  return 14; // vYELLOW
-	case kColorTread1:    return 14; // vYELLOW
-	case kColorTread2:    return 14; // vYELLOW
-	case kColorPot:       return 6;  // vBROWN
-	case kColorPlant:     return 2;  // vGREEN
-	case kColorPower:     return 1;  // vBLUE
-	case kColorPBase:     return 1;  // vBLUE
-	case kColorPSource:   return 4;  // vRED
-	case kColorTable:     return 6;  // vBROWN
-	case kColorTableBase: return 6;  // vBROWN
-	case kColorPStand:    return 5;  // vMAGENTA
-	case kColorPLens:     return 0;  // vBLACK
-	case kColorProjector: return 3;  // vCYAN
-	case kColorTele:      return 4;  // vRED
-	case kColorTeleDoor:  return 1;  // vBLUE
-	case kColorWall:      return 0;  // vBLACK
-	case kColorRainbow1:  return 4;  // vRED
-	case kColorRainbow2:  return 14; // vYELLOW
-	// Robot colors (from ROBOCOLR.C fill color column 2)
-	case kColorCube:        return 3;  // vCYAN
-	case kColorDrone:       return 3;  // vCYAN
-	case kColorClaw1:       return 4;  // vRED
-	case kColorClaw2:       return 3;  // vCYAN
-	case kColorEyes:        return 15; // vINTWHITE
-	case kColorEye:         return 15; // vINTWHITE
-	case kColorIris:        return 1;  // vBLUE
-	case kColorPupil:       return 0;  // vBLACK
-	case kColorPyramid:     return 4;  // vRED
-	case kColorQueen:       return 14; // vYELLOW
-	case kColorTopSnoop:    return 3;  // vCYAN
-	case kColorBottomSnoop: return 8;  // vDKGRAY
-	case kColorUPyramid:    return 9;  // vLTBLUE
-	case kColorShadow:      return 8;  // vDKGRAY
-	default:              return 0;  // vBLACK
-	}
+// DOS lsColor table from ROBOCOLR.C — maps color index to EGA rendering attributes.
+// Fields: backColor (BACKCOLOR), fillColor (FILLCOLOR), lineFillColor (LINEFILLCOLOR),
+//         lineColor (LINECOLOR), pattern (PATTERN).
+// DrawPrism uses: polyfill ON → fill with fillColor/backColor/pattern, outline with lineFillColor.
+//                 polyfill OFF → outline only with lineColor.
+struct DOSColorEntry {
+	uint8 backColor;
+	uint8 fillColor;
+	uint8 lineFillColor;
+	uint8 lineColor;
+	uint8 pattern;
+};
+
+static const DOSColorEntry g_dosColors[79] = {
+	/* 0  cCLEAR      */ { 0,  0,  0,  0, 1},
+	/* 1  cBLACK      */ { 0,  0,  0,  0, 1},
+	/* 2  cBLUE       */ { 0,  1,  1,  1, 1},
+	/* 3  cGREEN      */ { 0,  2,  2,  2, 1},
+	/* 4  cCYAN       */ { 0,  3,  3,  3, 1},
+	/* 5  cRED        */ { 0,  4,  4,  4, 1},
+	/* 6  cMAGENTA    */ { 0,  5,  5,  5, 1},
+	/* 7  cBROWN      */ { 0,  6,  6,  6, 1},
+	/* 8  cWHITE      */ { 0,  7,  7,  7, 1},
+	/* 9  cDKGRAY     */ { 0,  8,  8,  8, 1},
+	/* 10 cLTBLUE     */ { 0,  9,  9,  9, 1},
+	/* 11 cLTGREEN    */ { 0, 10, 10, 10, 1},
+	/* 12 cLTCYAN     */ { 0, 11, 11, 11, 1},
+	/* 13 cLTRED      */ { 0, 12, 12, 12, 1},
+	/* 14 cLTMAGENTA  */ { 0, 13, 13, 13, 1},
+	/* 15 cYELLOW     */ { 0, 14, 14, 14, 1},
+	/* 16 cINTWHITE   */ { 0, 15, 15, 15, 1},
+	/* 17 cBATH       */ { 0, 15, 15,  0, 1},
+	/* 18 cWATER      */ { 0,  7,  1,  1, 4},
+	/* 19 cSILVER     */ { 0,  7, 15,  1, 3},
+	/* 20 cREACTOR    */ { 0,  7,  7,  7, 1},
+	/* 21 cBLANKET    */ { 0,  2,  7,  2, 3},
+	/* 22 cSHEET      */ { 0, 15, 15, 15, 1},
+	/* 23 cBED        */ { 0,  6,  4,  6, 4},
+	/* 24 cBOX        */ { 0,  6,  7,  6, 3},
+	/* 25 cBENCH      */ { 0,  6,  6,  6, 1},
+	/* 26 cCHAIR      */ { 1,  9,  7,  1, 3},
+	/* 27 cCHAIRBASE  */ { 1,  1,  9,  1, 3},
+	/* 28 cCOUCH      */ { 0,  4,  3,  4, 3},
+	/* 29 cCONSOLE    */ { 0,  6,  4,  4, 3},
+	/* 30 cTV         */ { 0,  6,  7,  6, 3},
+	/* 31 cTVSCREEN   */ { 8,  8,  7,  8, 1},
+	/* 32 cDRAWER     */ { 0,  6,  7,  6, 3},
+	/* 33 cCRYO       */ { 1,  9, 15,  1, 3},
+	/* 34 cCRYOGLASS  */ { 1, 15,  9,  1, 3},
+	/* 35 cCRYOBASE   */ { 1,  1,  9,  1, 3},
+	/* 36 cCUBE       */ { 0,  3, 15,  3, 3},
+	/* 37 cDESK       */ { 0,  6,  7,  6, 3},
+	/* 38 cDESKTOP    */ { 0,  6,  6,  6, 1},
+	/* 39 cDESKCHAIR  */ { 0,  2,  8,  2, 3},
+	/* 40 cMAC        */ { 0, 15, 15,  0, 1},
+	/* 41 cMACSCREEN  */ { 0,  8,  8,  8, 1},
+	/* 42 cDRONE      */ { 0,  3,  3,  0, 1},
+	/* 43 cCLAW1      */ { 0,  4,  4,  4, 1},
+	/* 44 cCLAW2      */ { 0,  3,  3,  3, 1},
+	/* 45 cEYES       */ {15, 15, 15, 15, 1},
+	/* 46 cEYE        */ {15, 15, 15, 15, 1},
+	/* 47 cIRIS       */ { 0,  1,  7,  1, 3},
+	/* 48 cPUPIL      */ { 0,  0,  0,  0, 3},
+	/* 49 cFORKLIFT   */ { 0, 14,  6, 14, 3},
+	/* 50 cTREAD1     */ { 0, 14,  6, 14, 4},
+	/* 51 cTREAD2     */ { 0, 14,  6, 14, 5},
+	/* 52 cPOT        */ { 0,  6,  6,  6, 1},
+	/* 53 cPLANT      */ { 2,  2,  2,  2, 1},
+	/* 54 cPOWER      */ { 1,  9,  7,  1, 3},
+	/* 55 cPBASE      */ { 1,  9,  7,  1, 3},
+	/* 56 cPSOURCE    */ { 4,  4, 14,  4, 3},
+	/* 57 cPYRAMID    */ { 0,  4, 15,  4, 3},
+	/* 58 cQUEEN      */ {14, 14, 15, 14, 3},
+	/* 59 cTOPSNOOP   */ { 5,  3,  5,  3, 3},
+	/* 60 cBOTTOMSNOOP*/ { 5,  8,  5,  5, 3},
+	/* 61 cTABLE      */ { 6,  6,  7,  6, 3},
+	/* 62 cTABLEBASE  */ { 6,  6,  8,  6, 3},
+	/* 63 cPSTAND     */ { 0,  5,  5,  5, 1},
+	/* 64 cPLENS      */ { 0,  0,  0,  0, 1},
+	/* 65 cPROJECTOR  */ { 0,  3,  3,  3, 1},
+	/* 66 cTELE       */ { 4,  8,  7,  4, 3},
+	/* 67 cTELEDOOR   */ { 0,  7, 15,  1, 3},
+	/* 68 cUPYRAMID   */ { 0,  9, 15,  1, 3},
+	/* 69 cROCK       */ { 4,  4,  4,  4, 1},
+	/* 70 cCOLONY     */ {14, 14, 14, 14, 1},
+	/* 71 cCDOOR      */ {14, 14, 14, 14, 1},
+	/* 72 cSHIP       */ { 9,  9,  9,  9, 1},
+	/* 73 cPROJ       */ { 3,  3,  3,  3, 1},
+	/* 74 cSHADOW     */ { 8,  8,  8,  8, 1},
+	/* 75 cLTGRAY     */ { 8,  8, 15,  8, 3},
+	/* 76 cGRAY       */ { 8,  8,  7,  8, 3},
+	/* 77 cWALL       */ { 0,  7,  7,  0, 3},
+	/* 78 cQUEEN2     */ { 0, 15, 15,  0, 3},
+};
+
+// Look up the DOS lsColor entry for a given ObjColor index.
+// Returns a fallback entry (all zeros) for out-of-range indices.
+static const DOSColorEntry &lookupDOSColor(int colorIdx) {
+	static const DOSColorEntry fallback = {0, 0, 0, 0, 1};
+	if (colorIdx >= 0 && colorIdx < 79)
+		return g_dosColors[colorIdx];
+	return fallback;
 }
 
 // Mac Classic dither patterns (from colorize.c cColor[].pattern).
@@ -198,6 +231,17 @@ static int lookupMacPattern(int colorIdx) {
 	}
 }
 
+// Map DOS EGA pattern value → GL stipple data for dithered fills.
+// Pattern 1 = solid fill (no stipple). Patterns 3-5 = mixed FILLCOLOR/BACKCOLOR.
+static const byte *dosPatternStipple(int pattern) {
+	switch (pattern) {
+	case 3: return kStippleGray;   // 50% dither
+	case 4: return kStippleDkGray; // 75% foreground
+	case 5: return kStippleLtGray; // 25% foreground
+	default: return nullptr;       // solid (pattern 1) or unknown
+	}
+}
+
 // Pack Mac 16-bit RGB into 32-bit ARGB with 0xFF000000 marker for direct RGB.
 static uint32 packMacColor(const uint16 rgb[3]) {
 	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
@@ -1372,11 +1416,32 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 					_gfx->draw3DPolygon(px, py, pz, count, 0); // black outline
 					_gfx->setStippleData(nullptr);
 				} else {
-					// EGA: global wall fill, per-surface colored outline
-					_gfx->draw3DPolygon(px, py, pz, count, (uint32)lookupLineColor(colorIdx));
+					// EGA: per-surface colors from DOS lsColor table.
+					// polyfill ON  → fill with FILLCOLOR/BACKCOLOR/PATTERN, outline with LINEFILLCOLOR.
+					// polyfill OFF → outline only with LINECOLOR.
+					const DOSColorEntry &dc = lookupDOSColor(colorIdx);
+					if (!_wireframe) {
+						// Polyfill mode: per-surface fill + LINEFILLCOLOR outline.
+						const byte *stipple = dosPatternStipple(dc.pattern);
+						if (stipple) {
+							_gfx->setMacColors((uint32)dc.fillColor, (uint32)dc.backColor);
+							_gfx->setStippleData(stipple);
+							_gfx->setWireframe(true, (uint32)dc.backColor);
+						} else {
+							_gfx->setWireframe(true, (uint32)dc.fillColor);
+						}
+						_gfx->draw3DPolygon(px, py, pz, count, (uint32)dc.lineFillColor);
+						if (stipple) _gfx->setStippleData(nullptr);
+					} else {
+						// Wireframe only: LINECOLOR outline, no fill.
+						_gfx->draw3DPolygon(px, py, pz, count, (uint32)dc.lineColor);
+					}
 				}
 			} else {
-				// Unlit: same for both modes — black fill, white outline
+				// Unlit: black fill, white outline (DOS: PenColor(vINTWHITE))
+				if (!_wireframe) {
+					_gfx->setWireframe(true, 0); // black fill
+				}
 				_gfx->draw3DPolygon(px, py, pz, count, 15);
 			}
 		}
@@ -1509,9 +1574,17 @@ void ColonyEngine::draw3DSphere(const Thing &obj, int pt0x, int pt0y, int pt0z,
 			_gfx->draw3DPolygon(px, py, pz, N, 0);
 			_gfx->setStippleData(nullptr);
 		} else {
-			_gfx->draw3DPolygon(px, py, pz, N, fillColor);
+			// EGA: per-surface fill + outline.
+			if (!_wireframe) {
+				_gfx->setWireframe(true, fillColor);
+			}
+			_gfx->draw3DPolygon(px, py, pz, N, outlineColor);
 		}
 	} else {
+		// Unlit: black fill, white outline.
+		if (!_wireframe) {
+			_gfx->setWireframe(true, 0);
+		}
 		_gfx->draw3DPolygon(px, py, pz, N, outlineColor);
 	}
 }
@@ -2380,8 +2453,8 @@ void ColonyEngine::renderCorridor3D() {
 		// Mac B&W: walls are pure white (c_dwall=WHITE); EGA: light gray (7)
 		wallFill = lit ? (macMode ? 255 : 7) : 0;
 		wallLine = lit ? 0 : (macMode ? 255 : 7);
-		floorColor = macMode ? (lit ? 0 : 255) : wallLine;
-		ceilColor  = macMode ? (lit ? 255 : 0) : wallLine;
+		floorColor = macMode ? (lit ? 0 : 255) : wallFill;
+		ceilColor  = macMode ? (lit ? 255 : 0) : wallFill;
 	}
 
 	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);


Commit: a451973a605a429508094726f7cf23b246a5e765
    https://github.com/scummvm/scummvm/commit/a451973a605a429508094726f7cf23b246a5e765
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:29+02:00

Commit Message:
COLONY: regression fixes for Mac (b&w)

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index fc9efda9cfb..905a4b1307b 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -36,12 +36,15 @@ static const int g_indexTable[4][10] = {
 	{0, 0,  0, 1,  1,  0,  0, -1,  2, 1}
 };
 
-// DOS lsColor table from ROBOCOLR.C — maps color index to EGA rendering attributes.
-// Fields: backColor (BACKCOLOR), fillColor (FILLCOLOR), lineFillColor (LINEFILLCOLOR),
+// DOS lsColor table from ROBOCOLR.C — maps color index to rendering attributes.
+// Fields: monochrome (MONOCHROME — Mac B&W dither pattern, matches MacPattern enum),
+//         backColor (BACKCOLOR), fillColor (FILLCOLOR), lineFillColor (LINEFILLCOLOR),
 //         lineColor (LINECOLOR), pattern (PATTERN).
 // DrawPrism uses: polyfill ON → fill with fillColor/backColor/pattern, outline with lineFillColor.
 //                 polyfill OFF → outline only with lineColor.
+// Mac B&W uses: monochrome field for dither pattern (WHITE/LTGRAY/GRAY/DKGRAY/BLACK/CLEAR).
 struct DOSColorEntry {
+	uint8 monochrome;
 	uint8 backColor;
 	uint8 fillColor;
 	uint8 lineFillColor;
@@ -50,91 +53,92 @@ struct DOSColorEntry {
 };
 
 static const DOSColorEntry g_dosColors[79] = {
-	/* 0  cCLEAR      */ { 0,  0,  0,  0, 1},
-	/* 1  cBLACK      */ { 0,  0,  0,  0, 1},
-	/* 2  cBLUE       */ { 0,  1,  1,  1, 1},
-	/* 3  cGREEN      */ { 0,  2,  2,  2, 1},
-	/* 4  cCYAN       */ { 0,  3,  3,  3, 1},
-	/* 5  cRED        */ { 0,  4,  4,  4, 1},
-	/* 6  cMAGENTA    */ { 0,  5,  5,  5, 1},
-	/* 7  cBROWN      */ { 0,  6,  6,  6, 1},
-	/* 8  cWHITE      */ { 0,  7,  7,  7, 1},
-	/* 9  cDKGRAY     */ { 0,  8,  8,  8, 1},
-	/* 10 cLTBLUE     */ { 0,  9,  9,  9, 1},
-	/* 11 cLTGREEN    */ { 0, 10, 10, 10, 1},
-	/* 12 cLTCYAN     */ { 0, 11, 11, 11, 1},
-	/* 13 cLTRED      */ { 0, 12, 12, 12, 1},
-	/* 14 cLTMAGENTA  */ { 0, 13, 13, 13, 1},
-	/* 15 cYELLOW     */ { 0, 14, 14, 14, 1},
-	/* 16 cINTWHITE   */ { 0, 15, 15, 15, 1},
-	/* 17 cBATH       */ { 0, 15, 15,  0, 1},
-	/* 18 cWATER      */ { 0,  7,  1,  1, 4},
-	/* 19 cSILVER     */ { 0,  7, 15,  1, 3},
-	/* 20 cREACTOR    */ { 0,  7,  7,  7, 1},
-	/* 21 cBLANKET    */ { 0,  2,  7,  2, 3},
-	/* 22 cSHEET      */ { 0, 15, 15, 15, 1},
-	/* 23 cBED        */ { 0,  6,  4,  6, 4},
-	/* 24 cBOX        */ { 0,  6,  7,  6, 3},
-	/* 25 cBENCH      */ { 0,  6,  6,  6, 1},
-	/* 26 cCHAIR      */ { 1,  9,  7,  1, 3},
-	/* 27 cCHAIRBASE  */ { 1,  1,  9,  1, 3},
-	/* 28 cCOUCH      */ { 0,  4,  3,  4, 3},
-	/* 29 cCONSOLE    */ { 0,  6,  4,  4, 3},
-	/* 30 cTV         */ { 0,  6,  7,  6, 3},
-	/* 31 cTVSCREEN   */ { 8,  8,  7,  8, 1},
-	/* 32 cDRAWER     */ { 0,  6,  7,  6, 3},
-	/* 33 cCRYO       */ { 1,  9, 15,  1, 3},
-	/* 34 cCRYOGLASS  */ { 1, 15,  9,  1, 3},
-	/* 35 cCRYOBASE   */ { 1,  1,  9,  1, 3},
-	/* 36 cCUBE       */ { 0,  3, 15,  3, 3},
-	/* 37 cDESK       */ { 0,  6,  7,  6, 3},
-	/* 38 cDESKTOP    */ { 0,  6,  6,  6, 1},
-	/* 39 cDESKCHAIR  */ { 0,  2,  8,  2, 3},
-	/* 40 cMAC        */ { 0, 15, 15,  0, 1},
-	/* 41 cMACSCREEN  */ { 0,  8,  8,  8, 1},
-	/* 42 cDRONE      */ { 0,  3,  3,  0, 1},
-	/* 43 cCLAW1      */ { 0,  4,  4,  4, 1},
-	/* 44 cCLAW2      */ { 0,  3,  3,  3, 1},
-	/* 45 cEYES       */ {15, 15, 15, 15, 1},
-	/* 46 cEYE        */ {15, 15, 15, 15, 1},
-	/* 47 cIRIS       */ { 0,  1,  7,  1, 3},
-	/* 48 cPUPIL      */ { 0,  0,  0,  0, 3},
-	/* 49 cFORKLIFT   */ { 0, 14,  6, 14, 3},
-	/* 50 cTREAD1     */ { 0, 14,  6, 14, 4},
-	/* 51 cTREAD2     */ { 0, 14,  6, 14, 5},
-	/* 52 cPOT        */ { 0,  6,  6,  6, 1},
-	/* 53 cPLANT      */ { 2,  2,  2,  2, 1},
-	/* 54 cPOWER      */ { 1,  9,  7,  1, 3},
-	/* 55 cPBASE      */ { 1,  9,  7,  1, 3},
-	/* 56 cPSOURCE    */ { 4,  4, 14,  4, 3},
-	/* 57 cPYRAMID    */ { 0,  4, 15,  4, 3},
-	/* 58 cQUEEN      */ {14, 14, 15, 14, 3},
-	/* 59 cTOPSNOOP   */ { 5,  3,  5,  3, 3},
-	/* 60 cBOTTOMSNOOP*/ { 5,  8,  5,  5, 3},
-	/* 61 cTABLE      */ { 6,  6,  7,  6, 3},
-	/* 62 cTABLEBASE  */ { 6,  6,  8,  6, 3},
-	/* 63 cPSTAND     */ { 0,  5,  5,  5, 1},
-	/* 64 cPLENS      */ { 0,  0,  0,  0, 1},
-	/* 65 cPROJECTOR  */ { 0,  3,  3,  3, 1},
-	/* 66 cTELE       */ { 4,  8,  7,  4, 3},
-	/* 67 cTELEDOOR   */ { 0,  7, 15,  1, 3},
-	/* 68 cUPYRAMID   */ { 0,  9, 15,  1, 3},
-	/* 69 cROCK       */ { 4,  4,  4,  4, 1},
-	/* 70 cCOLONY     */ {14, 14, 14, 14, 1},
-	/* 71 cCDOOR      */ {14, 14, 14, 14, 1},
-	/* 72 cSHIP       */ { 9,  9,  9,  9, 1},
-	/* 73 cPROJ       */ { 3,  3,  3,  3, 1},
-	/* 74 cSHADOW     */ { 8,  8,  8,  8, 1},
-	/* 75 cLTGRAY     */ { 8,  8, 15,  8, 3},
-	/* 76 cGRAY       */ { 8,  8,  7,  8, 3},
-	/* 77 cWALL       */ { 0,  7,  7,  0, 3},
-	/* 78 cQUEEN2     */ { 0, 15, 15,  0, 3},
+	//                     MONO BK  FILL LFIL LINE PAT
+	/* 0  cCLEAR      */ { 5,  0,  0,  0,  0, 1},
+	/* 1  cBLACK      */ { 4,  0,  0,  0,  0, 1},
+	/* 2  cBLUE       */ { 2,  0,  1,  1,  1, 1},
+	/* 3  cGREEN      */ { 1,  0,  2,  2,  2, 1},
+	/* 4  cCYAN       */ { 2,  0,  3,  3,  3, 1},
+	/* 5  cRED        */ { 2,  0,  4,  4,  4, 1},
+	/* 6  cMAGENTA    */ { 2,  0,  5,  5,  5, 1},
+	/* 7  cBROWN      */ { 3,  0,  6,  6,  6, 1},
+	/* 8  cWHITE      */ { 0,  0,  7,  7,  7, 1},
+	/* 9  cDKGRAY     */ { 3,  0,  8,  8,  8, 1},
+	/* 10 cLTBLUE     */ { 2,  0,  9,  9,  9, 1},
+	/* 11 cLTGREEN    */ { 1,  0, 10, 10, 10, 1},
+	/* 12 cLTCYAN     */ { 1,  0, 11, 11, 11, 1},
+	/* 13 cLTRED      */ { 1,  0, 12, 12, 12, 1},
+	/* 14 cLTMAGENTA  */ { 1,  0, 13, 13, 13, 1},
+	/* 15 cYELLOW     */ { 0,  0, 14, 14, 14, 1},
+	/* 16 cINTWHITE   */ { 0,  0, 15, 15, 15, 1},
+	/* 17 cBATH       */ { 0,  0, 15, 15,  0, 1},
+	/* 18 cWATER      */ { 1,  0,  7,  1,  1, 4},
+	/* 19 cSILVER     */ { 1,  0,  7, 15,  1, 3},
+	/* 20 cREACTOR    */ { 0,  0,  7,  7,  7, 1},
+	/* 21 cBLANKET    */ { 2,  0,  2,  7,  2, 3},
+	/* 22 cSHEET      */ { 0,  0, 15, 15, 15, 1},
+	/* 23 cBED        */ { 3,  0,  6,  4,  6, 4},
+	/* 24 cBOX        */ { 2,  0,  6,  7,  6, 3},
+	/* 25 cBENCH      */ { 2,  0,  6,  6,  6, 1},
+	/* 26 cCHAIR      */ { 1,  1,  9,  7,  1, 3},
+	/* 27 cCHAIRBASE  */ { 3,  1,  1,  9,  1, 3},
+	/* 28 cCOUCH      */ { 2,  0,  4,  3,  4, 3},
+	/* 29 cCONSOLE    */ { 1,  0,  6,  4,  4, 3},
+	/* 30 cTV         */ { 2,  0,  6,  7,  6, 3},
+	/* 31 cTVSCREEN   */ { 3,  8,  8,  7,  8, 1},
+	/* 32 cDRAWER     */ { 2,  0,  6,  7,  6, 3},
+	/* 33 cCRYO       */ { 2,  1,  9, 15,  1, 3},
+	/* 34 cCRYOGLASS  */ { 1,  1, 15,  9,  1, 3},
+	/* 35 cCRYOBASE   */ { 3,  1,  1,  9,  1, 3},
+	/* 36 cCUBE       */ { 1,  0,  3, 15,  3, 3},
+	/* 37 cDESK       */ { 2,  0,  6,  7,  6, 3},
+	/* 38 cDESKTOP    */ { 3,  0,  6,  6,  6, 1},
+	/* 39 cDESKCHAIR  */ { 1,  0,  2,  8,  2, 3},
+	/* 40 cMAC        */ { 0,  0, 15, 15,  0, 1},
+	/* 41 cMACSCREEN  */ { 3,  0,  8,  8,  8, 1},
+	/* 42 cDRONE      */ { 1,  0,  3,  3,  0, 1},
+	/* 43 cCLAW1      */ { 3,  0,  4,  4,  4, 1},
+	/* 44 cCLAW2      */ { 1,  0,  3,  3,  3, 1},
+	/* 45 cEYES       */ { 0, 15, 15, 15, 15, 1},
+	/* 46 cEYE        */ { 0, 15, 15, 15, 15, 1},
+	/* 47 cIRIS       */ { 1,  0,  1,  7,  1, 3},
+	/* 48 cPUPIL      */ { 4,  0,  0,  0,  0, 3},
+	/* 49 cFORKLIFT   */ { 1,  0, 14,  6, 14, 3},
+	/* 50 cTREAD1     */ { 3,  0, 14,  6, 14, 4},
+	/* 51 cTREAD2     */ { 3,  0, 14,  6, 14, 5},
+	/* 52 cPOT        */ { 2,  0,  6,  6,  6, 1},
+	/* 53 cPLANT      */ { 2,  2,  2,  2,  2, 1},
+	/* 54 cPOWER      */ { 1,  1,  9,  7,  1, 3},
+	/* 55 cPBASE      */ { 3,  1,  9,  7,  1, 3},
+	/* 56 cPSOURCE    */ { 2,  4,  4, 14,  4, 3},
+	/* 57 cPYRAMID    */ { 2,  0,  4, 15,  4, 3},
+	/* 58 cQUEEN      */ { 2, 14, 14, 15, 14, 3},
+	/* 59 cTOPSNOOP   */ { 2,  5,  3,  5,  3, 3},
+	/* 60 cBOTTOMSNOOP*/ { 3,  5,  8,  5,  5, 3},
+	/* 61 cTABLE      */ { 2,  6,  6,  7,  6, 3},
+	/* 62 cTABLEBASE  */ { 3,  6,  6,  8,  6, 3},
+	/* 63 cPSTAND     */ { 3,  0,  5,  5,  5, 1},
+	/* 64 cPLENS      */ { 4,  0,  0,  0,  0, 1},
+	/* 65 cPROJECTOR  */ { 2,  0,  3,  3,  3, 1},
+	/* 66 cTELE       */ { 1,  4,  8,  7,  4, 3},
+	/* 67 cTELEDOOR   */ { 1,  0,  7, 15,  1, 3},
+	/* 68 cUPYRAMID   */ { 1,  0,  9, 15,  1, 3},
+	/* 69 cROCK       */ { 2,  4,  4,  4,  4, 1},
+	/* 70 cCOLONY     */ { 2, 14, 14, 14, 14, 1},
+	/* 71 cCDOOR      */ { 2, 14, 14, 14, 14, 1},
+	/* 72 cSHIP       */ { 2,  9,  9,  9,  9, 1},
+	/* 73 cPROJ       */ { 2,  3,  3,  3,  3, 1},
+	/* 74 cSHADOW     */ { 3,  8,  8,  8,  8, 1},
+	/* 75 cLTGRAY     */ { 1,  8,  8, 15,  8, 3},
+	/* 76 cGRAY       */ { 2,  8,  8,  7,  8, 3},
+	/* 77 cWALL       */ { 0,  0,  7,  7,  0, 3},
+	/* 78 cQUEEN2     */ { 2,  0, 15, 15,  0, 3},
 };
 
 // Look up the DOS lsColor entry for a given ObjColor index.
-// Returns a fallback entry (all zeros) for out-of-range indices.
+// Returns a fallback entry for out-of-range indices.
 static const DOSColorEntry &lookupDOSColor(int colorIdx) {
-	static const DOSColorEntry fallback = {0, 0, 0, 0, 1};
+	static const DOSColorEntry fallback = {2, 0, 0, 0, 0, 1}; // GRAY monochrome
 	if (colorIdx >= 0 && colorIdx < 79)
 		return g_dosColors[colorIdx];
 	return fallback;
@@ -202,33 +206,11 @@ static const byte *kMacStippleData[] = {
 	nullptr         // kPatternClear  - outline only
 };
 
-// Map ObjColor constant → Mac dither pattern.
-// From colorize.c cColor[] array: ~90% of objects use GRAY,
-// special cases: c_dwall=WHITE, c_lwall=LTGRAY, c_window=DKGRAY,
-// c_desktop=WHITE, c_shelves=LTGRAY.
+// Map ObjColor → Mac B&W dither pattern (from ROBOCOLR.C MONOCHROME field).
+// The monochrome field in the DOS table matches MacPattern enum values directly:
+// WHITE=0, LTGRAY=1, GRAY=2, DKGRAY=3, BLACK=4, CLEAR=5.
 static int lookupMacPattern(int colorIdx) {
-	switch (colorIdx) {
-	case kColorClear:     return kPatternClear;
-	case kColorBlack:     return kPatternBlack;
-	case kColorWall:      return kPatternWhite;  // c_dwall = WHITE
-	case kColorDeskTop:   return kPatternWhite;  // c_desktop = WHITE
-	case kColorSheet:     return kPatternWhite;  // c_bedsheet = WHITE (bright surface)
-	case kColorBath:      return kPatternWhite;  // c_bath = WHITE (porcelain)
-	case kColorMac:       return kPatternWhite;  // c_computer = WHITE (bright casing)
-	case kColorSilver:    return kPatternLtGray; // c_mirror = LTGRAY
-	case kColorReactor:   return kPatternLtGray; // c_reactor = LTGRAY
-	case kColorTVScreen:  return kPatternDkGray; // c_tvscreen = DKGRAY
-	case kColorMacScreen: return kPatternDkGray; // c_screen = DKGRAY
-	case kColorWater:     return kPatternDkGray; // c_water = DKGRAY
-	// Robot colors (from ROBOCOLR.C Mac pattern column 0)
-	case kColorEyes:        return kPatternWhite;
-	case kColorEye:         return kPatternWhite;
-	case kColorPupil:       return kPatternBlack;
-	case kColorShadow:      return kPatternDkGray;
-	case kColorBottomSnoop: return kPatternDkGray;
-	case kColorClaw1:       return kPatternDkGray;
-	default:              return kPatternGray;   // Most objects = GRAY
-	}
+	return lookupDOSColor(colorIdx).monochrome;
 }
 
 // Map DOS EGA pattern value → GL stipple data for dithered fills.
@@ -2453,7 +2435,7 @@ void ColonyEngine::renderCorridor3D() {
 		// Mac B&W: walls are pure white (c_dwall=WHITE); EGA: light gray (7)
 		wallFill = lit ? (macMode ? 255 : 7) : 0;
 		wallLine = lit ? 0 : (macMode ? 255 : 7);
-		floorColor = macMode ? (lit ? 0 : 255) : wallFill;
+		floorColor = macMode ? (lit ? 255 : 0) : wallFill;
 		ceilColor  = macMode ? (lit ? 255 : 0) : wallFill;
 	}
 


Commit: 156e5b5a7fd3838875c83de84ad2c9dbe40cb396
    https://github.com/scummvm/scummvm/commit/156e5b5a7fd3838875c83de84ad2c9dbe40cb396
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:29+02:00

Commit Message:
COLONY: fix DOS polyfill to use B&W fill with colored outline

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 905a4b1307b..8fe188b2bff 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -1399,21 +1399,16 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 					_gfx->setStippleData(nullptr);
 				} else {
 					// EGA: per-surface colors from DOS lsColor table.
-					// polyfill ON  → fill with FILLCOLOR/BACKCOLOR/PATTERN, outline with LINEFILLCOLOR.
+					// polyfill ON  → B&W fill (from MONOCHROME field), colored LINECOLOR outline.
 					// polyfill OFF → outline only with LINECOLOR.
 					const DOSColorEntry &dc = lookupDOSColor(colorIdx);
 					if (!_wireframe) {
-						// Polyfill mode: per-surface fill + LINEFILLCOLOR outline.
-						const byte *stipple = dosPatternStipple(dc.pattern);
-						if (stipple) {
-							_gfx->setMacColors((uint32)dc.fillColor, (uint32)dc.backColor);
-							_gfx->setStippleData(stipple);
-							_gfx->setWireframe(true, (uint32)dc.backColor);
-						} else {
-							_gfx->setWireframe(true, (uint32)dc.fillColor);
-						}
-						_gfx->draw3DPolygon(px, py, pz, count, (uint32)dc.lineFillColor);
-						if (stipple) _gfx->setStippleData(nullptr);
+						// Polyfill mode: B&W fill + colored LINECOLOR outline.
+						// LINECOLOR (not LINEFILLCOLOR) — has proper contrast against B&W fills.
+						if (dc.monochrome == kPatternClear)
+							continue;
+						_gfx->setWireframe(true, 7); // all surfaces white; colored outlines provide distinction
+						_gfx->draw3DPolygon(px, py, pz, count, (uint32)dc.lineColor);
 					} else {
 						// Wireframe only: LINECOLOR outline, no fill.
 						_gfx->draw3DPolygon(px, py, pz, count, (uint32)dc.lineColor);


Commit: 1f15206feb878752102e91f07cdd853c76cae0e2
    https://github.com/scummvm/scummvm/commit/1f15206feb878752102e91f07cdd853c76cae0e2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:30+02:00

Commit Message:
COLONY: fix crash decoding mac animations

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 27415064c3b..6b01d9a30b6 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -2234,10 +2234,29 @@ Image *ColonyEngine::loadImage(Common::SeekableReadStream &file) {
 	int16 tf = readSint16(file);
 	uint32 size;
 	if (tf) {
-		/* uint32 bsize = */ readUint32(file);
+		// Mac original loadbitmap: reads bsize bytes into a buffer, then
+		// decompresses from that buffer. We must read exactly bsize bytes
+		// from the stream to keep file position aligned.
+		uint32 bsize = readUint32(file);
 		size = readUint32(file);
 		im->data = (byte *)malloc(size);
-		unpackBytes(file, im->data, size);
+		byte *packed = (byte *)calloc(bsize + 8, 1); // +8 matches original NewPtr(bsize+8)
+		file.read(packed, bsize);
+		// Decompress: exact match of Mac UnPackBytes(src, dst, len).
+		// Buffer is pairs of (count, value). Count is decremented in-place;
+		// when it reaches 0, advance to next pair.
+		byte *sp = packed;
+		for (uint32 di = 0; di < size; di++) {
+			if (*sp) {
+				im->data[di] = *(sp + 1);
+				(*sp)--;
+			} else {
+				sp += 2;
+				im->data[di] = *(sp + 1);
+				(*sp)--;
+			}
+		}
+		free(packed);
 	} else {
 		size = readUint32(file);
 		im->data = (byte *)malloc(size);
@@ -2258,19 +2277,22 @@ void ColonyEngine::unpackBytes(Common::SeekableReadStream &file, byte *dst, uint
 }
 
 Common::Rect ColonyEngine::readRect(Common::SeekableReadStream &file) {
+	int16 top, left, bottom, right;
 	if (getPlatform() == Common::kPlatformMacintosh) {
-		int16 top = readSint16(file);
-		int16 left = readSint16(file);
-		int16 bottom = readSint16(file);
-		int16 right = readSint16(file);
-		return Common::Rect(left, top, right, bottom);
+		top = readSint16(file);
+		left = readSint16(file);
+		bottom = readSint16(file);
+		right = readSint16(file);
 	} else {
-		int16 left = readSint16(file);
-		int16 top = readSint16(file);
-		int16 right = readSint16(file);
-		int16 bottom = readSint16(file);
-		return Common::Rect(left, top, right, bottom);
-	}
+		left = readSint16(file);
+		top = readSint16(file);
+		right = readSint16(file);
+		bottom = readSint16(file);
+	}
+	// Guard against invalid rects from animation data
+	if (left > right || top > bottom)
+		return Common::Rect();
+	return Common::Rect(left, top, right, bottom);
 }
 
 int16 ColonyEngine::readSint16(Common::SeekableReadStream &s) {


Commit: 856082f8dc219f97e004381ec862c0d1830b0ca8
    https://github.com/scummvm/scummvm/commit/856082f8dc219f97e004381ec862c0d1830b0ca8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:30+02:00

Commit Message:
COLONY: use original mac ui for b&w

Changed paths:
    engines/colony/ui.cpp


diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 065d53f4327..a21b728dd0e 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -128,11 +128,11 @@ void ColonyEngine::updateViewportLayout() {
 
 	int dashWidth = 0;
 	if (_showDashBoard) {
-		// Mac color mode: original moveWindow was 70px wide (2*CCENTER),
-		// infoWindow ~72px. theWindow started at x=96. Use tighter sizing.
-		const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
-		if (macColor)
-			dashWidth = CLIP<int>(_width / 7, 70, 96);
+		// Mac mode: original inits.c sets screenR.left=96 — fixed 96px sidebar.
+		// Two floating windows (infoWindow + moveWindow) centered in sidebar.
+		const bool isMac = (_renderMode == Common::kRenderMacintosh);
+		if (isMac)
+			dashWidth = MIN(96, _width / 2);
 		else
 			dashWidth = CLIP<int>(_width / 6, 72, 140);
 		if (_width - dashWidth < 160)
@@ -154,11 +154,11 @@ void ColonyEngine::updateViewportLayout() {
 		return;
 	}
 
-	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const bool isMac = (_renderMode == Common::kRenderMacintosh);
 	const int pad = 2;
 	const int topPad = menuTop + pad;
 
-	if (macColor) {
+	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.
@@ -172,7 +172,12 @@ void ColonyEngine::updateViewportLayout() {
 		if (!_pictCompass)
 			_pictCompass = loadPictSurface(-32757);
 		if (!_pictPower) {
-			int wantID = _armor ? -32755 : -32761;
+			// power.c: !armor → FindDepth()>=8 ? -32761 (color) : -32752 (B&W)
+			int wantID;
+			if (_armor > 0)
+				wantID = -32755;
+			else
+				wantID = _hasMacColors ? -32761 : -32752;
 			_pictPower = loadPictSurface(wantID);
 			if (!_pictPower && wantID != -32755)
 				_pictPower = loadPictSurface(-32755);
@@ -200,10 +205,11 @@ void ColonyEngine::updateViewportLayout() {
 		// _compassRect = entire moveWindow (used for compass dish drawing)
 		_compassRect = makeSafeRect(moveLeft, moveTop, moveLeft + moveW, moveTop + moveH);
 
-		// Position infoWindow right at the top of the sidebar (below menu bar).
-		// Original: infoWindow starts immediately below the Mac menu bar.
+		// 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;
+		const int infoTop = menuTop + pad;
 		_powerRect = makeSafeRect(infoLeft, infoTop, infoLeft + infoW, infoTop + infoH);
 	} else {
 		const int blockLeft = pad;
@@ -226,9 +232,9 @@ void ColonyEngine::drawDashboardStep1() {
 	if (_dashBoardRect.width() <= 0 || _dashBoardRect.height() <= 0)
 		return;
 
-	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const bool isMac = (_renderMode == Common::kRenderMacintosh);
 
-	if (macColor) {
+	if (isMac) {
 		drawDashboardMac();
 		return;
 	}
@@ -290,10 +296,12 @@ void ColonyEngine::drawDashboardStep1() {
 // Original Mac had two floating windows (infoWindow + moveWindow) over gray desktop.
 
 void ColonyEngine::drawDashboardMac() {
+	const bool macColor = _hasMacColors;
 	const uint32 colBlack = packRGB(0, 0, 0);
 	const uint32 colWhite = packRGB(255, 255, 255);
-	const uint32 colWinBg = packMacColorUI(_macColors[7].bg); // Mac desktop gray
-	const uint32 colBlue = packRGB(0, 0, 255); // power.c: ForeColor(blueColor)
+	const uint32 colWinBg = macColor ? packMacColorUI(_macColors[7].bg) : colWhite;
+	// power.c: ForeColor(blueColor) — on 1-bit display, blue maps to black
+	const uint32 colBlue = macColor ? packRGB(0, 0, 255) : colBlack;
 
 	// Dashboard background — Mac desktop dither pattern (classic 50% gray checkerboard).
 	// Original Mac desktop: alternating black/white pixels between floating windows.
@@ -312,6 +320,7 @@ void ColonyEngine::drawDashboardMac() {
 		_gfx->fillRect(_powerRect, colWhite);
 
 		// Select correct PICT based on armor/trouble state
+		// power.c: !armor && FindDepth()>=8 → -32761 (color); FindDepth()<8 → -32752 (B&W)
 		auto qlog = [](int32 x) -> int { int i = 0; while (x > 0) { x >>= 1; i++; } return i; };
 		const int ePower[3] = { qlog(_corePower[0]), qlog(_corePower[1]), qlog(_corePower[2]) };
 		const bool trouble = (ePower[1] < 6);
@@ -319,7 +328,7 @@ void ColonyEngine::drawDashboardMac() {
 		if (_armor > 0)
 			wantPictID = trouble ? -32760 : -32755;
 		else
-			wantPictID = -32761;
+			wantPictID = macColor ? -32761 : -32752;
 
 		// Reload PICT if state changed (fall back to -32755 if variant missing)
 		if (_pictPowerID != wantPictID) {
@@ -406,10 +415,10 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 			_gfx->drawLine(x1, y1, x2, y2, color);
 	};
 
-	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const bool isMac = (_renderMode == Common::kRenderMacintosh);
 
 	int lExt, sExt, xloc, yloc, ccenterx, ccentery;
-	if (macColor) {
+	if (isMac) {
 		// compass.c: CSIZE=64, CCENTER=35
 		// xloc = ((Me.xindex << 8) - Me.xloc) >> 2
 		// Center at (CCENTER, CCENTER) relative to moveWindow
@@ -456,14 +465,14 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 	drawMiniMapLine(xcorner[3] - dy, ycorner[3] - dx, xcorner[0] + dy, ycorner[0] + dx, lineColor);
 
 	// compass.c: food markers use FrameOval ±3px, robot markers ±5px.
-	const int foodR = macColor ? 3 : 1;
-	const int robotR = macColor ? 5 : 2;
+	const int foodR = isMac ? 3 : 1;
+	const int robotR = isMac ? 5 : 2;
 
 	auto drawMarker = [&](int x, int y, int halfSize, uint32 color) {
 		if (x < _headsUpRect.left + 1 || x >= _headsUpRect.right - 1 ||
 		    y < _headsUpRect.top + 1 || y >= _headsUpRect.bottom - 1)
 			return;
-		if (macColor) {
+		if (isMac) {
 			// compass.c: FrameOval — circle outline
 			_gfx->drawEllipse(x, y, halfSize, halfSize, color);
 		} else {
@@ -526,12 +535,16 @@ void ColonyEngine::drawCrosshair() {
 	if (!_crosshair || _screenR.width() <= 0 || _screenR.height() <= 0)
 		return;
 
-	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const bool isMac = (_renderMode == Common::kRenderMacintosh);
 	uint32 color;
-	if (macColor) {
+	if (isMac) {
 		// Mac: black when powered, gray when no weapons, white when armed but no power
-		color = (_corePower[_coreIndex] > 0) ? packRGB(0, 0, 0)
-			: (_weapons > 0) ? packRGB(255, 255, 255) : packRGB(128, 128, 128);
+		// B&W: no gray, so powered=black, else white
+		if (_hasMacColors)
+			color = (_corePower[_coreIndex] > 0) ? packRGB(0, 0, 0)
+				: (_weapons > 0) ? packRGB(255, 255, 255) : packRGB(128, 128, 128);
+		else
+			color = (_corePower[_coreIndex] > 0) ? packRGB(0, 0, 0) : packRGB(255, 255, 255);
 	} else {
 		color = (_weapons > 0) ? 15 : 7;
 		if (_corePower[_coreIndex] > 0)


Commit: d24de62a89b7f0a36c3f8187f6f0a9fd790e5c0b
    https://github.com/scummvm/scummvm/commit/d24de62a89b7f0a36c3f8187f6f0a9fd790e5c0b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:30+02:00

Commit Message:
COLONY: expand one-line conditionals to blocks and add code comments

Changed paths:
    engines/colony/colony.cpp
    engines/colony/render.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 6b01d9a30b6..7e9de096e92 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -296,9 +296,18 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 
 ColonyEngine::~ColonyEngine() {
 	deleteAnimation();
-	if (_pictPower) { _pictPower->free(); delete _pictPower; }
-	if (_pictPowerNoArmor) { _pictPowerNoArmor->free(); delete _pictPowerNoArmor; }
-	if (_pictCompass) { _pictCompass->free(); delete _pictCompass; }
+	if (_pictPower) {
+		_pictPower->free();
+		delete _pictPower;
+	}
+	if (_pictPowerNoArmor) {
+		_pictPowerNoArmor->free();
+		delete _pictPowerNoArmor;
+	}
+	if (_pictCompass) {
+		_pictCompass->free();
+		delete _pictCompass;
+	}
 	delete _frameLimiter;
 	delete _gfx;
 	delete _sound;
@@ -657,7 +666,8 @@ void ColonyEngine::loadMacColors() {
 			delete f;
 		}
 	}
-	if (!file) return;
+	if (!file)
+		return;
 
 	uint32 vers = file->readUint32BE(); // Should be 'v1.0' = 0x76312E30
 	(void)vers; // Ignore
@@ -1349,7 +1359,11 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 	bool qt = false;
 
 	for (int scrollOff = _height; scrollOff > 0 && !qt; scrollOff -= inc) {
-		if (checkSkipRequested()) { qt = true; _sound->stop(); break; }
+		if (checkSkipRequested()) {
+			qt = true;
+			_sound->stop();
+			break;
+		}
 
 		_gfx->clear(_gfx->black());
 		for (int i = 0; i < storyLength; i++) {
@@ -1379,7 +1393,11 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 	// Original: scrollRect continues moving up, text slides upward
 	if (!qt) {
 		for (int scrollOff = 0; scrollOff > -_height && !qt; scrollOff -= inc) {
-			if (checkSkipRequested()) { qt = true; _sound->stop(); break; }
+			if (checkSkipRequested()) {
+			qt = true;
+			_sound->stop();
+			break;
+		}
 
 			_gfx->clear(_gfx->black());
 			for (int i = 0; i < storyLength; i++) {
@@ -1448,7 +1466,8 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 		ysave1[i] = centerY + (int)(((long long)c * rr) >> 7);
 
 		int d2 = d - deltapd;
-		if (d2 < 1) d2 = 1;
+		if (d2 < 1)
+			d2 = 1;
 		rr = rtable[d2];
 		xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
 		ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
@@ -1459,7 +1478,10 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 
 	// Animate: original loops ~200 frames or until Mars sound repeats 2x
 	for (int k = 0; k < 120; k++) {
-		if (checkSkipRequested()) { _gfx->setXorMode(false); return true; }
+		if (checkSkipRequested()) {
+			_gfx->setXorMode(false);
+			return true;
+		}
 
 		for (int i = 0; i < NSTARS; i++) {
 			// Erase previous — XOR the same line again to restore underlying pixels
@@ -1495,7 +1517,10 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 	int nstars = 2 * ((MAXSTAR - 0x030) / deltapd);
 	if (nstars > 200) nstars = 200;
 	for (int k = 0; k < nstars; k++) {
-		if (checkSkipRequested()) { _gfx->setXorMode(false); return true; }
+		if (checkSkipRequested()) {
+			_gfx->setXorMode(false);
+			return true;
+		}
 
 		for (int i = 0; i < NSTARS; i++) {
 			int d = dist[i];
@@ -1507,7 +1532,8 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 			if (d >= 1 && d <= MAXSTAR) {
 				int rr1 = rtable[d];
 				int d2 = d - deltapd;
-				if (d2 < 1) d2 = 1;
+				if (d2 < 1)
+			d2 = 1;
 				int rr2 = rtable[d2];
 				int x1 = centerX + (int)(((long long)s * rr1) >> 7);
 				int y1 = centerY + (int)(((long long)c * rr1) >> 7);
@@ -1727,10 +1753,12 @@ bool ColonyEngine::drawPict(int resID) {
 			// Draw PICT pixels using direct RGB (packRGB) for full color support.
 			for (int iy = 0; iy < surface->h; iy++) {
 				int sy = y + iy;
-				if (sy < clipY1 || sy >= clipY2) continue;
+				if (sy < clipY1 || sy >= clipY2)
+					continue;
 				for (int ix = 0; ix < surface->w; ix++) {
 					int sx = x + ix;
-					if (sx < clipX1 || sx >= clipX2) continue;
+					if (sx < clipX1 || sx >= clipX2)
+						continue;
 					byte r, g, b;
 					if (isCLUT8) {
 						byte idx = *((const byte *)surface->getBasePtr(ix, iy));
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 8fe188b2bff..fccfe803402 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -1383,7 +1383,8 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 					// matches fill (invisible). Others: black outline.
 					uint32 outlineColor = (pattern == 4) ? fg : (uint32)0xFF000000;
 					_gfx->draw3DPolygon(px, py, pz, count, outlineColor);
-					if (stipple) _gfx->setStippleData(nullptr);
+					if (stipple)
+					_gfx->setStippleData(nullptr);
 				}
 			} else if (lit) {
 				if (_renderMode == Common::kRenderMacintosh) {
@@ -1540,7 +1541,8 @@ void ColonyEngine::draw3DSphere(const Thing &obj, int pt0x, int pt0y, int pt0z,
 		}
 		const byte *stipple = setupMacPattern(_gfx, pattern, fg, bg);
 		_gfx->draw3DPolygon(px, py, pz, N, fg);
-		if (stipple) _gfx->setStippleData(nullptr);
+		if (stipple)
+			_gfx->setStippleData(nullptr);
 	} else if (lit) {
 		if (_renderMode == Common::kRenderMacintosh) {
 			int pattern = (fillColor == 15) ? kPatternWhite : kPatternGray;
@@ -1850,7 +1852,8 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 			int pat = _macColors[macIdx].pattern;
 			const byte *stipple = setupMacPattern(_gfx, pat, fg, bg);
 			wallPolygon(corners, u, v, cnt, fg);
-			if (stipple) _gfx->setStippleData(nullptr);
+			if (stipple)
+			_gfx->setStippleData(nullptr);
 		} else if (macMode) {
 			_gfx->setStippleData(kStippleGray);
 			wallPolygon(corners, u, v, cnt, 0);
@@ -1904,10 +1907,22 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	// This prevents backside decorations (like Level 2 lines) from bleeding through.
 	// We use non-inclusive comparisons so features remain visible while standing on the boundary.
 	switch (direction) {
-	case kDirNorth: if (_me.yloc > (cellY + 1) * 256) return; break;
-	case kDirSouth: if (_me.yloc < cellY * 256) return;       break;
-	case kDirWest:  if (_me.xloc < cellX * 256) return;       break;
-	case kDirEast:  if (_me.xloc > (cellX + 1) * 256) return; break;
+	case kDirNorth:
+		if (_me.yloc > (cellY + 1) * 256)
+			return;
+		break;
+	case kDirSouth:
+		if (_me.yloc < cellY * 256)
+			return;
+		break;
+	case kDirWest:
+		if (_me.xloc < cellX * 256)
+			return;
+		break;
+	case kDirEast:
+		if (_me.xloc > (cellX + 1) * 256)
+			return;
+		break;
 	default: break;
 	}
 	
@@ -1924,7 +1939,8 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		uint32 bg = packMacColor(_macColors[macIdx].bg);
 		const byte *stipple = setupMacPattern(_gfx, _macColors[macIdx].pattern, fg, bg);
 		wallPolygon(corners, u, v, cnt, fg);
-		if (stipple) _gfx->setStippleData(nullptr);
+		if (stipple)
+			_gfx->setStippleData(nullptr);
 	};
 
 	switch (map[0]) {
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index a21b728dd0e..3a842e4ddad 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -321,7 +321,14 @@ void ColonyEngine::drawDashboardMac() {
 
 		// Select correct PICT based on armor/trouble state
 		// power.c: !armor && FindDepth()>=8 → -32761 (color); FindDepth()<8 → -32752 (B&W)
-		auto qlog = [](int32 x) -> int { int i = 0; while (x > 0) { x >>= 1; i++; } return i; };
+		auto qlog = [](int32 x) -> int {
+			int i = 0;
+			while (x > 0) {
+				x >>= 1;
+				i++;
+			}
+			return i;
+		};
 		const int ePower[3] = { qlog(_corePower[0]), qlog(_corePower[1]), qlog(_corePower[2]) };
 		const bool trouble = (ePower[1] < 6);
 		int wantPictID;
@@ -332,7 +339,11 @@ void ColonyEngine::drawDashboardMac() {
 
 		// Reload PICT if state changed (fall back to -32755 if variant missing)
 		if (_pictPowerID != wantPictID) {
-			if (_pictPower) { _pictPower->free(); delete _pictPower; _pictPower = nullptr; }
+			if (_pictPower) {
+				_pictPower->free();
+				delete _pictPower;
+				_pictPower = nullptr;
+			}
 			_pictPower = loadPictSurface(wantPictID);
 			if (!_pictPower && wantPictID != -32755)
 				_pictPower = loadPictSurface(-32755);
@@ -674,7 +685,11 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		if (yind2 > pobject->yindex) {
 			if (!(_wall[pobject->xindex][yind2] & 1))
 				return moveTo();
-			{ int r = tryFeature(kDirNorth); if (r != -2) return r; }
+			{
+				int r = tryFeature(kDirNorth);
+				if (r != -2)
+					return r;
+			}
 			debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
 			_sound->play(Sound::kBang);
 			return -1;
@@ -683,7 +698,11 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 1))
 			return moveTo();
-		{ int r = tryFeature(kDirSouth); if (r != -2) return r; }
+		{
+			int r = tryFeature(kDirSouth);
+			if (r != -2)
+				return r;
+		}
 		debug("Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
 		_sound->play(Sound::kBang);
 		return -1;
@@ -694,7 +713,11 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		if (xind2 > pobject->xindex) {
 			if (!(_wall[xind2][pobject->yindex] & 2))
 				return moveTo();
-			{ int r = tryFeature(kDirEast); if (r != -2) return r; }
+			{
+				int r = tryFeature(kDirEast);
+				if (r != -2)
+					return r;
+			}
 			debug("Collision East at x=%d y=%d", xind2, pobject->yindex);
 			_sound->play(Sound::kBang);
 			return -1;
@@ -703,7 +726,11 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 2))
 			return moveTo();
-		{ int r = tryFeature(kDirWest); if (r != -2) return r; }
+		{
+			int r = tryFeature(kDirWest);
+			if (r != -2)
+				return r;
+		}
 		debug("Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
 		_sound->play(Sound::kBang);
 		return -1;


Commit: ea2ff8bfda282b44a29d4e68ba9518dc8bb11a4a
    https://github.com/scummvm/scummvm/commit/ea2ff8bfda282b44a29d4e68ba9518dc8bb11a4a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:30+02:00

Commit Message:
COLONY: font color

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 7e9de096e92..56491c64dc1 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -1338,16 +1338,19 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 	int totalHeight = lineHeight * storyLength;
 	int ht = (_height - totalHeight) / 2;
 
-	// Set up blue gradient palette entries (200-213) for story text
+	// 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);
 	byte pal[14 * 3]; // storyLength entries
 	memset(pal, 0, sizeof(pal));
 	for (int i = 0; i < storyLength; i++) {
-		int blue = 255 - i * 16;
-		if (blue < 0) blue = 0;
-		pal[i * 3 + 0] = 0;     // R
-		pal[i * 3 + 1] = 0;     // G
-		pal[i * 3 + 2] = blue;  // B
+		int val = 255 - i * 16;
+		if (val < 0)
+			val = 0;
+		pal[i * 3 + 0] = bwMac ? val : 0;  // R
+		pal[i * 3 + 1] = bwMac ? val : 0;  // G
+		pal[i * 3 + 2] = val;               // B
 	}
 	_gfx->setPalette(pal, 200, storyLength);
 
@@ -1637,17 +1640,20 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 
 	int centery = _height / 2 - 10;
 
-	// Set up gradient palette entries (160-175) for the blue gradient lines
+	// Set up gradient palette entries (160-175) for the gradient lines
 	// Mac original: blue starts at 0xFFFF and decreases by 4096 per line pair
+	// B&W Mac: white gradient instead of blue
+	const bool bwMac = (macFont && !_hasMacColors);
 	for (int i = 0; i < 16; i++) {
-		int blue = 255 - i * 16; // 255, 239, 223, ... 15
-		if (blue < 0) blue = 0;
-		byte pal[3] = { 0, 0, (byte)blue };
+		int val = 255 - i * 16; // 255, 239, 223, ... 15
+		if (val < 0)
+			val = 0;
+		byte pal[3] = { (byte)(bwMac ? val : 0), (byte)(bwMac ? val : 0), (byte)val };
 		_gfx->setPalette(pal, 160 + i, 1);
 	}
-	// Set palette entry 176 for red text
+	// Set palette entry 176 for text (red in color, white in B&W)
 	{
-		byte pal[3] = { 255, 0, 0 };
+		byte pal[3] = { 255, (byte)(bwMac ? 255 : 0), (byte)(bwMac ? 255 : 0) };
 		_gfx->setPalette(pal, 176, 1);
 	}
 


Commit: a54c83f86aaab96c12fde8301d6d62d6a72f30a2
    https://github.com/scummvm/scummvm/commit/a54c83f86aaab96c12fde8301d6d62d6a72f30a2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:31+02:00

Commit Message:
COLONY: normalize comment characters and whitespace

Changed paths:
    engines/colony/colony.cpp
    engines/colony/render.cpp
    engines/colony/sound.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 56491c64dc1..c71ccda22ea 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -65,7 +65,7 @@ enum {
 	kMacGreen = 341, kMacBlue = 409
 };
 
-// BMColor arrays from ganimate.c — per-animation color maps.
+// BMColor arrays from ganimate.c  per-animation color maps.
 // Index 0 = background top, 1 = background image, 2+ = per-sprite fill.
 // Positive = cColor[] index, negative = -MacSystemColor, 0 = level-based.
 static const int16 kBMC_Desk[] = {
@@ -402,7 +402,7 @@ void ColonyEngine::loadMap(int mnum) {
 }
 
 // PATCH.C: Create a new object in _objects and register in _robotArray.
-// Mirrors DOS CreateObject() — sets basic Thing fields for static objects.
+// Mirrors DOS CreateObject()  sets basic Thing fields for static objects.
 void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
 	Thing obj;
 	memset(&obj, 0, sizeof(obj));
@@ -441,7 +441,7 @@ void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
 		_robotNum = slot + 1;
 }
 
-// PATCH.C: DoPatch() — remove originals and install relocated objects.
+// PATCH.C: DoPatch()  remove originals and install relocated objects.
 void ColonyEngine::doPatch() {
 	// Pass 1: remove objects that were moved away from this level
 	for (uint i = 0; i < _patches.size(); i++) {
@@ -465,7 +465,7 @@ void ColonyEngine::doPatch() {
 	}
 }
 
-// PATCH.C: savewall() — save 5 bytes of map feature data for persistence across level loads.
+// PATCH.C: savewall()  save 5 bytes of map feature data for persistence across level loads.
 void ColonyEngine::saveWall(int x, int y, int direction) {
 	if (_level < 1 || _level > 8)
 		return;
@@ -493,7 +493,7 @@ void ColonyEngine::saveWall(int x, int y, int direction) {
 	ld.size++;
 }
 
-// PATCH.C: getwall() — restore saved wall bytes into _mapData after level load.
+// PATCH.C: getwall()  restore saved wall bytes into _mapData after level load.
 void ColonyEngine::getWall() {
 	if (_level < 1 || _level > 8)
 		return;
@@ -509,7 +509,7 @@ void ColonyEngine::getWall() {
 	}
 }
 
-// PATCH.C: newpatch() — create or update a patch entry.
+// PATCH.C: newpatch()  create or update a patch entry.
 void ColonyEngine::newPatch(int type, const PassPatch &from, const PassPatch &to, const uint8 *mapdata) {
 	// Search for existing patch where 'from' matches an existing 'to'
 	for (uint i = 0; i < _patches.size(); i++) {
@@ -548,7 +548,7 @@ void ColonyEngine::newPatch(int type, const PassPatch &from, const PassPatch &to
 	_patches.push_back(pe);
 }
 
-// PATCH.C: patchmapto() — find patch entry by destination, fill mapdata.
+// PATCH.C: patchmapto()  find patch entry by destination, fill mapdata.
 bool ColonyEngine::patchMapTo(const PassPatch &to, uint8 *mapdata) {
 	for (uint i = 0; i < _patches.size(); i++) {
 		if (to.level == _patches[i].to.level &&
@@ -562,7 +562,7 @@ bool ColonyEngine::patchMapTo(const PassPatch &to, uint8 *mapdata) {
 	return false;
 }
 
-// PATCH.C: patchmapfrom() — find patch entry by source, fill destination into mapdata.
+// PATCH.C: patchmapfrom()  find patch entry by source, fill destination into mapdata.
 bool ColonyEngine::patchMapFrom(const PassPatch &from, uint8 *mapdata) {
 	for (uint i = 0; i < _patches.size(); i++) {
 		if (from.level == _patches[i].from.level &&
@@ -577,7 +577,7 @@ bool ColonyEngine::patchMapFrom(const PassPatch &from, uint8 *mapdata) {
 	return false;
 }
 
-// DOS InitObject() — spawn robots for the current level.
+// DOS InitObject()  spawn robots for the current level.
 // Level 1 = no robots; Level 2 = 25; Level 3-4 = 30; Level 5-7 = 35.
 // Robot #1 = QUEEN, #2 = SNOOP, rest = random type weighted by level.
 void ColonyEngine::initRobots() {
@@ -779,7 +779,7 @@ void ColonyEngine::initMacMenus() {
 		{1, "Save As...",                   kMenuActionSaveAs, 0, true},
 		{1, nullptr,                        0, 0, false},   // separator
 		{1, "Quit",                         kMenuActionQuit, 'Q', true},
-		// Edit submenu (index 2, disabled — original Mac had these but non-functional)
+		// Edit submenu (index 2, disabled  original Mac had these but non-functional)
 		{2, "Undo",                         0, 'Z', false},
 		{2, nullptr,                        0, 0, false},
 		{2, "Cut",                          0, 'X', false},
@@ -1188,7 +1188,7 @@ void ColonyEngine::playIntro() {
 		}
 
 		// Original: intro() in intro.c, lines 40-119
-		// qt flag propagates through sections — only modifier+click sets it
+		// qt flag propagates through sections  only modifier+click sets it
 		bool qt = false;
 
 		// 1. ScrollInfo() - scrolling story text with BeamMe sound
@@ -1199,7 +1199,7 @@ void ColonyEngine::playIntro() {
 		while (!qt && !shouldQuit() && _sound->isPlaying())
 			_system->delayMillis(10);
 
-		// Original: if(Button()) qt=OptionKey(); — check for skip
+		// Original: if(Button()) qt=OptionKey();  check for skip
 		if (!qt)
 			qt = checkSkipRequested();
 
@@ -1262,7 +1262,7 @@ void ColonyEngine::playIntro() {
 			}
 		}
 
-		// 11. Final crash — always runs (even if qt)
+		// 11. Final crash  always runs (even if qt)
 		// Original: FillRect black; if(!qt) while(!SoundDone());
 		_gfx->clear(_gfx->black());
 		_gfx->copyToScreen();
@@ -1413,7 +1413,7 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 		}
 	}
 
-	// Original does NOT stop the sound here — BeamMe continues playing
+	// Original does NOT stop the sound here  BeamMe continues playing
 	// and intro() waits for it with while(!SoundDone()) after ScrollInfo returns.
 	// Only stop if skipping (qt already stops in the modifier+click handlers above).
 	_gfx->clear(_gfx->black());
@@ -1450,7 +1450,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 			_gfx->setPixel(xx, yy, 15);
 	}
 
-	// Initialize moving stars — original uses PenMode(patXor) so stars
+	// Initialize moving stars  original uses PenMode(patXor) so stars
 	// don't damage the logo underneath (XOR drawing the same line twice
 	// restores the original pixels).
 	int xang[NSTARS], yang[NSTARS], dist[NSTARS];
@@ -1487,7 +1487,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 		}
 
 		for (int i = 0; i < NSTARS; i++) {
-			// Erase previous — XOR the same line again to restore underlying pixels
+			// Erase previous  XOR the same line again to restore underlying pixels
 			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
 
 			int s = xang[i];
@@ -1678,7 +1678,7 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 		_system->delayMillis(8);
 	}
 
-	// Phase 2: Klaxon flash — original: EndCSound(); then 6 iterations of:
+	// Phase 2: Klaxon flash  original: EndCSound(); then 6 iterations of:
 	//   if(Button()) if(qt=OptionKey()) break;
 	//   while(!SoundDone()); StopSound(); PlayKlaxon(); InvertRect(&invrt);
 	_sound->stop(); // EndCSound()
@@ -1692,7 +1692,7 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 
 		_sound->play(Sound::kKlaxon);
 
-		// InvertRect(&invrt) — toggle the text band
+		// InvertRect(&invrt)  toggle the text band
 		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), i % 2 ? 0 : 15);
 		_gfx->drawString(font, str, targetX, centery + 2, i % 2 ? 15 : 0, Graphics::kTextAlignLeft);
 		_gfx->copyToScreen();
@@ -2744,7 +2744,7 @@ void ColonyEngine::handleAnimationClick(int item) {
 				_animationRunning = false;
 			}
 		} else {
-			// Phase 2: inside — floor selection
+			// Phase 2: inside  floor selection
 			if (item >= 6 && item <= 10) {
 				int fl = item - 5;
 				if (fl == _elevatorFloor) {
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index fccfe803402..8055631fc4a 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -36,8 +36,8 @@ static const int g_indexTable[4][10] = {
 	{0, 0,  0, 1,  1,  0,  0, -1,  2, 1}
 };
 
-// DOS lsColor table from ROBOCOLR.C — maps color index to rendering attributes.
-// Fields: monochrome (MONOCHROME — Mac B&W dither pattern, matches MacPattern enum),
+// DOS lsColor table from ROBOCOLR.C  maps color index to rendering attributes.
+// Fields: monochrome (MONOCHROME  Mac B&W dither pattern, matches MacPattern enum),
 //         backColor (BACKCOLOR), fillColor (FILLCOLOR), lineFillColor (LINEFILLCOLOR),
 //         lineColor (LINECOLOR), pattern (PATTERN).
 // DrawPrism uses: polyfill ON → fill with fillColor/backColor/pattern, outline with lineFillColor.
@@ -213,17 +213,6 @@ static int lookupMacPattern(int colorIdx) {
 	return lookupDOSColor(colorIdx).monochrome;
 }
 
-// Map DOS EGA pattern value → GL stipple data for dithered fills.
-// Pattern 1 = solid fill (no stipple). Patterns 3-5 = mixed FILLCOLOR/BACKCOLOR.
-static const byte *dosPatternStipple(int pattern) {
-	switch (pattern) {
-	case 3: return kStippleGray;   // 50% dither
-	case 4: return kStippleDkGray; // 75% foreground
-	case 5: return kStippleLtGray; // 25% foreground
-	default: return nullptr;       // solid (pattern 1) or unknown
-	}
-}
-
 // Pack Mac 16-bit RGB into 32-bit ARGB with 0xFF000000 marker for direct RGB.
 static uint32 packMacColor(const uint16 rgb[3]) {
 	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
@@ -294,7 +283,7 @@ static int mapObjColorToMacColor(int colorIdx) {
 
 // Helper: set up Mac color stipple rendering for a given cColor[] pattern.
 // Configures setMacColors/setStippleData/setWireframe for the pattern type.
-// Returns the stipple pointer (null for solid patterns) — caller must clear with setStippleData(nullptr).
+// Returns the stipple pointer (null for solid patterns)  caller must clear with setStippleData(nullptr).
 static const byte *setupMacPattern(Renderer *gfx, int pattern, uint32 fg, uint32 bg) {
 	const byte *stipple = (pattern >= 1 && pattern <= 3) ? kMacStippleData[pattern] : nullptr;
 	if (stipple) {
@@ -1405,7 +1394,7 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 					const DOSColorEntry &dc = lookupDOSColor(colorIdx);
 					if (!_wireframe) {
 						// Polyfill mode: B&W fill + colored LINECOLOR outline.
-						// LINECOLOR (not LINEFILLCOLOR) — has proper contrast against B&W fills.
+						// LINECOLOR (not LINEFILLCOLOR)  has proper contrast against B&W fills.
 						if (dc.monochrome == kPatternClear)
 							continue;
 						_gfx->setWireframe(true, 7); // all surfaces white; colored outlines provide distinction
@@ -1584,7 +1573,7 @@ void ColonyEngine::computeVisibleCells() {
 	};
 
 	// BFS from player cell, stopping at walls AND wall features.
-	// This limits visibility to the current "room" — doors, shelves, etc.
+	// This limits visibility to the current "room"  doors, shelves, etc.
 	// act as solid barriers even if the cells are connected via open paths.
 	_visibleCell[px][py] = true;
 	int queueX[1024], queueY[1024];
@@ -1763,7 +1752,7 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 		// Mac drawchar(): sets cc=c_char0+level-1, fg=black, pattern=WHITE.
 		// SuperPoly fills with BACKGROUND color = cColor[cc].b.
 		// B&W: bg=black. Color: bg=c_char0.bg (e.g., yellow for level 1).
-		// Arrow shapes are concave — GL_POLYGON only handles convex polys.
+		// Arrow shapes are concave  GL_POLYGON only handles convex polys.
 		// Decompose into convex parts: triangle head + rectangle shaft.
 		if (_renderMode == Common::kRenderMacintosh) {
 			uint32 fillColor;
@@ -2079,7 +2068,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	}
 	case kWallFeatureUpStairs: {
-		// DOS: draw_up_stairs — staircase ascending into the wall with perspective
+		// DOS: draw_up_stairs  staircase ascending into the wall with perspective
 		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
 
 		// Mac: fill entire wall face (c_upstairs)
@@ -2156,7 +2145,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	}
 	case kWallFeatureDnStairs: {
-		// DOS: draw_dn_stairs — staircase descending into the wall with perspective
+		// DOS: draw_dn_stairs  staircase descending into the wall with perspective
 		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
 
 		// Mac: fill entire wall face (c_dnstairs)
@@ -2456,7 +2445,7 @@ void ColonyEngine::renderCorridor3D() {
 	uint32 wallColor = wallLine;
 
 	// --- Phase 1: Background (floor + ceiling) ---
-	// No depth test or write — these are pure background, everything overwrites them.
+	// No depth test or write  these are pure background, everything overwrites them.
 	_gfx->setDepthState(false, false);
 
 	// Draw large floor and ceiling quads.
@@ -2508,13 +2497,13 @@ void ColonyEngine::renderCorridor3D() {
 	}
 
 	// --- Phase 3: Wall & cell features ---
-	// Closer depth range than walls — features always beat their own wall surface.
+	// Closer depth range than walls  features always beat their own wall surface.
 	// Depth test still active so far-away features are hidden behind nearer walls.
 	_gfx->setDepthRange(0.005, 1.0);
 	drawWallFeatures3D();
 
 	// --- Phase 4: Objects ---
-	// Full depth range — objects beat walls and features at the same distance.
+	// Full depth range  objects beat walls and features at the same distance.
 	_gfx->setDepthRange(0.0, 1.0);
 
 	// F7 toggles object fill.
@@ -2650,7 +2639,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		draw3DPrism(obj, kCryoParts[0], false); // top second
 		break;
 	case kObjProjector:
-		// Projector sits on table — draw table first, then projector parts
+		// Projector sits on table  draw table first, then projector parts
 		_gfx->setDepthRange(0.008, 1.0);
 		draw3DPrism(obj, kTableParts[0], false); // table base
 		_gfx->setDepthRange(0.006, 1.0);
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index cb0b6a1f818..f52c83e477a 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -291,8 +291,8 @@ bool Sound::playMacSound(int soundID) {
 		return true;
 
 	// Fallback resource IDs for sounds missing from this binary version.
-	// MARS (23390) not in Zounds or Colony — use MARS2 (32291) from Zounds.
-	// BEAMME (5342) not in Zounds or Colony — use 32015 from Zounds.
+	// MARS (23390) not in Zounds or Colony  use MARS2 (32291) from Zounds.
+	// BEAMME (5342) not in Zounds or Colony  use 32015 from Zounds.
 	int altResID = -1;
 	switch (soundID) {
 	case kMars: altResID = 32291; break;   // MARS2
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 3a842e4ddad..02e644aadfe 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -128,7 +128,7 @@ void ColonyEngine::updateViewportLayout() {
 
 	int dashWidth = 0;
 	if (_showDashBoard) {
-		// Mac mode: original inits.c sets screenR.left=96 — fixed 96px sidebar.
+		// Mac mode: original inits.c sets screenR.left=96  fixed 96px sidebar.
 		// Two floating windows (infoWindow + moveWindow) centered in sidebar.
 		const bool isMac = (_renderMode == Common::kRenderMacintosh);
 		if (isMac)
@@ -160,10 +160,10 @@ void ColonyEngine::updateViewportLayout() {
 
 	if (isMac) {
 		// Original Mac layout from inits.c/compass.c/power.c:
-		// screenR.left = 96 — sidebar is 96px wide.
+		// screenR.left = 96  sidebar is 96px wide.
 		// Two floating windows centered in sidebar over gray desktop.
 		// moveWindow: compRect = (0,0, 2*CCENTER, 3*CCENTER) = (0,0, 70, 105)
-		//   floorRect (minimap) = (8,8)-(62,62) — 54x54 inside moveWindow
+		//   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
 		const int CCENTER = 35;
@@ -198,7 +198,7 @@ void ColonyEngine::updateViewportLayout() {
 		const int moveTop = _height - pad - moveH;
 
 		// _headsUpRect = floorRect (8,8)-(62,62) relative to moveWindow
-		// This is the minimap clipping area — must NOT overlap compass dish
+		// This is the minimap clipping area  must NOT overlap compass dish
 		_headsUpRect = makeSafeRect(moveLeft + 8, moveTop + 8,
 		                            moveLeft + 2 * CCENTER - 8, moveTop + 2 * CCENTER - 8);
 
@@ -300,10 +300,10 @@ void ColonyEngine::drawDashboardMac() {
 	const uint32 colBlack = packRGB(0, 0, 0);
 	const uint32 colWhite = packRGB(255, 255, 255);
 	const uint32 colWinBg = macColor ? packMacColorUI(_macColors[7].bg) : colWhite;
-	// power.c: ForeColor(blueColor) — on 1-bit display, blue maps to black
+	// power.c: ForeColor(blueColor)  on 1-bit display, blue maps to black
 	const uint32 colBlue = macColor ? packRGB(0, 0, 255) : colBlack;
 
-	// Dashboard background — Mac desktop dither pattern (classic 50% gray checkerboard).
+	// Dashboard background  Mac desktop dither pattern (classic 50% gray checkerboard).
 	// Original Mac desktop: alternating black/white pixels between floating windows.
 	_gfx->fillDitherRect(_dashBoardRect, colBlack, colWhite);
 
@@ -316,7 +316,7 @@ void ColonyEngine::drawDashboardMac() {
 	// armor && !trouble → PICT -32755; armor && trouble → PICT -32760;
 	// !armor && depth>=8 → PICT -32761; !armor && depth<8 → PICT -32752
 	if (_powerRect.width() > 4 && _powerRect.height() > 4) {
-		// power.c: FillRect(&clr, white) — white background under PICT
+		// power.c: FillRect(&clr, white)  white background under PICT
 		_gfx->fillRect(_powerRect, colWhite);
 
 		// Select correct PICT based on armor/trouble state
@@ -369,7 +369,7 @@ void ColonyEngine::drawDashboardMac() {
 					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
+					// 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);
@@ -393,7 +393,7 @@ void ColonyEngine::drawDashboardMac() {
 			_gfx->fillRect(_compassRect, colWinBg);
 
 		// Floor map (compass.c lines 72-213):
-		// floorRect = (8,8)-(62,62) — clipped to _headsUpRect
+		// floorRect = (8,8)-(62,62)  clipped to _headsUpRect
 		// Eye icon at center (CCENTER,CCENTER) = (35,35)
 		drawMiniMap(colBlack);
 
@@ -484,7 +484,7 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 		    y < _headsUpRect.top + 1 || y >= _headsUpRect.bottom - 1)
 			return;
 		if (isMac) {
-			// compass.c: FrameOval — circle outline
+			// compass.c: FrameOval  circle outline
 			_gfx->drawEllipse(x, y, halfSize, halfSize, color);
 		} else {
 			const int l = MAX<int>(_headsUpRect.left + 1, x - halfSize);
@@ -666,7 +666,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 	auto tryFeature = [&](int dir) -> int {
 		int r = tryPassThroughFeature(pobject->xindex, pobject->yindex, dir, pobject);
 		if (r == 2)
-			return 0; // teleported — position already updated by the feature
+			return 0; // teleported  position already updated by the feature
 		if (r == 1)
 			return moveTo();
 		return -2; // blocked, caller handles
@@ -842,7 +842,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 	switch (map[0]) {
 	case kWallFeatureDoor:
 		if (map[1] == 0)
-			return 1; // already open — pass through
+			return 1; // already open  pass through
 		if (pobject != &_me)
 			return 0; // robots can't open doors
 		// DOS DoDoor: play door animation, player clicks handle to open
@@ -864,7 +864,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 		}
 	case kWallFeatureAirlock:
 		if (map[1] == 0)
-			return 1; // already open — pass through
+			return 1; // already open  pass through
 		if (pobject != &_me)
 			return 0;
 		// DOS DoAirLock: play airlock animation
@@ -896,7 +896,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 		const int targetY = map[4];
 
 		if (targetMap == 0 && targetX == 0 && targetY == 0)
-			return 1; // no destination data — just pass through
+			return 1; // no destination data  just pass through
 
 		// Special cases from DOS
 		if (targetMap == 100) {
@@ -987,7 +987,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 			debug("Elevator: level=%d pos=(%d,%d)", _level, pobject->xindex, pobject->yindex);
 			return 2; // teleported
 		}
-		// Player entered but stayed on same floor — turn around
+		// Player entered but stayed on same floor  turn around
 		if (entered) {
 			pobject->ang += 128;
 			pobject->look = pobject->ang;
@@ -1060,7 +1060,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 	case kObjTeleport:
 	{
 		if (_fl == 1) {
-			// In empty forklift — pick up the teleporter itself
+			// In empty forklift  pick up the teleporter itself
 			if (loadAnimation("lift")) {
 				_animationResult = 0;
 				playAnimation();
@@ -1185,7 +1185,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 	case kObjBox2:
 	case kObjCryo:
 		if (_fl == 1) {
-			// In empty forklift — pick up object
+			// In empty forklift  pick up object
 			if (loadAnimation("lift")) {
 				_animationResult = 0;
 				playAnimation();
@@ -1211,13 +1211,13 @@ void ColonyEngine::interactWithObject(int objNum) {
 				}
 			}
 		} else if (_fl == 0 && obj.type == kObjCryo) {
-			// Not in forklift — read cryo text
+			// Not in forklift  read cryo text
 			doText(_action0, 0);
 		}
 		break;
 	case kObjReactor:
 		if (_fl == 1 && _coreState[_coreIndex] == 1) {
-			// Empty forklift at open reactor — pick up reactor core
+			// Empty forklift at open reactor  pick up reactor core
 			if (loadAnimation("lift")) {
 				_animationResult = 0;
 				playAnimation();
@@ -1230,7 +1230,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 				}
 			}
 		} else if (_fl == 2 && _carryType == kObjReactor && _coreState[_coreIndex] == 2) {
-			// Carrying reactor core — drop it into reactor
+			// Carrying reactor core  drop it into reactor
 			if (loadAnimation("lift")) {
 				_animationResult = 0;
 				playAnimation();
@@ -1431,7 +1431,7 @@ void ColonyEngine::fallThroughHole() {
 
 	_sound->play(Sound::kClatter);
 
-	// DOS tunnel(pt=TRUE): falling animation — nested rectangles shrinking toward
+	// DOS tunnel(pt=TRUE): falling animation  nested rectangles shrinking toward
 	// center, simulating falling down a shaft. White outlines on black background.
 	// DOS runs 10 steps × 2 frames = 20 display frames at ~15fps = ~1.3 seconds.
 	// At 60fps we use 80 frames for the same duration, paced by the frame limiter.
@@ -1450,7 +1450,7 @@ void ColonyEngine::fallThroughHole() {
 
 			float progress = (float)frame / totalFrames;
 
-			// Draw nested rectangles — outer ring shrinks in, inner rings follow
+			// Draw nested rectangles  outer ring shrinks in, inner rings follow
 			// The number of visible rings decreases as we fall deeper
 			int visibleRings = maxRings - (int)(progress * (maxRings - 1));
 			for (int ring = 0; ring < visibleRings; ring++) {
@@ -1491,7 +1491,7 @@ void ColonyEngine::fallThroughHole() {
 		_robotArray[targetX][targetY] = kMeNum;
 	}
 
-	// DOS: if(map) load_mapnum(map, TRUE) — always reload when map != 0
+	// DOS: if(map) load_mapnum(map, TRUE)  always reload when map != 0
 	if (targetMap > 0)
 		loadMap(targetMap);
 
@@ -1508,7 +1508,7 @@ void ColonyEngine::checkCenter() {
 		return;
 
 	switch (cellType) {
-	case 1: { // SMHOLEFLR — small floor hole, must be near center
+	case 1: { // SMHOLEFLR  small floor hole, must be near center
 		// DOS: xcheck=abs(xloc-(xindex<<8)); if(xcheck>64&&xcheck<192)
 		int xcheck = ABS(_me.xloc - (_me.xindex << 8));
 		int ycheck = ABS(_me.yloc - (_me.yindex << 8));
@@ -1516,10 +1516,10 @@ void ColonyEngine::checkCenter() {
 			fallThroughHole();
 		break;
 	}
-	case 2: // LGHOLEFLR — large floor hole, full cell
+	case 2: // LGHOLEFLR  large floor hole, full cell
 		fallThroughHole();
 		break;
-	case 5: // HOTFOOT — electric floor, damages power
+	case 5: // HOTFOOT  electric floor, damages power
 		// DOS: SetPower(-(5<<level),-(5<<level),-(5<<level))
 		for (int i = 0; i < 3; i++)
 			_corePower[i] -= (5 << _level);
@@ -1602,7 +1602,7 @@ void ColonyEngine::dropCarriedObject() {
 		_animationResult = 0;
 		playAnimation();
 		if (!_animationResult) {
-			// Animation was cancelled — don't drop
+			// Animation was cancelled  don't drop
 			return;
 		}
 	}


Commit: 0d8367edc50bd37712bc496110a8f9b9b5d28dd1
    https://github.com/scummvm/scummvm/commit/0d8367edc50bd37712bc496110a8f9b9b5d28dd1
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:31+02:00

Commit Message:
COLONY: register engine keymap bindings

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c71ccda22ea..c60e5609442 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -226,6 +226,12 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_wireframe = (_renderMode != Common::kRenderMacintosh);
 	_fullscreen = false;
 	_speedShift = 2; // DOS default: speedshift=1, but 2 feels better with our frame rate
+	_moveForward = false;
+	_moveBackward = false;
+	_strafeLeft = false;
+	_strafeRight = false;
+	_rotateLeft = false;
+	_rotateRight = false;
 	_wm = nullptr;
 	_macMenu = nullptr;
 	_menuSurface = nullptr;
@@ -897,6 +903,7 @@ Common::Error ColonyEngine::run() {
 
 	int mouseDX = 0, mouseDY = 0;
 	bool mouseMoved = false;
+	uint32 lastMoveTick = _system->getMillis();
 	while (!shouldQuit()) {
 		_frameLimiter->startFrame();
 		Common::Event event;
@@ -922,135 +929,115 @@ Common::Error ColonyEngine::run() {
 				}
 			}
 
-				if (event.type == Common::EVENT_KEYDOWN) {
-					debug("Key down: %d", event.kbd.keycode);
-					const bool allowInteraction = (event.kbd.flags & Common::KBD_CTRL) == 0;
-					// DOS movement: xai[ang] << speedshift, where xai[i] = cost[i] >> 4
-					const int moveX = (_cost[_me.look] * (1 << _speedShift)) >> 4;
-					const int moveY = (_sint[_me.look] * (1 << _speedShift)) >> 4;
-					const int rotSpeed = 1 << (_speedShift - 1); // DOS: speed = 1 << (speedshift-1)
-					switch (event.kbd.keycode) {
-					// Move forward (DOS: w / 8 / up)
-					case Common::KEYCODE_UP:
-					case Common::KEYCODE_w:
-						cCommand(_me.xloc + moveX, _me.yloc + moveY, allowInteraction);
-						break;
-					// Move backward (DOS: s / 5 / down)
-					case Common::KEYCODE_DOWN:
-					case Common::KEYCODE_s:
-						cCommand(_me.xloc - moveX, _me.yloc - moveY, allowInteraction);
-						break;
-					// Strafe left (DOS: a / 4 / left)
-					case Common::KEYCODE_LEFT:
-					case Common::KEYCODE_a:
-					{
-						uint8 strafeAngle = (uint8)((int)_me.look + 64);
-						int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
-						int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
-						cCommand(_me.xloc + sx, _me.yloc + sy, allowInteraction);
-						break;
+			if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
+				switch (event.customType) {
+				case kActionMoveForward:
+					_moveForward = true;
+					break;
+				case kActionMoveBackward:
+					_moveBackward = true;
+					break;
+				case kActionStrafeLeft:
+					_strafeLeft = true;
+					break;
+				case kActionStrafeRight:
+					_strafeRight = true;
+					break;
+				case kActionRotateLeft:
+					_rotateLeft = true;
+					break;
+				case kActionRotateRight:
+					_rotateRight = true;
+					break;
+				case kActionLookLeft:
+					_me.look = _me.ang + 64;
+					break;
+				case kActionLookRight:
+					_me.look = _me.ang - 64;
+					break;
+				case kActionLookBehind:
+					_me.look = _me.ang + 128;
+					break;
+				case kActionToggleDashboard:
+					_showDashBoard = !_showDashBoard;
+					break;
+				case kActionToggleWireframe:
+					_wireframe = !_wireframe;
+					debug("Polyfill: %s", _wireframe ? "off (wireframe)" : "on (filled)");
+					break;
+				case kActionToggleFullscreen:
+					if (_macMenu) {
+						_fullscreen = !_fullscreen;
+						_menuBarHeight = _fullscreen ? 0 : 20;
+						updateViewportLayout();
 					}
-					// Strafe right (DOS: d / 6 / right)
-					case Common::KEYCODE_RIGHT:
-					case Common::KEYCODE_d:
-					{
-						uint8 strafeAngle = (uint8)((int)_me.look - 64);
-						int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
-						int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
-						cCommand(_me.xloc + sx, _me.yloc + sy, allowInteraction);
-						break;
-					}
-					// Rotate left (DOS: q / 7)
-					case Common::KEYCODE_q:
-						_me.ang += rotSpeed;
-						_me.look += rotSpeed;
-						break;
-					// Rotate right (DOS: e / 9)
-					case Common::KEYCODE_e:
-						_me.ang -= rotSpeed;
-						_me.look -= rotSpeed;
-						break;
-					// Look left (DOS: z / 1)
-					case Common::KEYCODE_z:
-						_me.look = _me.ang + 64;
-						break;
-					// Look right (DOS: c / 3)
-					case Common::KEYCODE_c:
-						_me.look = _me.ang - 64;
-						break;
-					// Look behind (DOS: x / 2)
-					case Common::KEYCODE_x:
-						_me.look = _me.ang + 128;
-						break;
-					// Speed 1-5 (DOS: keys 1-5)
-					case Common::KEYCODE_1:
-					case Common::KEYCODE_2:
-					case Common::KEYCODE_3:
-					case Common::KEYCODE_4:
-					case Common::KEYCODE_5:
-						_speedShift = event.kbd.keycode - Common::KEYCODE_1 + 1;
-						debug("Speed: %d", _speedShift);
-						break;
-					// F7: toggle dashboard (DOS: doFunctionKey case 7)
-					case Common::KEYCODE_F7:
-						_showDashBoard = !_showDashBoard;
-						break;
-					// F8: toggle polyfill (DOS: doFunctionKey case 8)
-					case Common::KEYCODE_F8:
-						_wireframe = !_wireframe;
-						debug("Polyfill: %s", _wireframe ? "off (wireframe)" : "on (filled)");
-						break;
-					// F10: ScummVM menu (replaces DOS F10 pause)
-					case Common::KEYCODE_F10:
-						_system->lockMouse(false);
-						openMainMenuDialog();
-						_gfx->computeScreenViewport();
+					break;
+				case kActionToggleMouselook:
+					if (_fl == 2)
+						dropCarriedObject();
+					else if (_fl == 1)
+						exitForklift();
+					else {
+						_mouseLocked = !_mouseLocked;
 						_system->lockMouse(_mouseLocked);
 						if (_mouseLocked) {
 							_system->warpMouse(_centerX, _centerY);
 							_system->getEventManager()->purgeMouseEvents();
+							mouseDX = mouseDY = 0;
+							mouseMoved = false;
 						}
-						break;
-					// F11: toggle fullscreen (hide Mac menu bar)
-					case Common::KEYCODE_F11:
-						if (_macMenu) {
-							_fullscreen = !_fullscreen;
-							_menuBarHeight = _fullscreen ? 0 : 20;
-							updateViewportLayout();
-						}
-						break;
-					// Space: toggle mouselook / free cursor
-					case Common::KEYCODE_SPACE:
-						if (_fl == 2)
-							dropCarriedObject();
-						else if (_fl == 1)
-							exitForklift();
-						else {
-							_mouseLocked = !_mouseLocked;
-							_system->lockMouse(_mouseLocked);
-							if (_mouseLocked) {
-								_system->warpMouse(_centerX, _centerY);
-								_system->getEventManager()->purgeMouseEvents();
-								mouseDX = mouseDY = 0;
-								mouseMoved = false;
-							}
-						}
-						break;
-					// Escape: also opens ScummVM menu
-					case Common::KEYCODE_ESCAPE:
-						_system->lockMouse(false);
-						openMainMenuDialog();
-						_gfx->computeScreenViewport();
-						_system->lockMouse(_mouseLocked);
-						if (_mouseLocked) {
-							_system->warpMouse(_centerX, _centerY);
-							_system->getEventManager()->purgeMouseEvents();
-						}
-						break;
-					default:
-						break;
 					}
-				debug("Me: x=%d y=%d", _me.xloc, _me.yloc);
+					break;
+				case kActionEscape:
+					_system->lockMouse(false);
+					openMainMenuDialog();
+					_gfx->computeScreenViewport();
+					_system->lockMouse(_mouseLocked);
+					if (_mouseLocked) {
+						_system->warpMouse(_centerX, _centerY);
+						_system->getEventManager()->purgeMouseEvents();
+					}
+					break;
+				default:
+					break;
+				}
+			} else if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END) {
+				switch (event.customType) {
+				case kActionMoveForward:
+					_moveForward = false;
+					break;
+				case kActionMoveBackward:
+					_moveBackward = false;
+					break;
+				case kActionStrafeLeft:
+					_strafeLeft = false;
+					break;
+				case kActionStrafeRight:
+					_strafeRight = false;
+					break;
+				case kActionRotateLeft:
+					_rotateLeft = false;
+					break;
+				case kActionRotateRight:
+					_rotateRight = false;
+					break;
+				default:
+					break;
+				}
+			} else if (event.type == Common::EVENT_KEYDOWN) {
+				// Speed keys 1-5 remain as raw keyboard events
+				switch (event.kbd.keycode) {
+				case Common::KEYCODE_1:
+				case Common::KEYCODE_2:
+				case Common::KEYCODE_3:
+				case Common::KEYCODE_4:
+				case Common::KEYCODE_5:
+					_speedShift = event.kbd.keycode - Common::KEYCODE_1 + 1;
+					debug("Speed: %d", _speedShift);
+					break;
+				default:
+					break;
+				}
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
 				mouseDX += event.relMouse.x;
 				mouseDY += event.relMouse.y;
@@ -1075,8 +1062,43 @@ Common::Error ColonyEngine::run() {
 			mouseDX = mouseDY = 0;
 		}
 
+		// Apply continuous movement/rotation from held keys,
+		// throttled to ~15 ticks/sec to match original key-repeat feel
+		uint32 now = _system->getMillis();
+		if (now - lastMoveTick >= 66) {
+			lastMoveTick = now;
+			const int moveX = (_cost[_me.look] * (1 << _speedShift)) >> 4;
+			const int moveY = (_sint[_me.look] * (1 << _speedShift)) >> 4;
+			const int rotSpeed = 1 << (_speedShift - 1);
+
+			if (_moveForward)
+				cCommand(_me.xloc + moveX, _me.yloc + moveY, true);
+			if (_moveBackward)
+				cCommand(_me.xloc - moveX, _me.yloc - moveY, true);
+			if (_strafeLeft) {
+				uint8 strafeAngle = (uint8)((int)_me.look + 64);
+				int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
+				int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+				cCommand(_me.xloc + sx, _me.yloc + sy, true);
+			}
+			if (_strafeRight) {
+				uint8 strafeAngle = (uint8)((int)_me.look - 64);
+				int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
+				int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+				cCommand(_me.xloc + sx, _me.yloc + sy, true);
+			}
+			if (_rotateLeft) {
+				_me.ang += rotSpeed;
+				_me.look += rotSpeed;
+			}
+			if (_rotateRight) {
+				_me.ang -= rotSpeed;
+				_me.look -= rotSpeed;
+			}
+		}
+
 		_gfx->clear((_corePower[_coreIndex] > 0) ? 15 : 0);
-		
+
 		corridor();
 		drawDashboardStep1();
 		drawCrosshair();
@@ -1099,18 +1121,17 @@ Common::Error ColonyEngine::run() {
 }
 
 bool ColonyEngine::checkSkipRequested() {
-	// Non-blocking check for Shift+S skip during intro/animation sequences.
+	// Non-blocking check for skip during intro/animation sequences.
 	// Drains pending events, handles screen refresh and quit events.
-	// Returns true if Shift+S was pressed or shouldQuit() is true.
+	// Returns true if skip action fired or shouldQuit() is true.
 	Common::Event event;
 	while (_system->getEventManager()->pollEvent(event)) {
 		switch (event.type) {
 		case Common::EVENT_QUIT:
 		case Common::EVENT_RETURN_TO_LAUNCHER:
 			return true;
-		case Common::EVENT_KEYDOWN:
-			if (event.kbd.keycode == Common::KEYCODE_s &&
-			    (event.kbd.flags & Common::KBD_SHIFT))
+		case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
+			if (event.customType == kActionSkipIntro)
 				return true;
 			break;
 		case Common::EVENT_SCREEN_CHANGED:
@@ -1126,7 +1147,13 @@ bool ColonyEngine::checkSkipRequested() {
 bool ColonyEngine::waitForInput() {
 	// Blocking wait for any key press or mouse click.
 	// Handles screen refresh, quit events, and mouse movement while waiting.
-	// Returns true if Shift+S was pressed (for intro skip propagation).
+	// Returns true if skip action was triggered (for intro skip propagation).
+
+	// Clear movement flags so held keys don't re-trigger on return
+	_moveForward = _moveBackward = false;
+	_strafeLeft = _strafeRight = false;
+	_rotateLeft = _rotateRight = false;
+
 	while (!shouldQuit()) {
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
@@ -1134,11 +1161,11 @@ bool ColonyEngine::waitForInput() {
 			case Common::EVENT_QUIT:
 			case Common::EVENT_RETURN_TO_LAUNCHER:
 				return false;
-			case Common::EVENT_KEYDOWN:
-				if (event.kbd.keycode == Common::KEYCODE_s &&
-				    (event.kbd.flags & Common::KBD_SHIFT))
+			case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
+				if (event.customType == kActionSkipIntro)
 					return true;
 				return false;
+			case Common::EVENT_KEYDOWN:
 			case Common::EVENT_LBUTTONDOWN:
 				return false;
 			case Common::EVENT_SCREEN_CHANGED:
@@ -1904,6 +1931,14 @@ void ColonyEngine::deleteAnimation() {
 }
 
 void ColonyEngine::playAnimation() {
+	// Clear movement flags so held keys don't re-trigger on return
+	_moveForward = false;
+	_moveBackward = false;
+	_strafeLeft = false;
+	_strafeRight = false;
+	_rotateLeft = false;
+	_rotateRight = false;
+
 	_animationRunning = true;
 	_system->lockMouse(false);
 	_system->showMouse(true);
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index d5e49aca52f..10ae5f4b552 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -37,6 +37,25 @@
 
 namespace Colony {
 
+enum ColonyAction {
+	kActionNone,
+	kActionMoveForward,
+	kActionMoveBackward,
+	kActionStrafeLeft,
+	kActionStrafeRight,
+	kActionRotateLeft,
+	kActionRotateRight,
+	kActionLookLeft,
+	kActionLookRight,
+	kActionLookBehind,
+	kActionToggleMouselook,
+	kActionToggleDashboard,
+	kActionToggleWireframe,
+	kActionToggleFullscreen,
+	kActionSkipIntro,
+	kActionEscape
+};
+
 enum WallFeatureType {
 	kWallFeatureNone = 0,
 	kWallFeatureDoor = 2,
@@ -369,6 +388,14 @@ private:
 	bool _fullscreen;
 	int _speedShift; // 1-5, movement speed = 1 << (_speedShift - 1)
 
+	// Continuous movement flags (set/cleared by keymapper action events)
+	bool _moveForward;
+	bool _moveBackward;
+	bool _strafeLeft;
+	bool _strafeRight;
+	bool _rotateLeft;
+	bool _rotateRight;
+
 	Common::RandomSource _randomSource;
 	uint8 _decode1[4];
 	uint8 _decode2[4];
diff --git a/engines/colony/metaengine.cpp b/engines/colony/metaengine.cpp
index 0dc23c9631f..948d0234559 100644
--- a/engines/colony/metaengine.cpp
+++ b/engines/colony/metaengine.cpp
@@ -24,6 +24,9 @@
 #include "common/system.h"
 #include "common/translation.h"
 #include "common/config-manager.h"
+#include "backends/keymapper/action.h"
+#include "backends/keymapper/keymap.h"
+#include "backends/keymapper/standard-actions.h"
 
 namespace Colony {
 
@@ -57,8 +60,103 @@ public:
 		*engine = new ColonyEngine(syst, gd);
 		return Common::kNoError;
 	}
+
+	Common::KeymapArray initKeymaps(const char *target) const override;
 };
 
+Common::KeymapArray ColonyMetaEngine::initKeymaps(const char *target) const {
+	Common::Keymap *engineKeyMap = new Common::Keymap(Common::Keymap::kKeymapTypeGame, "colony", "The Colony");
+	Common::Action *act;
+
+	act = new Common::Action(Common::kStandardActionMoveUp, _("Move forward"));
+	act->setCustomEngineActionEvent(kActionMoveForward);
+	act->addDefaultInputMapping("UP");
+	act->addDefaultInputMapping("w");
+	act->addDefaultInputMapping("JOY_UP");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action(Common::kStandardActionMoveDown, _("Move backward"));
+	act->setCustomEngineActionEvent(kActionMoveBackward);
+	act->addDefaultInputMapping("DOWN");
+	act->addDefaultInputMapping("s");
+	act->addDefaultInputMapping("JOY_DOWN");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action(Common::kStandardActionMoveLeft, _("Strafe left"));
+	act->setCustomEngineActionEvent(kActionStrafeLeft);
+	act->addDefaultInputMapping("LEFT");
+	act->addDefaultInputMapping("a");
+	act->addDefaultInputMapping("JOY_LEFT");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action(Common::kStandardActionMoveRight, _("Strafe right"));
+	act->setCustomEngineActionEvent(kActionStrafeRight);
+	act->addDefaultInputMapping("RIGHT");
+	act->addDefaultInputMapping("d");
+	act->addDefaultInputMapping("JOY_RIGHT");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action("ROTL", _("Rotate left"));
+	act->setCustomEngineActionEvent(kActionRotateLeft);
+	act->addDefaultInputMapping("q");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action("ROTR", _("Rotate right"));
+	act->setCustomEngineActionEvent(kActionRotateRight);
+	act->addDefaultInputMapping("e");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action("LOOKL", _("Look left"));
+	act->setCustomEngineActionEvent(kActionLookLeft);
+	act->addDefaultInputMapping("z");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action("LOOKR", _("Look right"));
+	act->setCustomEngineActionEvent(kActionLookRight);
+	act->addDefaultInputMapping("c");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action("LOOKB", _("Look behind"));
+	act->setCustomEngineActionEvent(kActionLookBehind);
+	act->addDefaultInputMapping("x");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action("MOUSE", _("Toggle mouselook"));
+	act->setCustomEngineActionEvent(kActionToggleMouselook);
+	act->addDefaultInputMapping("SPACE");
+	act->addDefaultInputMapping("JOY_A");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action("DASH", _("Toggle dashboard"));
+	act->setCustomEngineActionEvent(kActionToggleDashboard);
+	act->addDefaultInputMapping("F7");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action("WIRE", _("Toggle wireframe"));
+	act->setCustomEngineActionEvent(kActionToggleWireframe);
+	act->addDefaultInputMapping("F8");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action("FULL", _("Toggle fullscreen"));
+	act->setCustomEngineActionEvent(kActionToggleFullscreen);
+	act->addDefaultInputMapping("F11");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action("SKIP", _("Skip intro"));
+	act->setCustomEngineActionEvent(kActionSkipIntro);
+	act->addDefaultInputMapping("S+s");
+	act->addDefaultInputMapping("JOY_X");
+	engineKeyMap->addAction(act);
+
+	act = new Common::Action("ESCAPE", _("Menu"));
+	act->setCustomEngineActionEvent(kActionEscape);
+	act->addDefaultInputMapping("ESCAPE");
+	act->addDefaultInputMapping("JOY_BACK");
+	engineKeyMap->addAction(act);
+
+	return Common::Keymap::arrayOf(engineKeyMap);
+}
+
 } // End of namespace Colony
 
 #if PLUGIN_ENABLED_DYNAMIC(COLONY)


Commit: f5312417f53e4ba21270cca97a30726a27aec23d
    https://github.com/scummvm/scummvm/commit/f5312417f53e4ba21270cca97a30726a27aec23d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:31+02:00

Commit Message:
COLONY: extract animation, interaction, intro, map and movement modules

Changed paths:
  A engines/colony/animation.cpp
  A engines/colony/interaction.cpp
  A engines/colony/intro.cpp
  A engines/colony/map.cpp
  A engines/colony/movement.cpp
    engines/colony/colony.cpp
    engines/colony/module.mk
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
new file mode 100644
index 00000000000..7c855872d8c
--- /dev/null
+++ b/engines/colony/animation.cpp
@@ -0,0 +1,1466 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "colony/gfx.h"
+#include "common/system.h"
+#include "common/events.h"
+#include "common/debug.h"
+#include "common/file.h"
+#include "graphics/cursorman.h"
+#include <math.h>
+
+namespace Colony {
+
+// Mac color indices from colordef.h enum (cColor[] table in Color256).
+enum {
+	mc_dwall = 6, mc_lwall = 7,
+	mc_char0 = 8,    // char0..char6 = 8..14
+	mc_bulkhead = 15, mc_door = 16,
+	mc_desk = 58, mc_desktop = 59, mc_screen = 62,
+	mc_proj = 72, mc_console = 79, mc_powerbase = 81,
+	mc_box1 = 84, mc_forklift = 86, mc_flglass = 87,
+	mc_cryo = 90, mc_ccore = 111,
+	mc_teleport = 93, mc_teledoor = 94,
+	mc_vanity = 96, mc_mirror = 103,
+	mc_airlock = 25, mc_elevator = 23
+};
+
+// Mac Toolbox BackColor() constants.
+enum {
+	kMacWhite = 30, kMacBlack = 33,
+	kMacYellow = 69, kMacMagenta = 137,
+	kMacRed = 205, kMacCyan = 273,
+	kMacGreen = 341, kMacBlue = 409
+};
+
+// BMColor arrays from ganimate.c  per-animation color maps.
+// Index 0 = background top, 1 = background image, 2+ = per-sprite fill.
+// Positive = cColor[] index, negative = -MacSystemColor, 0 = level-based.
+static const int16 kBMC_Desk[] = {
+	0, mc_desktop,
+	-kMacRed, -kMacCyan, -kMacCyan, -kMacCyan, -kMacCyan,
+	-kMacWhite, -kMacWhite, -kMacMagenta, -kMacYellow, mc_desk,
+	mc_desk, mc_desk, mc_desk, mc_desk, mc_desk,
+	-kMacWhite, mc_screen, -kMacMagenta, -kMacCyan, -kMacCyan,
+	-kMacBlue, -kMacWhite, -kMacRed, -kMacWhite, -kMacYellow
+};
+static const int16 kBMC_Vanity[] = {
+	0, mc_vanity,
+	mc_mirror, -kMacRed, -kMacCyan, -kMacWhite, -kMacYellow,
+	-kMacGreen, -kMacBlue, -kMacRed, -kMacMagenta, -kMacRed,
+	mc_vanity, -kMacWhite, -kMacYellow, mc_mirror
+};
+static const int16 kBMC_Reactor[] = {
+	0, mc_console,
+	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
+	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
+	-kMacYellow, -kMacYellow, -kMacRed, -kMacRed, -kMacRed,
+	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacRed,
+	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacCyan,
+	-kMacMagenta, -kMacWhite
+};
+static const int16 kBMC_Security[] = {
+	0, mc_console,
+	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
+	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
+	-kMacYellow, -kMacYellow, -kMacRed, -kMacRed, -kMacRed,
+	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacRed,
+	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacWhite,
+	-kMacRed, -kMacCyan, -kMacCyan, -kMacCyan, -kMacCyan
+};
+static const int16 kBMC_Teleport[] = {
+	0, mc_teleport, 0, mc_teledoor
+};
+static const int16 kBMC_Creatures[] = {
+	-kMacWhite, 0, -kMacWhite, -kMacCyan, mc_proj,
+	-kMacBlue, -kMacMagenta, -kMacMagenta
+};
+static const int16 kBMC_Controls[] = {
+	0, mc_console,
+	-kMacRed, -kMacYellow, -kMacYellow, -kMacBlue, -kMacYellow, -kMacGreen, mc_screen
+};
+static const int16 kBMC_Lift[] = {
+	0, mc_flglass,
+	mc_teleport, mc_box1, mc_cryo, mc_ccore, 0,
+	-kMacRed, -kMacRed, -kMacCyan, -kMacCyan
+};
+static const int16 kBMC_Powersuit[] = {
+	0, mc_powerbase,
+	-kMacMagenta, -kMacMagenta, -kMacYellow, -kMacYellow, mc_powerbase, -kMacWhite
+};
+static const int16 kBMC_Forklift[] = {
+	0, mc_forklift, mc_forklift, mc_forklift
+};
+static const int16 kBMC_Door[] = {
+	0, mc_bulkhead, 0, mc_door, -kMacYellow
+};
+static const int16 kBMC_Bulkhead[] = {
+	0, mc_bulkhead, 0, mc_bulkhead, -kMacYellow
+};
+static const int16 kBMC_Airlock[] = {
+	0, mc_bulkhead, mc_bulkhead, -kMacRed, mc_airlock
+};
+static const int16 kBMC_Elevator[] = {
+	0, mc_bulkhead, 0, mc_elevator, mc_elevator, -kMacYellow
+};
+static const int16 kBMC_Elevator2[] = {
+	0, mc_bulkhead, 0, -kMacMagenta, mc_elevator, mc_elevator,
+	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow
+};
+
+struct AnimColorEntry {
+	const char *name;
+	const int16 *colors;
+	int count;
+};
+
+static const AnimColorEntry kAnimColors[] = {
+	{ "desk",        kBMC_Desk,       ARRAYSIZE(kBMC_Desk) },
+	{ "vanity",      kBMC_Vanity,     ARRAYSIZE(kBMC_Vanity) },
+	{ "reactor",     kBMC_Reactor,    ARRAYSIZE(kBMC_Reactor) },
+	{ "security",    kBMC_Security,   ARRAYSIZE(kBMC_Security) },
+	{ "teleporter",  kBMC_Teleport,   ARRAYSIZE(kBMC_Teleport) },
+	{ "teleporter2", kBMC_Teleport,   ARRAYSIZE(kBMC_Teleport) },
+	{ "slides",      kBMC_Creatures,  ARRAYSIZE(kBMC_Creatures) },
+	{ "slideshow",   kBMC_Creatures,  ARRAYSIZE(kBMC_Creatures) },
+	{ "teleshow",    kBMC_Creatures,  ARRAYSIZE(kBMC_Creatures) },
+	{ "controls",    kBMC_Controls,   ARRAYSIZE(kBMC_Controls) },
+	{ "lift",        kBMC_Lift,       ARRAYSIZE(kBMC_Lift) },
+	{ "lifter",      kBMC_Lift,       ARRAYSIZE(kBMC_Lift) },
+	{ "suit",        kBMC_Powersuit,  ARRAYSIZE(kBMC_Powersuit) },
+	{ "spacesuit",   kBMC_Powersuit,  ARRAYSIZE(kBMC_Powersuit) },
+	{ "forklift",    kBMC_Forklift,   ARRAYSIZE(kBMC_Forklift) },
+	{ "door",        kBMC_Door,       ARRAYSIZE(kBMC_Door) },
+	{ "bulkhead",    kBMC_Bulkhead,   ARRAYSIZE(kBMC_Bulkhead) },
+	{ "airlock",     kBMC_Airlock,    ARRAYSIZE(kBMC_Airlock) },
+	{ "elev",        kBMC_Elevator,   ARRAYSIZE(kBMC_Elevator) },
+	{ "elevator",    kBMC_Elevator,   ARRAYSIZE(kBMC_Elevator) },
+	{ "elevator2",   kBMC_Elevator2,  ARRAYSIZE(kBMC_Elevator2) },
+	{ nullptr, nullptr, 0 }
+};
+
+// Convert Mac Toolbox BackColor constant to ARGB.
+static uint32 macSysColorToARGB(int sysColor) {
+	switch (sysColor) {
+	case kMacWhite:   return 0xFFFFFFFF;
+	case kMacBlack:   return 0xFF000000;
+	case kMacYellow:  return 0xFFFFFF00;
+	case kMacMagenta: return 0xFFFF00FF;
+	case kMacRed:     return 0xFFFF0000;
+	case kMacCyan:    return 0xFF00FFFF;
+	case kMacGreen:   return 0xFF00FF00;
+	case kMacBlue:    return 0xFF0000FF;
+	default:          return 0xFFFFFFFF;
+	}
+}
+
+static uint32 packMacColorBG(const uint16 rgb[3]) {
+	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
+	       ((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
+}
+
+bool ColonyEngine::loadAnimation(const Common::String &name) {
+	_animationName = name;
+	for (int i = 0; i < 6; i++)
+		_animDisplay[i] = 1;
+
+	// Look up per-animation BMColor map (from ganimate.c).
+	_animBMColors.clear();
+	Common::String nameLower = name;
+	nameLower.toLowercase();
+	for (const AnimColorEntry *e = kAnimColors; e->name; e++) {
+		if (nameLower == e->name) {
+			_animBMColors.resize(e->count);
+			for (int i = 0; i < e->count; i++)
+				_animBMColors[i] = e->colors[i];
+			break;
+		}
+	}
+
+	Common::String fileName = name + ".pic";
+	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
+	if (!file) {
+		// Try lowercase for Mac
+		fileName = name;
+		fileName.toLowercase();
+		file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
+		if (!file) {
+			// Try CData directory
+			fileName = "CData/" + fileName;
+			file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
+			if (!file) {
+				warning("Could not open animation file %s", name.c_str());
+				return false;
+			}
+		}
+	}
+
+	deleteAnimation();
+
+	// Read background data
+	file->read(_topBG, 8);
+	file->read(_bottomBG, 8);
+	_divideBG = readSint16(*file);
+	_backgroundActive = readSint16(*file) != 0;
+	if (_backgroundActive) {
+		_backgroundClip = readRect(*file);
+		_backgroundLocate = readRect(*file);
+		_backgroundMask = loadImage(*file);
+		_backgroundFG = loadImage(*file);
+	}
+
+	// Read sprite data
+	int16 maxsprite = readSint16(*file);
+	readSint16(*file); // locSprite
+	for (int i = 0; i < maxsprite; i++) {
+		Sprite *s = new Sprite();
+		s->fg = loadImage(*file);
+		s->mask = loadImage(*file);
+		s->used = readSint16(*file) != 0;
+		s->clip = readRect(*file);
+		s->locate = readRect(*file);
+		_cSprites.push_back(s);
+	}
+
+	// Read complex sprite data
+	int16 maxLSprite = readSint16(*file);
+	readSint16(*file); // anum
+	for (int i = 0; i < maxLSprite; i++) {
+		ComplexSprite *ls = new ComplexSprite();
+		int16 size = readSint16(*file);
+		for (int j = 0; j < size; j++) {
+			ComplexSprite::SubObject sub;
+			sub.spritenum = readSint16(*file);
+			sub.xloc = readSint16(*file);
+			sub.yloc = readSint16(*file);
+			ls->objects.push_back(sub);
+		}
+		ls->bounds = readRect(*file);
+		ls->visible = readSint16(*file) != 0;
+		ls->current = readSint16(*file);
+		ls->xloc = readSint16(*file);
+		ls->yloc = readSint16(*file);
+		ls->acurrent = readSint16(*file);
+		ls->axloc = readSint16(*file);
+		ls->ayloc = readSint16(*file);
+		ls->type = file->readByte();
+		ls->frozen = file->readByte();
+		ls->locked = file->readByte();
+		ls->link = readSint16(*file);
+		ls->key = readSint16(*file);
+		ls->lock = readSint16(*file);
+		ls->onoff = true;
+		_lSprites.push_back(ls);
+	}
+
+	delete file;
+	return true;
+}
+
+void ColonyEngine::deleteAnimation() {
+	delete _backgroundMask;
+	_backgroundMask = nullptr;
+	delete _backgroundFG;
+	_backgroundFG = nullptr;
+	for (uint i = 0; i < _cSprites.size(); i++)
+		delete _cSprites[i];
+	_cSprites.clear();
+	for (uint i = 0; i < _lSprites.size(); i++)
+		delete _lSprites[i];
+	_lSprites.clear();
+}
+
+void ColonyEngine::playAnimation() {
+	// Clear movement flags so held keys don't re-trigger on return
+	_moveForward = false;
+	_moveBackward = false;
+	_strafeLeft = false;
+	_strafeRight = false;
+	_rotateLeft = false;
+	_rotateRight = false;
+
+	_animationRunning = true;
+	_system->lockMouse(false);
+	_system->showMouse(true);
+	_system->warpMouse(_centerX, _centerY);
+	CursorMan.setDefaultArrowCursor(true);
+	CursorMan.showMouse(true);
+	_system->updateScreen();
+
+	if (_animationName == "security" && !_unlocked) {
+		for (int i = 0; i < 4; i++) {
+			_decode1[i] = (uint8)(2 + _randomSource.getRandomNumber(3));
+			setObjectState(27 + i, _decode1[i]);
+		}
+	} else if (_animationName == "reactor") {
+		for (int i = 0; i < 6; i++) {
+			setObjectOnOff(14 + i * 2, false);
+			setObjectState(13 + i * 2, 1);
+		}
+	} else if (_animationName == "controls") {
+		switch (_corePower[_coreIndex]) {
+		case 0: setObjectState(2, 1); setObjectState(5, 1); break;
+		case 1: setObjectState(2, 1); setObjectState(5, 2); break;
+		case 2: setObjectState(2, 2); setObjectState(5, 1); break;
+		}
+	} else if (_animationName == "desk") {
+		if (!(_action0 == 11 || _action0 == 18)) {
+			for (int i = 1; i <= 5; i++)
+				setObjectOnOff(i, false);
+		} else {
+			uint8 *decode = (_action0 == 11) ? _decode2 : _decode3;
+			for (int i = 0; i < 4; i++) {
+				if (decode[i])
+					setObjectState(i + 2, decode[i]);
+				else
+					setObjectState(i + 2, 1);
+			}
+		}
+
+		if (_action0 != 10) {
+			setObjectOnOff(23, false);
+			setObjectOnOff(24, false);
+		}
+		if (_action0 != 30)
+			setObjectOnOff(6, false); // Teeth
+		if (_action0 != 33) { // Jack-in-the-box
+			for (int i = 18; i <= 21; i++)
+				setObjectOnOff(i, false);
+		}
+
+		int ntype = _action1 / 10;
+		switch (ntype) {
+		case 0:
+		case 1:
+		case 2:
+		case 3:
+			setObjectOnOff(7, false);
+			setObjectOnOff(8, false);
+			setObjectOnOff(9, false);
+			setObjectOnOff(22, false);
+			setObjectOnOff(25, false);
+			break;
+		case 4: // letters
+			setObjectOnOff(22, false);
+			setObjectOnOff(9, false);
+			setObjectOnOff(25, false);
+			break;
+		case 5: // book
+			setObjectOnOff(7, false);
+			setObjectOnOff(8, false);
+			setObjectOnOff(9, false);
+			setObjectOnOff(25, false);
+			break;
+		case 6: // clipboard
+			setObjectOnOff(22, false);
+			setObjectOnOff(7, false);
+			setObjectOnOff(8, false);
+			setObjectOnOff(25, false);
+			break;
+		case 7: // postit
+			setObjectOnOff(22, false);
+			setObjectOnOff(7, false);
+			setObjectOnOff(8, false);
+			setObjectOnOff(9, false);
+			break;
+		}
+	} else if (_animationName == "vanity") {
+		debug(0, "Vanity init: action0=%d action1=%d level=%d weapons=%d armor=%d", _action0, _action1, _level, _weapons, _armor);
+		for (int i = 0; i < (int)_lSprites.size(); i++) {
+			ComplexSprite *ls = _lSprites[i];
+			debug(0, "  Vanity sprite %d: type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d frames=%d",
+				i + 1, ls->type, ls->frozen, ls->locked, ls->current, (int)ls->onoff, ls->key, ls->lock, (int)ls->objects.size());
+		}
+		// DOS DoVanity: set suit state on mirror display (object 1)
+		if (_weapons && _armor)
+			setObjectState(1, 3);
+		else if (_weapons)
+			setObjectState(1, 2);
+		else if (_armor)
+			setObjectState(1, 1);
+		else
+			setObjectState(1, 4);
+		// Badge only visible on level 1
+		if (_level != 1)
+			setObjectOnOff(14, false);
+		// Hide items based on action0 (num parameter in DOS)
+		if (_action0 < 90) { // coffee cup only
+			setObjectOnOff(4, false);
+			setObjectOnOff(7, false);
+			setObjectOnOff(13, false);
+		} else if (_action0 < 100) { // paper
+			setObjectOnOff(12, false);
+			setObjectOnOff(4, false);
+			setObjectOnOff(7, false);
+		} else if (_action0 < 110) { // diary
+			setObjectOnOff(12, false);
+			setObjectOnOff(13, false);
+			setObjectOnOff(7, false);
+		} else if (_action0 < 120) { // book
+			setObjectOnOff(12, false);
+			setObjectOnOff(13, false);
+			setObjectOnOff(4, false);
+		}
+	}
+
+	while (_animationRunning && !shouldQuit()) {
+		updateAnimation();
+		drawAnimation();
+		_gfx->copyToScreen();
+
+		Common::Event event;
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_LBUTTONDOWN) {
+				int item = whichSprite(event.mouse);
+				if (item > 0) {
+					handleAnimationClick(item);
+				}
+			} else if (event.type == Common::EVENT_RBUTTONDOWN) {
+				// DOS: right-click exits animation (AnimControl returns FALSE on button-up)
+				_animationRunning = false;
+			} else if (event.type == Common::EVENT_MOUSEMOVE) {
+				debug(5, "Animation Mouse: %d, %d", event.mouse.x, event.mouse.y);
+			} else if (event.type == Common::EVENT_KEYDOWN) {
+				int item = 0;
+				if (event.kbd.keycode >= Common::KEYCODE_0 && event.kbd.keycode <= Common::KEYCODE_9) {
+					item = 1 + (event.kbd.keycode - Common::KEYCODE_0);
+				} else if (event.kbd.keycode >= Common::KEYCODE_KP0 && event.kbd.keycode <= Common::KEYCODE_KP9) {
+					item = 1 + (event.kbd.keycode - Common::KEYCODE_KP0);
+				} else if (event.kbd.keycode == Common::KEYCODE_RETURN || event.kbd.keycode == Common::KEYCODE_KP_ENTER) {
+					item = 12; // Enter
+				} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE || event.kbd.keycode == Common::KEYCODE_DELETE) {
+					item = 11; // Clear
+				} else if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					_animationRunning = false;
+				}
+
+				if (item > 0) {
+					handleAnimationClick(item);
+				}
+			}
+		}
+		_system->delayMillis(20);
+	}
+
+	_system->lockMouse(true);
+	_system->showMouse(false);
+	CursorMan.showMouse(false);
+	CursorMan.popAllCursors();
+	deleteAnimation();
+}
+
+void ColonyEngine::updateAnimation() {
+	uint32 now = _system->getMillis();
+	if (now - _lastAnimUpdate < 50) // Reduced to 50ms (20 fps) to make it "move"
+		return;
+	_lastAnimUpdate = now;
+
+	for (uint i = 0; i < _lSprites.size(); i++) {
+		ComplexSprite *ls = _lSprites[i];
+		// type 0 are displays that auto-animate
+		// Original NoShowIt ONLY checked !ls->locked
+		if (ls->onoff && ls->type == 0 && !ls->locked && ls->objects.size() > 1) {
+			ls->current++;
+			if (ls->current >= (int)ls->objects.size())
+				ls->current = 0;
+		}
+	}
+}
+
+// Resolve a BMColor entry to an ARGB color.
+// bmEntry > 0: cColor index -> use _macColors[idx].bg
+// bmEntry < 0: negated Mac system color constant
+// bmEntry == 0: level-based character color (depends on corepower)
+uint32 ColonyEngine::resolveAnimColor(int16 bmEntry) const {
+	if (bmEntry < 0) {
+		return macSysColorToARGB(-bmEntry);
+	} else if (bmEntry > 0) {
+		if (bmEntry < 145)
+			return packMacColorBG(_macColors[bmEntry].bg);
+		return 0xFFFFFFFF;
+	} else {
+		// Zero = level-based (original gamesprt.c DrawlSprite/DrawBackGround):
+		//   if(corepower[coreindex]) RGBBackColor(&cColor[c_char0+level-1].f);
+		//   else RGBBackColor(&cColor[c_dwall].b);
+		if (_corePower[_coreIndex] > 0 && _level >= 1 && _level <= 7)
+			return packMacColorBG(_macColors[mc_char0 + _level - 1].fg);
+		return packMacColorBG(_macColors[mc_dwall].bg);
+	}
+}
+
+void ColonyEngine::drawAnimation() {
+	_gfx->clear(0);
+
+	// Center 416x264 animation area on screen (from original InitDejaVu)
+	int ox = _screenR.left + (_screenR.width() - 416) / 2;
+	ox = (ox / 8) * 8;
+	int oy = _screenR.top + (_screenR.height() - 264) / 2;
+
+	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh
+	                       && !_animBMColors.empty());
+
+	// Fill background patterns (416x264 area).
+	// Color mode: QuickDraw pattern bit 1 -> ForeColor (black), bit 0 -> BackColor.
+	// Original DrawBackGround():
+	//   Top: BMColor[0]<0 -> system color; ==0 -> powered:c_char0+level-1.f, else:c_dwall.b
+	//   Bottom: powered -> c_lwall.f; unpowered -> inherits top BackColor
+	// B&W/DOS: preserve existing palette-index behavior (bit 1 -> 15, bit 0 -> 0).
+	if (useColor) {
+		const bool powered = (_corePower[_coreIndex] > 0);
+		uint32 topBG = resolveAnimColor(_animBMColors[0]);
+		// Bottom: only uses c_lwall.f when powered; unpowered inherits top color
+		uint32 botBG = powered ? packMacColorBG(_macColors[mc_lwall].fg) : topBG;
+		for (int y = 0; y < 264; y++) {
+			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
+			byte row = pat[y % 8];
+			uint32 bg = (y < _divideBG) ? topBG : botBG;
+			for (int x = 0; x < 416; x++) {
+				bool set = (row & (0x80 >> (x % 8))) != 0;
+				_gfx->setPixel(ox + x, oy + y, set ? (uint32)0xFF000000 : bg);
+			}
+		}
+	} else {
+		for (int y = 0; y < 264; y++) {
+			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
+			byte row = pat[y % 8];
+			for (int x = 0; x < 416; x++) {
+				bool set = (row & (0x80 >> (x % 8))) != 0;
+				_gfx->setPixel(ox + x, oy + y, set ? 15 : 0);
+			}
+		}
+	}
+
+	// Draw background image if active.
+	// Original: BMColor[1] only applied when corepower[coreindex] > 0.
+	if (_backgroundActive && _backgroundFG) {
+		uint32 bgFill = 0xFFFFFFFF; // B&W default
+		if (useColor && _animBMColors.size() > 1) {
+			if (_corePower[_coreIndex] > 0)
+				bgFill = resolveAnimColor(_animBMColors[1]);
+			else
+				bgFill = resolveAnimColor(_animBMColors[0]); // unpowered: inherits top
+		}
+		drawAnimationImage(_backgroundFG, _backgroundMask,
+		                   ox + _backgroundLocate.left, oy + _backgroundLocate.top,
+		                   bgFill);
+	}
+
+	// Draw complex sprites
+	for (uint i = 0; i < _lSprites.size(); i++) {
+		if (_lSprites[i]->onoff)
+			drawComplexSprite(i, ox, oy);
+	}
+}
+
+void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
+	ComplexSprite *ls = _lSprites[index];
+	if (!ls->onoff)
+		return;
+
+	int cnum = ls->current;
+	if (cnum < 0 || cnum >= (int)ls->objects.size())
+		return;
+
+	int spriteIdx = ls->objects[cnum].spritenum;
+	if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size())
+		return;
+
+	Sprite *s = _cSprites[spriteIdx];
+	int x = ox + ls->xloc + ls->objects[cnum].xloc + s->clip.left;
+	int y = oy + ls->yloc + ls->objects[cnum].yloc + s->clip.top;
+
+	// Resolve fill color from BMColor[index+2] (ganimate.c DrawlSprite).
+	uint32 fillColor = 0xFFFFFFFF; // B&W default: white
+	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh
+	                       && !_animBMColors.empty());
+	if (useColor) {
+		int bmIdx = index + 2;
+		if (bmIdx < (int)_animBMColors.size())
+			fillColor = resolveAnimColor(_animBMColors[bmIdx]);
+		else
+			fillColor = resolveAnimColor(0); // fallback to level-based
+	}
+
+	drawAnimationImage(s->fg, s->mask, x, y, fillColor);
+}
+
+void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y, uint32 fillColor) {
+	if (!img || !img->data)
+		return;
+
+	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh);
+	// Mac QuickDraw srcBic+srcOr rendering:
+	//   mask bit=1 -> opaque (part of sprite)
+	//   fg bit=1   -> ForeColor (black outline)
+	//   fg bit=0   -> BackColor (fillColor from BMColor)
+	// B&W/DOS fallback preserves existing palette-index behavior.
+	const uint32 fgColor = useColor ? (uint32)0xFF000000 : 15;
+	const uint32 bgColor = useColor ? fillColor : 0;
+
+	for (int iy = 0; iy < img->height; iy++) {
+		for (int ix = 0; ix < img->width; ix++) {
+			int byteIdx = iy * img->rowBytes + (ix / 8);
+			int bitIdx = 7 - (ix % 8);
+
+			bool maskSet = true;
+			if (mask && mask->data) {
+				int mByteIdx = iy * mask->rowBytes + (ix / 8);
+				int mBitIdx = 7 - (ix % 8);
+				maskSet = (mask->data[mByteIdx] & (1 << mBitIdx)) != 0;
+			}
+
+			if (!maskSet)
+				continue;
+
+			bool fgSet = (img->data[byteIdx] & (1 << bitIdx)) != 0;
+			uint32 color = fgSet ? fgColor : bgColor;
+
+			_gfx->setPixel(x + ix, y + iy, color);
+		}
+	}
+}
+
+Image *ColonyEngine::loadImage(Common::SeekableReadStream &file) {
+	Image *im = new Image();
+	if (getPlatform() == Common::kPlatformMacintosh) {
+		readUint32(file); // baseAddr placeholder
+		im->rowBytes = readSint16(file);
+		Common::Rect r = readRect(file);
+		im->width = r.width();
+		im->height = r.height();
+		im->align = 0;
+		im->bits = 1;
+		im->planes = 1;
+	} else {
+		im->width = readSint16(file);
+		im->height = readSint16(file);
+		im->align = readSint16(file);
+		im->rowBytes = readSint16(file);
+		im->bits = file.readByte();
+		im->planes = file.readByte();
+	}
+
+	int16 tf = readSint16(file);
+	uint32 size;
+	if (tf) {
+		// Mac original loadbitmap: reads bsize bytes into a buffer, then
+		// decompresses from that buffer. We must read exactly bsize bytes
+		// from the stream to keep file position aligned.
+		uint32 bsize = readUint32(file);
+		size = readUint32(file);
+		im->data = (byte *)malloc(size);
+		byte *packed = (byte *)calloc(bsize + 8, 1); // +8 matches original NewPtr(bsize+8)
+		file.read(packed, bsize);
+		// Decompress: exact match of Mac UnPackBytes(src, dst, len).
+		// Buffer is pairs of (count, value). Count is decremented in-place;
+		// when it reaches 0, advance to next pair.
+		byte *sp = packed;
+		for (uint32 di = 0; di < size; di++) {
+			if (*sp) {
+				im->data[di] = *(sp + 1);
+				(*sp)--;
+			} else {
+				sp += 2;
+				im->data[di] = *(sp + 1);
+				(*sp)--;
+			}
+		}
+		free(packed);
+	} else {
+		size = readUint32(file);
+		im->data = (byte *)malloc(size);
+		file.read(im->data, size);
+	}
+	return im;
+}
+
+void ColonyEngine::unpackBytes(Common::SeekableReadStream &file, byte *dst, uint32 len) {
+	uint32 i = 0;
+	while (i < len) {
+		byte count = file.readByte();
+		byte value = file.readByte();
+		for (int j = 0; j < count && i < len; j++) {
+			dst[i++] = value;
+		}
+	}
+}
+
+Common::Rect ColonyEngine::readRect(Common::SeekableReadStream &file) {
+	int16 top, left, bottom, right;
+	if (getPlatform() == Common::kPlatformMacintosh) {
+		top = readSint16(file);
+		left = readSint16(file);
+		bottom = readSint16(file);
+		right = readSint16(file);
+	} else {
+		left = readSint16(file);
+		top = readSint16(file);
+		right = readSint16(file);
+		bottom = readSint16(file);
+	}
+	// Guard against invalid rects from animation data
+	if (left > right || top > bottom)
+		return Common::Rect();
+	return Common::Rect(left, top, right, bottom);
+}
+
+int16 ColonyEngine::readSint16(Common::SeekableReadStream &s) {
+	if (getPlatform() == Common::kPlatformMacintosh)
+		return s.readSint16BE();
+	return s.readSint16LE();
+}
+
+uint16 ColonyEngine::readUint16(Common::SeekableReadStream &s) {
+	if (getPlatform() == Common::kPlatformMacintosh)
+		return s.readUint16BE();
+	return s.readUint16LE();
+}
+
+uint32 ColonyEngine::readUint32(Common::SeekableReadStream &s) {
+	if (getPlatform() == Common::kPlatformMacintosh)
+		return s.readUint32BE();
+	return s.readUint32LE();
+}
+
+int ColonyEngine::whichSprite(const Common::Point &p) {
+	int ox = _screenR.left + (_screenR.width() - 416) / 2;
+	ox = (ox / 8) * 8;
+	int oy = _screenR.top + (_screenR.height() - 264) / 2;
+	Common::Point pt(p.x - ox, p.y - oy);
+
+	debug(1, "Click at (%d, %d), relative (%d, %d)", p.x, p.y, pt.x, pt.y);
+
+	for (int i = _lSprites.size() - 1; i >= 0; i--) {
+		ComplexSprite *ls = _lSprites[i];
+		if (!ls->onoff)
+			continue;
+
+		int cnum = ls->current;
+		if (cnum < 0 || cnum >= (int)ls->objects.size())
+			continue;
+
+		int spriteIdx = ls->objects[cnum].spritenum;
+		if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size())
+			continue;
+
+		Sprite *s = _cSprites[spriteIdx];
+		int xloc = ls->xloc + ls->objects[cnum].xloc;
+		int yloc = ls->yloc + ls->objects[cnum].yloc;
+
+		Common::Rect r = s->clip;
+		r.translate(xloc, yloc);
+
+		if (!r.contains(pt))
+			continue;
+
+		// Pixel-perfect mask test (matches DOS WhichlSprite)
+		Image *mask = s->mask;
+		if (mask && mask->data) {
+			int row = pt.y - r.top;
+			int col = pt.x - r.left;
+			int bitCol = (col + mask->align) * mask->bits;
+			int maskIndex = row * mask->rowBytes + (bitCol / 8);
+			int shift = bitCol % 8;
+
+			if (maskIndex >= 0 && maskIndex < mask->rowBytes * mask->height) {
+				byte maskByte = mask->data[maskIndex];
+				if (mask->planes == 2)
+					maskByte |= mask->data[mask->rowBytes * mask->height + maskIndex];
+				maskByte = maskByte >> shift;
+				if (!(maskByte & ((1 << mask->bits) - 1))) {
+					debug(0, "  Sprite %d (type=%d frz=%d): bbox hit but mask transparent at row=%d col=%d bits=%d align=%d",
+						i + 1, ls->type, ls->frozen, row, col, mask->bits, mask->align);
+					continue; // Transparent pixel, skip this sprite
+				}
+			} else {
+				debug(0, "  Sprite %d: mask index %d out of bounds (max %d)", i + 1, maskIndex, mask->rowBytes * mask->height);
+			}
+		} else {
+			debug(0, "  Sprite %d: no mask data, using bbox", i + 1);
+		}
+
+		debug(0, "Sprite %d HIT. type=%d frozen=%d Frame %d, Sprite %d. Box: (%d,%d,%d,%d)",
+			i + 1, ls->type, ls->frozen, cnum, spriteIdx, r.left, r.top, r.right, r.bottom);
+		return i + 1;
+	}
+
+	// Dump accurately calculated bounds if debug is high enough
+	if (gDebugLevel >= 2) {
+		for (int i = 0; i < (int)_lSprites.size(); i++) {
+			ComplexSprite *ls = _lSprites[i];
+			if (ls->onoff) {
+				int cnum = ls->current;
+				if (cnum < 0 || cnum >= (int)ls->objects.size())
+					continue;
+				int spriteIdx = ls->objects[cnum].spritenum;
+				if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size())
+					continue;
+				Sprite *s = _cSprites[spriteIdx];
+
+				int xloc = ls->xloc + ls->objects[cnum].xloc;
+				int yloc = ls->yloc + ls->objects[cnum].yloc;
+				Common::Rect r = s->clip;
+				r.translate(xloc, yloc);
+
+				debug(2, "  Sprite %d: Frame=%d Box=(%d,%d,%d,%d)", i + 1,
+					cnum, r.left, r.top, r.right, r.bottom);
+			}
+		}
+	}
+
+	return 0;
+}
+
+void ColonyEngine::handleAnimationClick(int item) {
+	uint32 now = _system->getMillis();
+	if (now - _lastClickTime < 250) {
+		debug("Ignoring rapid click on item %d", item);
+		return;
+	}
+	_lastClickTime = now;
+	debug(0, "Animation click on item %d in %s", item, _animationName.c_str());
+
+	if (item > 0) {
+		dolSprite(item - 1);
+	}
+
+	if (_animationName == "desk") {
+		if (item >= 2 && item <= 5) {
+			int idx = item - 2;
+			uint8 *decode = (_level == 1) ? _decode2 : _decode3;
+			if (decode[idx] == 0) {
+				decode[idx] = (uint8)(2 + (_randomSource.getRandomNumber(3)));
+				_lSprites[item - 1]->current = decode[idx] - 1;
+				drawAnimation();
+				_gfx->copyToScreen();
+			}
+		} else if (item == 7) { // Letter
+			if (_lSprites[6]->current > 0)
+				doText(_action1, 0);
+		} else if (item == 9) { // Clipboard
+			doText(_action1, 0);
+		} else if (item == 17) { // Screen
+			doText(_action0, 0);
+		} else if (item == 22) { // Book
+			doText(_action1, 0);
+		} else if (item == 24) { // Cigarette
+			doText(55, 0);
+			terminateGame(false);
+		} else if (item == 25) { // Post-it
+			doText(_action1, 0);
+		}
+	} else if (_animationName == "vanity") {
+		debug(0, "Vanity item %d clicked. Sprite type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d size=%d",
+			item,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->type : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->frozen : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->locked : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->current : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? (int)_lSprites[item-1]->onoff : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->key : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->lock : -1,
+			(item > 0 && item <= (int)_lSprites.size()) ? (int)_lSprites[item-1]->objects.size() : -1);
+		if (item == 12) { // Coffee cup - spill animation
+			if (!_doorOpen) { // reuse _doorOpen as "spilled" flag
+				for (int i = 1; i < 6; i++) {
+					setObjectState(12, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(50);
+				}
+				_doorOpen = true;
+			}
+		} else if (item == 13) { // Paper
+			doText(_action0, 0);
+		} else if (item == 14) { // Badge
+			doText(80, 0);
+		} else if (item == 4) { // Diary
+			doText(_action0, 0);
+		} else if (item == 7) { // Book
+			doText(_action0, 0);
+		} else {
+			debug(0, "Vanity: unhandled item %d", item);
+		}
+	} else if (_animationName == "slides") {
+		if (item == 2) { // Speaker
+			doText(261 + _creature, 0);
+		} else if (item == 5) { // Prev
+			_creature--;
+			if (_creature == 0)
+				_creature = 8;
+			setObjectState(1, _creature);
+		} else if (item == 6) { // Next
+			_creature++;
+			if (_creature == 9)
+				_creature = 1;
+			setObjectState(1, _creature);
+		}
+	} else if (_animationName == "teleshow") {
+		if (item == 2) { // Speaker
+			doText(269 + _creature, 0);
+		} else if (item == 5) { // Prev
+			_creature--;
+			if (_creature == 0)
+				_creature = 7;
+			setObjectState(1, _creature);
+		} else if (item == 6) { // Next
+			_creature++;
+			if (_creature == 8)
+				_creature = 1;
+			setObjectState(1, _creature);
+		}
+	} else if (_animationName == "reactor" || _animationName == "security" || _animationName == "suit") {
+		if (item >= 1 && item <= 10 && _animationName != "suit") {
+			for (int i = 5; i >= 1; i--)
+				_animDisplay[i] = _animDisplay[i - 1];
+			_animDisplay[0] = (uint8)(item + 1);
+			refreshAnimationDisplay();
+			drawAnimation();
+			_gfx->copyToScreen();
+			// Don't return, let dolSprite animate the button
+		} else if (item == 11 && _animationName != "suit") { // Clear
+			for (int i = 0; i < 6; i++)
+				_animDisplay[i] = 1;
+			// Reset keypad buttons to unpressed state
+			for (int i = 1; i <= 10; i++)
+				setObjectState(i, 1);
+			refreshAnimationDisplay();
+			drawAnimation();
+			_gfx->copyToScreen();
+		} else if (item == 12 && _animationName != "suit") { // Enter
+			uint8 testarray[6];
+			if (_animationName == "reactor") {
+				if (_level == 1)
+					crypt(testarray, _decode2[3] - 2, _decode2[2] - 2, _decode2[1] - 2, _decode2[0] - 2);
+				else
+					crypt(testarray, _decode3[3] - 2, _decode3[2] - 2, _decode3[1] - 2, _decode3[0] - 2);
+
+				bool match = true;
+				for (int i = 0; i < 6; i++) {
+					if (testarray[i] != _animDisplay[5 - i])
+						match = false;
+				}
+				if (match) {
+					if (_coreState[_coreIndex] == 0)
+						_coreState[_coreIndex] = 1;
+					else if (_coreState[_coreIndex] == 1)
+						_coreState[_coreIndex] = 0;
+					_gametest = true;
+				}
+				_animationRunning = false;
+			} else if (_animationName == "security") { // security
+				crypt(testarray, _decode1[0] - 2, _decode1[1] - 2, _decode1[2] - 2, _decode1[3] - 2);
+				bool match = true;
+				for (int i = 0; i < 6; i++) {
+					if (testarray[i] != _animDisplay[5 - i])
+						match = false;
+				}
+				if (match) {
+					_unlocked = true;
+					_gametest = true;
+				}
+				_animationRunning = false;
+			}
+		} else if (_animationName == "suit") {
+			if (item == 1) { // Armor
+				if (_armor == 3) {
+					for (int i = 6; i >= 1; i--) {
+						setObjectState(1, i);
+						setObjectState(3, i / 2 + 1);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(30);
+					}
+					_armor = 0;
+				} else {
+					setObjectState(1, (_armor * 2 + 1) + 1); // intermediate/pressed
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(50);
+					_armor++;
+				}
+				setObjectState(1, _armor * 2 + 1); // target state
+				setObjectState(3, _armor + 1); // display state
+				drawAnimation();
+				_gfx->copyToScreen();
+				if (_armor == 3 && _weapons == 3)
+					_corePower[_coreIndex] = 2;
+			} else if (item == 2) { // Weapons
+				if (_weapons == 3) {
+					for (int i = 6; i >= 1; i--) {
+						setObjectState(2, i);
+						setObjectState(4, i / 2 + 1);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(30);
+					}
+					_weapons = 0;
+				} else {
+					setObjectState(2, (_weapons * 2 + 1) + 1); // intermediate/pressed
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(50);
+					_weapons++;
+				}
+				setObjectState(2, _weapons * 2 + 1);
+				setObjectState(4, _weapons + 1);
+				drawAnimation();
+				_gfx->copyToScreen();
+				if (_armor == 3 && _weapons == 3)
+					_corePower[_coreIndex] = 2;
+			}
+		}
+		if (_animationName == "reactor" || _animationName == "security") {
+			if (item <= 12) {
+				// setObjectState(item, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
+				if (item > 10) // Clear/Enter should return to Off
+					setObjectState(item, 1);
+				drawAnimation();
+				_gfx->copyToScreen();
+			}
+		}
+	} else if (_animationName == "door" || _animationName == "bulkhead") {
+		// DOS DoDoor: item==3 toggles door open/close, item==1 or (item==101 && door open) exits
+		if (item == 3) {
+			_sound->play(Sound::kDoor);
+			if (_doorOpen) {
+				for (int i = 3; i >= 1; i--) {
+					_doorOpen = !_doorOpen;
+					setObjectState(2, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(80);
+				}
+			} else {
+				for (int i = 1; i < 4; i++) {
+					_doorOpen = !_doorOpen;
+					setObjectState(2, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(80);
+				}
+			}
+		}
+		if (item == 1 || (item == 101 && objectState(2) == 3)) {
+			_animationResult = 1;
+			_animationRunning = false;
+		}
+	} else if (_animationName == "airlock") {
+		// DOS DoAirLock: item==1 toggles airlock if power on && unlocked
+		// item==2 or (item==101 && airlock open) exits with pass-through
+		if ((item == 2 || item == 101) && _doorOpen) {
+			_animationResult = 1;
+			_animationRunning = false;
+		} else if (item == 1 && _corePower[_coreIndex] && _unlocked) {
+			_sound->play(Sound::kAirlock);
+			if (_doorOpen) {
+				for (int i = 3; i >= 1; i--) {
+					setObjectState(2, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(80);
+				}
+			} else {
+				for (int i = 1; i < 4; i++) {
+					setObjectState(2, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(80);
+				}
+			}
+			_doorOpen = !_doorOpen;
+		} else if (item == 101 && !_doorOpen) {
+			// Exit without opening
+			_animationRunning = false;
+		}
+	} else if (_animationName == "elev") {
+		// DOS DoElevator: two phases
+		// _doorOpen=false: Phase 1 (outside) - item==5 toggles doors
+		// _doorOpen=true: Phase 2 (inside) - items 6-10 select floor
+		// _animationResult tracks: 0=outside, 1=doors open, 2=inside
+		if (_animationResult < 2) {
+			// Phase 1: outside the elevator
+			if (item == 5) {
+				_sound->play(Sound::kElevator);
+				if (!_doorOpen) {
+					for (int i = 1; i < 4; i++) {
+						setObjectState(3, i);
+						setObjectState(4, i);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(80);
+					}
+					_doorOpen = true;
+				} else {
+					for (int i = 3; i >= 1; i--) {
+						setObjectState(4, i);
+						setObjectState(3, i);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(80);
+					}
+					_doorOpen = false;
+				}
+			} else if (item == 2 || (item == 101 && _doorOpen)) {
+				// Enter the elevator (transition to phase 2)
+				_animationResult = 2;
+				setObjectOnOff(6, true);
+				setObjectOnOff(7, true);
+				setObjectOnOff(8, true);
+				setObjectOnOff(9, true);
+				setObjectOnOff(10, true);
+				setObjectOnOff(2, false);
+				setObjectOnOff(5, false);
+				drawAnimation();
+				_gfx->copyToScreen();
+			} else if (item == 101 && !_doorOpen) {
+				// Exit without entering
+				_animationResult = 0;
+				_animationRunning = false;
+			}
+		} else {
+			// Phase 2: inside  floor selection
+			if (item >= 6 && item <= 10) {
+				int fl = item - 5;
+				if (fl == _elevatorFloor) {
+					setObjectState(item, 1); // already on this floor
+				} else {
+					_sound->play(Sound::kElevator);
+					for (int i = 3; i >= 1; i--) {
+						setObjectState(4, i);
+						setObjectState(3, i);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(80);
+					}
+					_elevatorFloor = fl;
+					for (int i = 1; i <= 3; i++) {
+						setObjectState(4, i);
+						setObjectState(3, i);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(80);
+					}
+					setObjectState(item, 1);
+				}
+			} else if (item == 1 || item == 101) {
+				// Exit elevator
+				_animationRunning = false;
+			}
+		}
+	} else if (_animationName == "controls") {
+		switch (item) {
+		case 4: // Accelerator
+			if (_corePower[_coreIndex] >= 2 && _coreState[_coreIndex] == 0 && !_orbit) {
+				_orbit = 1;
+				debug(0, "Taking off!");
+				// Animate the lever moving full range
+				for (int i = 1; i <= 6; i++) {
+					setObjectState(4, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(30);
+				}
+				_animationRunning = false;
+				return; // Exit animation immediately on success
+			} else {
+				debug(0, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
+				// Fail animation click
+				setObjectState(4, 1);
+				// dolSprite(3); // Animate lever moving and returning - handled by top dolSprite
+				for (int i = 6; i > 0; i--) {
+					setObjectState(4, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(20);
+				}
+			}
+			break;
+		case 5: // Emergency power
+			// setObjectState(5, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
+			// dolSprite(4); // Animate the button press - handled by top dolSprite
+			if (_coreState[_coreIndex] < 2) {
+				if (_corePower[_coreIndex] == 0)
+					_corePower[_coreIndex] = 1;
+				else if (_corePower[_coreIndex] == 1)
+					_corePower[_coreIndex] = 0;
+			}
+			// Finalize visual state according to power settings
+			switch (_corePower[_coreIndex]) {
+			case 0: setObjectState(2, 1); setObjectState(5, 1); break;
+			case 1: setObjectState(2, 1); setObjectState(5, 2); break;
+			case 2: setObjectState(2, 2); setObjectState(5, 1); break;
+			}
+			drawAnimation();
+			_gfx->copyToScreen();
+			break;
+		case 7: // Damage report
+		{
+			// dolSprite(6); // Button animation - handled by top dolSprite
+			if (_corePower[_coreIndex] < 2) {
+				doText(15, 0); // Critical status
+			} else if (!_orbit) {
+				doText(49, 0); // Ready for liftoff
+			} else {
+				doText(66, 0); // Orbital stabilization
+			}
+
+			setObjectState(7, 1); // Reset button
+			drawAnimation();
+			_gfx->copyToScreen();
+			break;
+		}
+			break;
+		}
+	}
+}
+
+void ColonyEngine::moveObject(int index) {
+	if (index < 0 || index >= (int)_lSprites.size())
+		return;
+
+	ComplexSprite *ls = _lSprites[index];
+
+	// Build link group
+	Common::Array<int> linked;
+	if (ls->link) {
+		for (int i = 0; i < (int)_lSprites.size(); i++)
+			if (_lSprites[i]->link == ls->link)
+				linked.push_back(i);
+	} else {
+		linked.push_back(index);
+	}
+
+	// Get initial mouse position and animation origin
+	Common::Point old = _system->getEventManager()->getMousePos();
+	int ox = _screenR.left + (_screenR.width() - 416) / 2;
+	ox = (ox / 8) * 8;
+	int oy = _screenR.top + (_screenR.height() - 264) / 2;
+
+	// Drag loop: track mouse while left button held.
+	// NOTE: The original DOS hides dragged sprites during drag (setObjectOnOff FALSE)
+	// and redraws them separately on top. We improve on this by keeping them visible
+	// throughout, and drawing an extra copy on top so they render above drawers.
+	while (!shouldQuit()) {
+		Common::Event event;
+		bool buttonDown = true;
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_LBUTTONUP) {
+				buttonDown = false;
+				break;
+			}
+		}
+		if (!buttonDown)
+			break;
+
+		Common::Point cur = _system->getEventManager()->getMousePos();
+		int dx = cur.x - old.x;
+		int dy = cur.y - old.y;
+
+		if (dx != 0 || dy != 0) {
+			// Cycle frame for non-type-2 sprites
+			if (ls->type != 2 && (int)ls->objects.size() > 1) {
+				ls->current++;
+				if (ls->current >= (int)ls->objects.size())
+					ls->current = 0;
+			}
+
+			// Move all linked sprites
+			for (uint i = 0; i < linked.size(); i++) {
+				_lSprites[linked[i]]->xloc += dx;
+				_lSprites[linked[i]]->yloc += dy;
+			}
+
+			old = cur;
+		}
+
+		// Draw all sprites normally, then draw dragged sprites again on top
+		// so they appear above drawers and other overlapping sprites
+		drawAnimation();
+		for (uint i = 0; i < linked.size(); i++)
+			drawComplexSprite(linked[i], ox, oy);
+		_gfx->copyToScreen();
+
+		_system->delayMillis(20);
+	}
+
+	// Reset frame for non-type-2
+	if (ls->type != 2)
+		ls->current = 0;
+
+	drawAnimation();
+	_gfx->copyToScreen();
+}
+
+void ColonyEngine::dolSprite(int index) {
+	if (index < 0 || index >= (int)_lSprites.size())
+		return;
+
+	ComplexSprite *ls = _lSprites[index];
+	int maxFrames = (int)ls->objects.size();
+
+	switch (ls->type) {
+	case 0: // Display
+		if (!ls->frozen)
+			moveObject(index);
+		break;
+	case 1: // Key and control
+		if (ls->frozen) {
+			// Container: can open or close if not locked, or if slightly open
+			if (!ls->locked || ls->current) {
+				if (ls->current > 1) {
+					// Close: animate from current down to 0
+					while (ls->current > 0) {
+						ls->current--;
+						// Sync linked sprites via key/lock
+						if (ls->key) {
+							for (int i = 0; i < (int)_lSprites.size(); i++) {
+								if (i != index && _lSprites[i]->lock == ls->key) {
+									_lSprites[i]->current = ls->current;
+									if (_lSprites[i]->current >= (int)_lSprites[i]->objects.size())
+										_lSprites[i]->current = (int)_lSprites[i]->objects.size() - 1;
+								}
+							}
+						}
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				} else {
+					// Open: animate from current up to max
+					while (ls->current < maxFrames - 1) {
+						ls->current++;
+						// Sync linked sprites via key/lock
+						if (ls->key) {
+							for (int i = 0; i < (int)_lSprites.size(); i++) {
+								if (i != index && _lSprites[i]->lock == ls->key) {
+									_lSprites[i]->current = ls->current;
+									if (_lSprites[i]->current >= (int)_lSprites[i]->objects.size())
+										_lSprites[i]->current = (int)_lSprites[i]->objects.size() - 1;
+								}
+							}
+						}
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				}
+			}
+		} else {
+			moveObject(index);
+		}
+		break;
+	case 2: // Container and object
+		if (ls->frozen) {
+			if (!ls->locked) {
+				// Unlocked container: toggle open/close
+				if (ls->current > 0) {
+					while (ls->current > 0) {
+						ls->current--;
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				} else {
+					while (ls->current < maxFrames - 1) {
+						ls->current++;
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				}
+			} else {
+				// Locked container: current>1 closes, current==1 opens further
+				if (ls->current > 1) {
+					while (ls->current > 0) {
+						ls->current--;
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				} else if (ls->current == 1) {
+					while (ls->current < maxFrames - 1) {
+						ls->current++;
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(50);
+					}
+				}
+			}
+		} else {
+			moveObject(index);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+void ColonyEngine::setObjectState(int num, int state) {
+	num--;
+	if (num >= 0 && num < (int)_lSprites.size())
+		_lSprites[num]->current = state - 1;
+}
+
+int ColonyEngine::objectState(int num) const {
+	num--;
+	if (num >= 0 && num < (int)_lSprites.size())
+		return _lSprites[num]->current + 1;
+	return 0;
+}
+
+void ColonyEngine::setObjectOnOff(int num, bool on) {
+	num--;
+	if (num >= 0 && num < (int)_lSprites.size())
+		_lSprites[num]->onoff = on;
+}
+
+void ColonyEngine::refreshAnimationDisplay() {
+	for (int i = 0; i < 6; i++) {
+		if (_animDisplay[i] < 9) {
+			setObjectOnOff(13 + i * 2, true);
+			setObjectOnOff(14 + i * 2, false);
+			setObjectState(13 + i * 2, _animDisplay[i]);
+		} else {
+			setObjectOnOff(14 + i * 2, true);
+			setObjectOnOff(13 + i * 2, false);
+			setObjectState(14 + i * 2, _animDisplay[i] - 8);
+		}
+	}
+}
+
+void ColonyEngine::crypt(uint8 sarray[6], int i, int j, int k, int l) {
+	int res[6];
+	res[0] = ((3 * l) ^ i ^ j ^ k) % 10;
+	res[1] = ((i * 3) ^ (j * 7) ^ (k * 11) ^ (l * 13)) % 10;
+	res[2] = (3 + (l * 17) ^ (j * 19) ^ (k * 23) ^ (i * 29)) % 10;
+	res[3] = ((l * 19) ^ (j * 23) ^ (k * 29) ^ (i * 31)) % 10;
+	res[4] = ((l * 17) | (j * 19) | (k * 23) | (i * 29)) % 10;
+	res[5] = (29 + (l * 17) - (j * 19) - (k * 23) - (i * 29)) % 10;
+	for (int m = 0; m < 6; m++) {
+		if (res[m] < 0)
+			res[m] = -res[m];
+		sarray[m] = (uint8)(res[m] + 2);
+	}
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c60e5609442..ce2b8245a26 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -22,175 +22,19 @@
 #include "colony/colony.h"
 #include "colony/gfx.h"
 #include "common/config-manager.h"
-#include "common/hashmap.h"
 #include "common/file.h"
 #include "common/system.h"
 #include "common/util.h"
-#include "common/algorithm.h"
 #include "common/debug.h"
 #include "common/events.h"
 #include "common/keyboard.h"
 #include "engines/util.h"
 #include "graphics/palette.h"
 #include "graphics/paletteman.h"
-#include "graphics/fontman.h"
-#include "graphics/font.h"
-#include "graphics/fonts/dosfont.h"
-#include "graphics/fonts/macfont.h"
-#include "graphics/cursorman.h"
-#include "image/pict.h"
 #include <math.h>
 
 namespace Colony {
 
-// Mac color indices from colordef.h enum (cColor[] table in Color256).
-enum {
-	mc_dwall = 6, mc_lwall = 7,
-	mc_char0 = 8,    // char0..char6 = 8..14
-	mc_bulkhead = 15, mc_door = 16,
-	mc_desk = 58, mc_desktop = 59, mc_screen = 62,
-	mc_proj = 72, mc_console = 79, mc_powerbase = 81,
-	mc_box1 = 84, mc_forklift = 86, mc_flglass = 87,
-	mc_cryo = 90, mc_ccore = 111,
-	mc_teleport = 93, mc_teledoor = 94,
-	mc_vanity = 96, mc_mirror = 103,
-	mc_airlock = 25, mc_elevator = 23
-};
-
-// Mac Toolbox BackColor() constants.
-enum {
-	kMacWhite = 30, kMacBlack = 33,
-	kMacYellow = 69, kMacMagenta = 137,
-	kMacRed = 205, kMacCyan = 273,
-	kMacGreen = 341, kMacBlue = 409
-};
-
-// BMColor arrays from ganimate.c  per-animation color maps.
-// Index 0 = background top, 1 = background image, 2+ = per-sprite fill.
-// Positive = cColor[] index, negative = -MacSystemColor, 0 = level-based.
-static const int16 kBMC_Desk[] = {
-	0, mc_desktop,
-	-kMacRed, -kMacCyan, -kMacCyan, -kMacCyan, -kMacCyan,
-	-kMacWhite, -kMacWhite, -kMacMagenta, -kMacYellow, mc_desk,
-	mc_desk, mc_desk, mc_desk, mc_desk, mc_desk,
-	-kMacWhite, mc_screen, -kMacMagenta, -kMacCyan, -kMacCyan,
-	-kMacBlue, -kMacWhite, -kMacRed, -kMacWhite, -kMacYellow
-};
-static const int16 kBMC_Vanity[] = {
-	0, mc_vanity,
-	mc_mirror, -kMacRed, -kMacCyan, -kMacWhite, -kMacYellow,
-	-kMacGreen, -kMacBlue, -kMacRed, -kMacMagenta, -kMacRed,
-	mc_vanity, -kMacWhite, -kMacYellow, mc_mirror
-};
-static const int16 kBMC_Reactor[] = {
-	0, mc_console,
-	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
-	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
-	-kMacYellow, -kMacYellow, -kMacRed, -kMacRed, -kMacRed,
-	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacRed,
-	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacCyan,
-	-kMacMagenta, -kMacWhite
-};
-static const int16 kBMC_Security[] = {
-	0, mc_console,
-	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
-	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
-	-kMacYellow, -kMacYellow, -kMacRed, -kMacRed, -kMacRed,
-	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacRed,
-	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacWhite,
-	-kMacRed, -kMacCyan, -kMacCyan, -kMacCyan, -kMacCyan
-};
-static const int16 kBMC_Teleport[] = {
-	0, mc_teleport, 0, mc_teledoor
-};
-static const int16 kBMC_Creatures[] = {
-	-kMacWhite, 0, -kMacWhite, -kMacCyan, mc_proj,
-	-kMacBlue, -kMacMagenta, -kMacMagenta
-};
-static const int16 kBMC_Controls[] = {
-	0, mc_console,
-	-kMacRed, -kMacYellow, -kMacYellow, -kMacBlue, -kMacYellow, -kMacGreen, mc_screen
-};
-static const int16 kBMC_Lift[] = {
-	0, mc_flglass,
-	mc_teleport, mc_box1, mc_cryo, mc_ccore, 0,
-	-kMacRed, -kMacRed, -kMacCyan, -kMacCyan
-};
-static const int16 kBMC_Powersuit[] = {
-	0, mc_powerbase,
-	-kMacMagenta, -kMacMagenta, -kMacYellow, -kMacYellow, mc_powerbase, -kMacWhite
-};
-static const int16 kBMC_Forklift[] = {
-	0, mc_forklift, mc_forklift, mc_forklift
-};
-static const int16 kBMC_Door[] = {
-	0, mc_bulkhead, 0, mc_door, -kMacYellow
-};
-static const int16 kBMC_Bulkhead[] = {
-	0, mc_bulkhead, 0, mc_bulkhead, -kMacYellow
-};
-static const int16 kBMC_Airlock[] = {
-	0, mc_bulkhead, mc_bulkhead, -kMacRed, mc_airlock
-};
-static const int16 kBMC_Elevator[] = {
-	0, mc_bulkhead, 0, mc_elevator, mc_elevator, -kMacYellow
-};
-static const int16 kBMC_Elevator2[] = {
-	0, mc_bulkhead, 0, -kMacMagenta, mc_elevator, mc_elevator,
-	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow
-};
-
-struct AnimColorEntry {
-	const char *name;
-	const int16 *colors;
-	int count;
-};
-
-static const AnimColorEntry kAnimColors[] = {
-	{ "desk",        kBMC_Desk,       ARRAYSIZE(kBMC_Desk) },
-	{ "vanity",      kBMC_Vanity,     ARRAYSIZE(kBMC_Vanity) },
-	{ "reactor",     kBMC_Reactor,    ARRAYSIZE(kBMC_Reactor) },
-	{ "security",    kBMC_Security,   ARRAYSIZE(kBMC_Security) },
-	{ "teleporter",  kBMC_Teleport,   ARRAYSIZE(kBMC_Teleport) },
-	{ "teleporter2", kBMC_Teleport,   ARRAYSIZE(kBMC_Teleport) },
-	{ "slides",      kBMC_Creatures,  ARRAYSIZE(kBMC_Creatures) },
-	{ "slideshow",   kBMC_Creatures,  ARRAYSIZE(kBMC_Creatures) },
-	{ "teleshow",    kBMC_Creatures,  ARRAYSIZE(kBMC_Creatures) },
-	{ "controls",    kBMC_Controls,   ARRAYSIZE(kBMC_Controls) },
-	{ "lift",        kBMC_Lift,       ARRAYSIZE(kBMC_Lift) },
-	{ "lifter",      kBMC_Lift,       ARRAYSIZE(kBMC_Lift) },
-	{ "suit",        kBMC_Powersuit,  ARRAYSIZE(kBMC_Powersuit) },
-	{ "spacesuit",   kBMC_Powersuit,  ARRAYSIZE(kBMC_Powersuit) },
-	{ "forklift",    kBMC_Forklift,   ARRAYSIZE(kBMC_Forklift) },
-	{ "door",        kBMC_Door,       ARRAYSIZE(kBMC_Door) },
-	{ "bulkhead",    kBMC_Bulkhead,   ARRAYSIZE(kBMC_Bulkhead) },
-	{ "airlock",     kBMC_Airlock,    ARRAYSIZE(kBMC_Airlock) },
-	{ "elev",        kBMC_Elevator,   ARRAYSIZE(kBMC_Elevator) },
-	{ "elevator",    kBMC_Elevator,   ARRAYSIZE(kBMC_Elevator) },
-	{ "elevator2",   kBMC_Elevator2,  ARRAYSIZE(kBMC_Elevator2) },
-	{ nullptr, nullptr, 0 }
-};
-
-// Convert Mac Toolbox BackColor constant to ARGB.
-static uint32 macSysColorToARGB(int sysColor) {
-	switch (sysColor) {
-	case kMacWhite:   return 0xFFFFFFFF;
-	case kMacBlack:   return 0xFF000000;
-	case kMacYellow:  return 0xFFFFFF00;
-	case kMacMagenta: return 0xFFFF00FF;
-	case kMacRed:     return 0xFFFF0000;
-	case kMacCyan:    return 0xFF00FFFF;
-	case kMacGreen:   return 0xFF00FF00;
-	case kMacBlue:    return 0xFF0000FF;
-	default:          return 0xFFFFFFFF;
-	}
-}
-
-static uint32 packMacColorBG(const uint16 rgb[3]) {
-	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
-	       ((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
-}
-
 ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd), _randomSource("colony") {
 	_level = 0;
 	_robotNum = 0;
@@ -222,7 +66,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 		else
 			_renderMode = Common::kRenderEGA;
 	}
-	
+
 	_wireframe = (_renderMode != Common::kRenderMacintosh);
 	_fullscreen = false;
 	_speedShift = 2; // DOS default: speedshift=1, but 2 feels better with our frame rate
@@ -236,7 +80,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_macMenu = nullptr;
 	_menuSurface = nullptr;
 	_menuBarHeight = 0;
-	
+
 	memset(_wall, 0, sizeof(_wall));
 	memset(_mapData, 0, sizeof(_mapData));
 	memset(_robotArray, 0, sizeof(_robotArray));
@@ -255,7 +99,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_compassRect = Common::Rect(0, 0, 0, 0);
 	_headsUpRect = Common::Rect(0, 0, 0, 0);
 	_powerRect = Common::Rect(0, 0, 0, 0);
-	
+
 	// DOS gameInit(): Me.ang=Me.look=32; Me.xloc=4400; Me.yloc=4400.
 	memset(&_me, 0, sizeof(_me));
 	_me.xloc = 4400;
@@ -323,332 +167,6 @@ ColonyEngine::~ColonyEngine() {
 	delete _wm;
 }
 
-
-void ColonyEngine::loadMap(int mnum) {
-	Common::Path mapPath(Common::String::format("MAP.%d", mnum));
-	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(mapPath);
-	if (!file) {
-		// Try Mac-style path
-		mapPath = Common::Path(Common::String::format("CData/map.%d", mnum));
-		file = Common::MacResManager::openFileOrDataFork(mapPath);
-		if (!file) {
-			warning("Could not open map file %s", mapPath.toString().c_str());
-			return;
-		}
-	}
-
-	file->readUint32BE(); // "DAVE" header
-	int16 mapDefs[10];
-	for (int i = 0; i < 10; i++) {
-		mapDefs[i] = file->readSint16BE();
-	}
-
-	uint16 bLength = file->readUint16BE();
-	uint8 *buffer = (uint8 *)malloc(bLength);
-	if (!buffer) {
-		delete file;
-		error("Out of memory loading map");
-	}
-	file->read(buffer, bLength);
-	delete file;
-
-	memset(_mapData, 0, sizeof(_mapData));
-	memset(_robotArray, 0, sizeof(_robotArray));
-	memset(_foodArray, 0, sizeof(_foodArray));
-	_objects.clear();
-
-	// expand logic
-	int c = 0;
-	_robotNum = kMeNum + 1;
-	for (int i = 0; i < 32; i++) {
-		for (int j = 0; j < 32; j++) {
-			_wall[i][j] = buffer[c++];
-			if (i < 31 && j < 31) {
-				for (int k = 0; k < 5; k++) {
-					if (_wall[i][j] & (1 << (k + 2))) {
-						for (int l = 0; l < 5; l++) {
-							_mapData[i][j][k][l] = buffer[c++];
-						}
-						// PACKIT.C: center feature type 6 marks static map objects.
-						if (k == 4 && _mapData[i][j][4][0] == 6 && i < 31 && j < 31) {
-							Thing obj;
-							memset(&obj, 0, sizeof(obj));
-							obj.alive = 1;
-							obj.visible = 0;
-							obj.type = _mapData[i][j][4][1] + kBaseObject;
-							obj.where.xloc = (i << 8) + 128;
-							obj.where.yloc = (j << 8) + 128;
-							obj.where.xindex = i;
-							obj.where.yindex = j;
-							obj.where.ang = (uint8)(_mapData[i][j][4][2] + 32);
-							obj.where.look = obj.where.ang;
-							_objects.push_back(obj);
-							const int objNum = (int)_objects.size(); // 1-based, DOS-style robot slots
-							if (objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
-								_robotArray[i][j] = (uint8)objNum;
-						}
-					} else {
-						_mapData[i][j][k][0] = 0;
-					}
-				}
-			}
-		}
-	}
-	free(buffer);
-	_level = mnum;
-	_me.type = kMeNum;
-
-	getWall();  // restore saved wall state changes (airlocks)
-	doPatch();  // apply object relocations from patch table
-	initRobots();  // spawn robot objects for this level
-
-	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-		_robotArray[_me.xindex][_me.yindex] = kMeNum;
-	debug("Successfully loaded map %d (objects: %d)", mnum, (int)_objects.size());
-}
-
-// PATCH.C: Create a new object in _objects and register in _robotArray.
-// Mirrors DOS CreateObject()  sets basic Thing fields for static objects.
-void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
-	Thing obj;
-	memset(&obj, 0, sizeof(obj));
-	while (ang > 255)
-		ang -= 256;
-	obj.alive = 1;
-	obj.visible = 0;
-	obj.type = type;
-	obj.where.xloc = xloc;
-	obj.where.yloc = yloc;
-	obj.where.xindex = xloc >> 8;
-	obj.where.yindex = yloc >> 8;
-	obj.where.delta = 4;
-	obj.where.ang = ang;
-	obj.where.look = ang;
-
-	// Try to reuse a dead slot (starting after kMeNum)
-	int slot = -1;
-	for (int j = kMeNum; j < (int)_objects.size(); j++) {
-		if (!_objects[j].alive) {
-			slot = j;
-			break;
-		}
-	}
-	if (slot >= 0) {
-		_objects[slot] = obj;
-	} else {
-		_objects.push_back(obj);
-		slot = (int)_objects.size() - 1;
-	}
-	int objNum = slot + 1; // 1-based for _robotArray
-	if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-	    obj.where.yindex >= 0 && obj.where.yindex < 32)
-		_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)objNum;
-	if (slot + 1 > _robotNum)
-		_robotNum = slot + 1;
-}
-
-// PATCH.C: DoPatch()  remove originals and install relocated objects.
-void ColonyEngine::doPatch() {
-	// Pass 1: remove objects that were moved away from this level
-	for (uint i = 0; i < _patches.size(); i++) {
-		if (_level == _patches[i].from.level) {
-			int robot = _robotArray[_patches[i].from.xindex][_patches[i].from.yindex];
-			if (robot > 0 && robot <= (int)_objects.size()) {
-				_robotArray[_objects[robot - 1].where.xindex][_objects[robot - 1].where.yindex] = 0;
-				_objects[robot - 1].alive = 0;
-			}
-		}
-	}
-	// Pass 2: install objects that were moved to this level
-	for (uint i = 0; i < _patches.size(); i++) {
-		if (_level == _patches[i].to.level) {
-			createObject(
-				(int)_patches[i].type,
-				(int)_patches[i].to.xloc,
-				(int)_patches[i].to.yloc,
-				_patches[i].to.ang);
-		}
-	}
-}
-
-// PATCH.C: savewall()  save 5 bytes of map feature data for persistence across level loads.
-void ColonyEngine::saveWall(int x, int y, int direction) {
-	if (_level < 1 || _level > 8)
-		return;
-	LevelData &ld = _levelData[_level - 1];
-
-	// Search for existing entry at this location
-	for (int i = 0; i < ld.size; i++) {
-		if (ld.location[i][0] == x && ld.location[i][1] == y && ld.location[i][2] == direction) {
-			for (int j = 0; j < 5; j++)
-				ld.data[i][j] = _mapData[x][y][direction][j];
-			return;
-		}
-	}
-	// Add new entry (max 10)
-	if (ld.size >= 10) {
-		warning("saveWall: too many wall changes for level %d", _level);
-		return;
-	}
-	int i = ld.size;
-	for (int j = 0; j < 5; j++)
-		ld.data[i][j] = _mapData[x][y][direction][j];
-	ld.location[i][0] = x;
-	ld.location[i][1] = y;
-	ld.location[i][2] = direction;
-	ld.size++;
-}
-
-// PATCH.C: getwall()  restore saved wall bytes into _mapData after level load.
-void ColonyEngine::getWall() {
-	if (_level < 1 || _level > 8)
-		return;
-	const LevelData &ld = _levelData[_level - 1];
-	for (int i = 0; i < ld.size; i++) {
-		int x = ld.location[i][0];
-		int y = ld.location[i][1];
-		int dir = ld.location[i][2];
-		if (x < 31 && y < 31 && dir < 5) {
-			for (int j = 0; j < 5; j++)
-				_mapData[x][y][dir][j] = ld.data[i][j];
-		}
-	}
-}
-
-// PATCH.C: newpatch()  create or update a patch entry.
-void ColonyEngine::newPatch(int type, const PassPatch &from, const PassPatch &to, const uint8 *mapdata) {
-	// Search for existing patch where 'from' matches an existing 'to'
-	for (uint i = 0; i < _patches.size(); i++) {
-		if (from.level == _patches[i].to.level &&
-		    from.xindex == _patches[i].to.xindex &&
-		    from.yindex == _patches[i].to.yindex) {
-			_patches[i].to.level = to.level;
-			_patches[i].to.xindex = to.xindex;
-			_patches[i].to.yindex = to.yindex;
-			_patches[i].to.xloc = to.xloc;
-			_patches[i].to.yloc = to.yloc;
-			_patches[i].to.ang = to.ang;
-			return;
-		}
-	}
-	// Create new patch entry (max 100)
-	if (_patches.size() >= 100)
-		return;
-	PatchEntry pe;
-	pe.type = type;
-	pe.from.level = from.level;
-	pe.from.xindex = from.xindex;
-	pe.from.yindex = from.yindex;
-	pe.to.level = to.level;
-	pe.to.xindex = to.xindex;
-	pe.to.yindex = to.yindex;
-	pe.to.xloc = to.xloc;
-	pe.to.yloc = to.yloc;
-	pe.to.ang = to.ang;
-	if (mapdata) {
-		for (int j = 0; j < 5; j++)
-			pe.mapdata[j] = mapdata[j];
-	} else {
-		memset(pe.mapdata, 0, sizeof(pe.mapdata));
-	}
-	_patches.push_back(pe);
-}
-
-// PATCH.C: patchmapto()  find patch entry by destination, fill mapdata.
-bool ColonyEngine::patchMapTo(const PassPatch &to, uint8 *mapdata) {
-	for (uint i = 0; i < _patches.size(); i++) {
-		if (to.level == _patches[i].to.level &&
-		    to.xindex == _patches[i].to.xindex &&
-		    to.yindex == _patches[i].to.yindex) {
-			for (int j = 0; j < 5; j++)
-				mapdata[j] = _patches[i].mapdata[j];
-			return true;
-		}
-	}
-	return false;
-}
-
-// PATCH.C: patchmapfrom()  find patch entry by source, fill destination into mapdata.
-bool ColonyEngine::patchMapFrom(const PassPatch &from, uint8 *mapdata) {
-	for (uint i = 0; i < _patches.size(); i++) {
-		if (from.level == _patches[i].from.level &&
-		    from.xindex == _patches[i].from.xindex &&
-		    from.yindex == _patches[i].from.yindex) {
-			mapdata[2] = _patches[i].to.level;
-			mapdata[3] = _patches[i].to.xindex;
-			mapdata[4] = _patches[i].to.yindex;
-			return true;
-		}
-	}
-	return false;
-}
-
-// DOS InitObject()  spawn robots for the current level.
-// Level 1 = no robots; Level 2 = 25; Level 3-4 = 30; Level 5-7 = 35.
-// Robot #1 = QUEEN, #2 = SNOOP, rest = random type weighted by level.
-void ColonyEngine::initRobots() {
-	if (_level == 1)
-		return;  // Level 1 has no robots
-
-	int maxrob;
-	switch (_level) {
-	case 2:  maxrob = 25; break;
-	case 3:
-	case 4:  maxrob = 30; break;
-	default: maxrob = 35; break;
-	}
-
-	int lvl = _level - 1;
-	if (lvl > 5)
-		lvl = 5;
-
-	for (int i = 1; i <= maxrob; i++) {
-		uint8 ang = _randomSource.getRandomNumber(255);
-		int xloc, yloc;
-
-		// Find unoccupied cell (avoiding borders)
-		do {
-			if (_level == 7 && i == 1) {
-				// Queen on level 7 has fixed position
-				xloc = 27;
-				yloc = 10;
-			} else {
-				xloc = 2 + _randomSource.getRandomNumber(26);  // 2..28
-				yloc = 2 + _randomSource.getRandomNumber(26);  // 2..28
-			}
-		} while (_robotArray[xloc][yloc] != 0);
-
-		// Convert grid coords to world coords (center of cell)
-		int wxloc = (xloc << 8) + 128;
-		int wyloc = (yloc << 8) + 128;
-
-		int type;
-		if (i == 1)
-			type = kRobQueen;
-		else if (i == 2)
-			type = kRobSnoop;
-		else {
-			// Random type weighted by level difficulty
-			int rnd = _randomSource.getRandomNumber(lvl);
-			if (rnd > 5)
-				rnd = 5;
-			switch (rnd) {
-			case 0: type = kRobCube; break;
-			case 1: type = kRobPyramid; break;
-			case 2: type = kRobUPyramid; break;
-			case 3: type = kRobEye; break;
-			case 4: type = kRobDrone; break;
-			case 5: type = kRobSoldier; break;
-			default: type = kRobCube; break;
-			}
-		}
-
-		createObject(type, wxloc, wyloc, ang);
-	}
-
-	debug("initRobots: spawned %d robots on level %d", maxrob, _level);
-}
-
 void ColonyEngine::loadMacColors() {
 	_hasMacColors = false;
 	Common::SeekableReadStream *file = nullptr;
@@ -1103,7 +621,7 @@ Common::Error ColonyEngine::run() {
 		drawDashboardStep1();
 		drawCrosshair();
 		checkCenter();
-		
+
 		// Draw Mac menu bar overlay (render directly to our surface, skip WM's
 		// g_system->copyRectToScreen which conflicts with the OpenGL backend)
 		if (_macMenu && _menuSurface && !_fullscreen) {
@@ -1181,1942 +699,4 @@ bool ColonyEngine::waitForInput() {
 	return false;
 }
 
-void ColonyEngine::playIntro() {
-	if (getPlatform() == Common::kPlatformMacintosh) {
-		// Load the Mac "Commando" font (FOND 190, 12pt) from Colony resources.
-		// Original intro.c: TextFont(190); TextSize(12);
-		// FONT resource ID = FOND_ID * 128 + size = 190 * 128 + 12 = 24332
-		// Some builds store it as NFNT instead of FONT.
-		Graphics::MacFONTFont *macFont = nullptr;
-		if (_resMan) {
-			const uint16 fontResID = 24332;
-			Common::SeekableReadStream *fontStream = _resMan->getResource(MKTAG('N', 'F', 'N', 'T'), fontResID);
-			if (!fontStream)
-				fontStream = _resMan->getResource(MKTAG('F', 'O', 'N', 'T'), fontResID);
-			if (fontStream) {
-				macFont = new Graphics::MacFONTFont();
-				if (!macFont->loadFont(*fontStream)) {
-					warning("playIntro: failed to load Commando 12pt font");
-					delete macFont;
-					macFont = nullptr;
-				}
-				delete fontStream;
-			} else {
-				// List available font resources for debugging
-				Common::MacResIDArray nfntIDs = _resMan->getResIDArray(MKTAG('N', 'F', 'N', 'T'));
-				Common::MacResIDArray fontIDs = _resMan->getResIDArray(MKTAG('F', 'O', 'N', 'T'));
-				debug("playIntro: FONT/NFNT %d not found. Available NFNT IDs: %d, FONT IDs: %d",
-				      fontResID, nfntIDs.size(), fontIDs.size());
-				for (uint i = 0; i < nfntIDs.size(); i++)
-					debug("  NFNT %d", nfntIDs[i]);
-				for (uint i = 0; i < fontIDs.size(); i++)
-					debug("  FONT %d", fontIDs[i]);
-			}
-		}
-
-		// Original: intro() in intro.c, lines 40-119
-		// qt flag propagates through sections  only modifier+click sets it
-		bool qt = false;
-
-		// 1. ScrollInfo() - scrolling story text with BeamMe sound
-		qt = scrollInfo(macFont);
-
-		// 2. Wait for BeamMe sound to finish
-		// Original: if(!qt) while(!SoundDone());
-		while (!qt && !shouldQuit() && _sound->isPlaying())
-			_system->delayMillis(10);
-
-		// Original: if(Button()) qt=OptionKey();  check for skip
-		if (!qt)
-			qt = checkSkipRequested();
-
-		if (!qt) {
-			// 3. Logo 1 + PlayMars + makestars
-			// Original: FillRect black; DoPicture; PlayMars(); makestars()
-			_gfx->clear(_gfx->black());
-			if (!drawPict(-32565))  // Color Colony
-				drawPict(-32748);   // B&W Colony
-			_sound->play(Sound::kMars);
-			qt = makeStars(_screenR, 0);
-
-			if (!qt) {
-				// 4. Logo 2 + makestars (inside the same !qt block as original)
-				// Original: FillRect black; DoPicture(-32564); makestars()
-				_gfx->clear(_gfx->black());
-				if (!drawPict(-32564))  // Color Colony
-					drawPict(-32750);   // B&W Colony
-				qt = makeStars(_screenR, 0);
-			}
-
-			if (!qt) {
-				// 5. Empty starfield
-				// Original: FillRect black; makestars()
-				_gfx->clear(_gfx->black());
-				_gfx->copyToScreen();
-				qt = makeStars(_screenR, 0);
-			}
-
-			if (!qt) {
-				// 6. TimeSquare("...BLACK HOLE COLLISION...")
-				qt = timeSquare("...BLACK HOLE COLLISION...", macFont);
-			}
-
-			if (!qt) {
-				// 7. Makeblackhole()
-				_gfx->clear(_gfx->black());
-				_gfx->copyToScreen();
-				qt = makeBlackHole();
-			}
-
-			if (!qt) {
-				// 8. TimeSquare("...FUEL HAS BEEN DEPLETED...")
-				qt = timeSquare("...FUEL HAS BEEN DEPLETED...", macFont);
-			}
-
-			// Original: SetPort(&metaPort); before next TimeSquare (no !qt guard)
-			if (!qt) {
-				// 9. TimeSquare("...PREPARE FOR CRASH LANDING...")
-				qt = timeSquare("...PREPARE FOR CRASH LANDING...", macFont);
-			}
-
-			if (!qt) {
-				// 10. makeplanet() + EndCSound()
-				// Simplified: starfield + delay (makeplanet draws a rotating planet)
-				_gfx->clear(_gfx->black());
-				_gfx->copyToScreen();
-				qt = makeStars(_screenR, 0);
-				_sound->stop(); // EndCSound()
-			}
-		}
-
-		// 11. Final crash  always runs (even if qt)
-		// Original: FillRect black; if(!qt) while(!SoundDone());
-		_gfx->clear(_gfx->black());
-		_gfx->copyToScreen();
-		while (!qt && !shouldQuit() && _sound->isPlaying())
-			_system->delayMillis(10);
-
-		// Original: DoExplodeSound(); while(!SoundDone()) InvertRect(&rScreen); StopSound();
-		_sound->play(Sound::kExplode);
-		while (!shouldQuit() && _sound->isPlaying()) {
-			_gfx->clear(_gfx->white());
-			_gfx->copyToScreen();
-			_system->delayMillis(50);
-			_gfx->clear(_gfx->black());
-			_gfx->copyToScreen();
-			_system->delayMillis(50);
-		}
-		_sound->stop();
-		_gfx->clear(_gfx->black());
-		_gfx->copyToScreen();
-		delete macFont;
-		macFont = nullptr;
-
-		// Restore palette entries modified during intro (128, 160-176, 200-213)
-		// back to grayscale so normal gameplay rendering isn't affected
-		byte restorePal[256 * 3];
-		for (int i = 0; i < 256; i++) {
-			restorePal[i * 3 + 0] = i;
-			restorePal[i * 3 + 1] = i;
-			restorePal[i * 3 + 2] = i;
-		}
-		_gfx->setPalette(restorePal + 128 * 3, 128, 128);
-	} else {
-		scrollInfo();
-	}
-}
-
-bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
-	// Original: ScrollInfo() in intro.c, lines 138-221
-	// Renders story text in blue gradient to offscreen half-width buffer,
-	// scrolls it up from below screen with DoBeammeSound(),
-	// waits for click, then scrolls it off the top.
-	// Mac original: TextFont(190 = Commando); TextSize(12);
-	// Text blue starts at 0xFFFF and fades by -4096 per visible line.
-	const char *story[] = {
-		"",
-		"Mankind has left the",
-		"cradle of earth and",
-		"is beginning to eye",
-		"the galaxy. He has",
-		"begun to colonize",
-		"distant planets but has",
-		"yet to meet any alien",
-		"life forms.",
-		"",      // null separator in original
-		"Until now...",
-		"",      // null separator in original
-		"Click to begin",
-		"the Adventure..."
-	};
-	const int storyLength = ARRAYSIZE(story);
-
-	if (getPlatform() == Common::kPlatformMacintosh)
-		_sound->play(Sound::kBeamMe);
-
-	_gfx->clear(_gfx->black());
-	_gfx->copyToScreen();
-
-	Graphics::DosFont dosFont;
-	const Graphics::Font *font = macFont ? macFont : (const Graphics::Font *)&dosFont;
-
-	// Original uses 19px line height, centers vertically within height
-	int lineHeight = 19;
-	int totalHeight = lineHeight * storyLength;
-	int ht = (_height - totalHeight) / 2;
-
-	// 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);
-	byte pal[14 * 3]; // storyLength entries
-	memset(pal, 0, sizeof(pal));
-	for (int i = 0; i < storyLength; i++) {
-		int val = 255 - i * 16;
-		if (val < 0)
-			val = 0;
-		pal[i * 3 + 0] = bwMac ? val : 0;  // R
-		pal[i * 3 + 1] = bwMac ? val : 0;  // G
-		pal[i * 3 + 2] = val;               // B
-	}
-	_gfx->setPalette(pal, 200, storyLength);
-
-	// Phase 1: Scroll text up from below screen
-	// Original: scrollRect starts at bottom (stayRect.bottom..stayRect.bottom*2),
-	// moves up by inc=4 each frame until text is visible at its correct position.
-	// We simulate by drawing text with a y-offset that starts at _height and decreases to 0.
-	int inc = 4;
-	bool qt = false;
-
-	for (int scrollOff = _height; scrollOff > 0 && !qt; scrollOff -= inc) {
-		if (checkSkipRequested()) {
-			qt = true;
-			_sound->stop();
-			break;
-		}
-
-		_gfx->clear(_gfx->black());
-		for (int i = 0; i < storyLength; i++) {
-			int drawY = ht + lineHeight * i + scrollOff;
-			if (strlen(story[i]) > 0 && drawY >= 0 && drawY < _height)
-				_gfx->drawString(font, story[i], _width / 2, drawY, 200 + i, Graphics::kTextAlignCenter);
-		}
-		_gfx->copyToScreen();
-		_system->delayMillis(16);
-	}
-
-	// Draw final position (scrollOff = 0)
-	if (!qt) {
-		_gfx->clear(_gfx->black());
-		for (int i = 0; i < storyLength; i++) {
-			if (strlen(story[i]) > 0)
-				_gfx->drawString(font, story[i], _width / 2, ht + lineHeight * i, 200 + i, Graphics::kTextAlignCenter);
-		}
-		_gfx->copyToScreen();
-	}
-
-	// Wait for click (original: while(!Button()); while(Button()&&!qt);)
-	if (!qt)
-		qt = waitForInput();
-
-	// Phase 2: Scroll text off the top of the screen
-	// Original: scrollRect continues moving up, text slides upward
-	if (!qt) {
-		for (int scrollOff = 0; scrollOff > -_height && !qt; scrollOff -= inc) {
-			if (checkSkipRequested()) {
-			qt = true;
-			_sound->stop();
-			break;
-		}
-
-			_gfx->clear(_gfx->black());
-			for (int i = 0; i < storyLength; i++) {
-				int drawY = ht + lineHeight * i + scrollOff;
-				if (strlen(story[i]) > 0 && drawY >= -lineHeight && drawY < _height)
-					_gfx->drawString(font, story[i], _width / 2, drawY, 200 + i, Graphics::kTextAlignCenter);
-			}
-			_gfx->copyToScreen();
-			_system->delayMillis(16);
-		}
-	}
-
-	// Original does NOT stop the sound here  BeamMe continues playing
-	// and intro() waits for it with while(!SoundDone()) after ScrollInfo returns.
-	// Only stop if skipping (qt already stops in the modifier+click handlers above).
-	_gfx->clear(_gfx->black());
-	_gfx->copyToScreen();
-	return qt;
-}
-
-bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
-	// Original: makestars() in stars.c
-	// Uses 75 moving stars that streak outward from center using XOR lines.
-	const int MAXSTAR = 0x1FF;
-	const int NSTARS = 75;
-	const int deltapd = 0x008;
-
-	int centerX = r.width() / 2;
-	int centerY = r.height() / 2;
-
-	// Build perspective lookup table: rtable[i] = (128*128)/i
-	int rtable[MAXSTAR + 1];
-	rtable[0] = 32000;
-	for (int i = 1; i <= MAXSTAR; i++)
-		rtable[i] = (128 * 128) / i;
-
-	// First draw static background stars (150 random dots)
-	for (int i = 0; i < 150; i++) {
-		int s = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
-		int c = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
-		int d = _randomSource.getRandomNumber(MAXSTAR);
-		if (d < 1) d = 1;
-		int rr = rtable[d];
-		int xx = centerX + (int)(((long long)s * rr) >> 7);
-		int yy = centerY + (int)(((long long)c * rr) >> 7);
-		if (xx >= 0 && xx < _width && yy >= 0 && yy < _height)
-			_gfx->setPixel(xx, yy, 15);
-	}
-
-	// Initialize moving stars  original uses PenMode(patXor) so stars
-	// don't damage the logo underneath (XOR drawing the same line twice
-	// restores the original pixels).
-	int xang[NSTARS], yang[NSTARS], dist[NSTARS];
-	int xsave1[NSTARS], ysave1[NSTARS], xsave2[NSTARS], ysave2[NSTARS];
-
-	_gfx->setXorMode(true);
-
-	for (int i = 0; i < NSTARS; i++) {
-		int d = dist[i] = _randomSource.getRandomNumber(MAXSTAR);
-		if (d <= 0x030) d = dist[i] = MAXSTAR;
-		int s = xang[i] = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
-		int c = yang[i] = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
-
-		int rr = rtable[d];
-		xsave1[i] = centerX + (int)(((long long)s * rr) >> 7);
-		ysave1[i] = centerY + (int)(((long long)c * rr) >> 7);
-
-		int d2 = d - deltapd;
-		if (d2 < 1)
-			d2 = 1;
-		rr = rtable[d2];
-		xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
-		ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
-
-		_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
-	}
-	_gfx->copyToScreen();
-
-	// Animate: original loops ~200 frames or until Mars sound repeats 2x
-	for (int k = 0; k < 120; k++) {
-		if (checkSkipRequested()) {
-			_gfx->setXorMode(false);
-			return true;
-		}
-
-		for (int i = 0; i < NSTARS; i++) {
-			// Erase previous  XOR the same line again to restore underlying pixels
-			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
-
-			int s = xang[i];
-			int c = yang[i];
-
-			if (dist[i] <= 0x030) {
-				dist[i] = MAXSTAR;
-				int rr = rtable[MAXSTAR];
-				xsave1[i] = centerX + (int)(((long long)s * rr) >> 7);
-				ysave1[i] = centerY + (int)(((long long)c * rr) >> 7);
-			} else {
-				xsave1[i] = xsave2[i];
-				ysave1[i] = ysave2[i];
-			}
-
-			int d = (dist[i] -= deltapd);
-			if (d < 1) d = 1;
-			int rr = rtable[d];
-			xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
-			ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
-
-			// Draw new star position
-			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
-		}
-		_gfx->copyToScreen();
-		_system->delayMillis(16);
-	}
-
-	// Fade-out phase: stars fly off without resetting (trails accumulate via XOR)
-	int nstars = 2 * ((MAXSTAR - 0x030) / deltapd);
-	if (nstars > 200) nstars = 200;
-	for (int k = 0; k < nstars; k++) {
-		if (checkSkipRequested()) {
-			_gfx->setXorMode(false);
-			return true;
-		}
-
-		for (int i = 0; i < NSTARS; i++) {
-			int d = dist[i];
-			int s = xang[i];
-			int c = yang[i];
-			dist[i] -= deltapd;
-			if (dist[i] <= 0x030) dist[i] = MAXSTAR;
-
-			if (d >= 1 && d <= MAXSTAR) {
-				int rr1 = rtable[d];
-				int d2 = d - deltapd;
-				if (d2 < 1)
-			d2 = 1;
-				int rr2 = rtable[d2];
-				int x1 = centerX + (int)(((long long)s * rr1) >> 7);
-				int y1 = centerY + (int)(((long long)c * rr1) >> 7);
-				int x2 = centerX + (int)(((long long)s * rr2) >> 7);
-				int y2 = centerY + (int)(((long long)c * rr2) >> 7);
-				_gfx->drawLine(x1, y1, x2, y2, 15);
-			}
-		}
-		_gfx->copyToScreen();
-		_system->delayMillis(8);
-	}
-
-	_gfx->setXorMode(false);
-	return false;
-}
-
-bool ColonyEngine::makeBlackHole() {
-	// Original: Makeblackhole() in intro.c
-	// Mac original draws spiral lines with fading colors:
-	// bcolor starts at (0,0,0) and subtracts (rd=2048, gd=1024, bd=4096) per step,
-	// which wraps around creating shifting color gradients.
-	// We use palette entries 128-191 for the gradient colors.
-	int centerX = _width / 2;
-	int centerY = _height / 2;
-	int dec = 16;
-	int starcnt = 0;
-
-	// Build a lookup table matching the original rtable: rtable[i] = (128*128)/i
-	int rtable[1024];
-	rtable[0] = 32000;
-	for (int i = 1; i < 1024; i++)
-		rtable[i] = (128 * 128) / i;
-
-	for (int k = 0; k < 17; k += 4) {
-		// Reset color per k-iteration (matches Mac: bcolor = {0,0,0} at start of each k)
-		int colorR = 0, colorG = 0, colorB = 0;
-		int rd = 2048, gd = 1024, bd = 4096;
-
-		for (int i = 1000; i > 32; i -= dec) {
-			// Mac original subtracts from color channels (wrapping as uint16);
-			// We simulate this as a gradient from dark to bright
-			// Since Mac uses unsigned wrap: 0 - 4096 = 0xF000 = bright.
-			// After one full cycle (16 steps), the colors cycle.
-			// Map to palette entry based on step
-			colorB = (colorB - bd) & 0xFFFF;
-			colorR = (colorR - rd) & 0xFFFF;
-			colorG = (colorG - gd) & 0xFFFF;
-
-			// Map Mac 16-bit color to 8-bit
-			uint8 palR = (colorR >> 8) & 0xFF;
-			uint8 palG = (colorG >> 8) & 0xFF;
-			uint8 palB = (colorB >> 8) & 0xFF;
-
-			// Use palette entry 128 for current step color
-			byte pal[3] = { palR, palG, palB };
-			_gfx->setPalette(pal, 128, 1);
-
-			starcnt++;
-			if (starcnt == 8) starcnt = 0;
-
-			for (int j = 0; j < 256; j += 8) {
-				int idx = (j + starcnt) & 0xFF;
-				int rt1 = rtable[MIN(i + k, 1023)];
-				int x1 = centerX + (int)(((long long)rt1 * _sint[idx]) >> 7);
-				int y1 = centerY + (int)(((long long)rt1 * _cost[idx]) >> 7);
-
-				int rt2 = rtable[MIN(i + k + 8, 1023)];
-				int x2 = centerX + (int)(((long long)rt2 * _sint[idx]) >> 7);
-				int y2 = centerY + (int)(((long long)rt2 * _cost[idx]) >> 7);
-
-				_gfx->drawLine(x1, y1, x2, y2, 128);
-			}
-
-			// Update screen every step and add a small delay
-			// to simulate the original 68k rendering speed
-			_gfx->copyToScreen();
-			_system->delayMillis(16);
-
-			if (checkSkipRequested()) return true;
-		}
-	}
-	_gfx->copyToScreen();
-	return false;
-}
-
-bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *macFont) {
-	// Original: TimeSquare() in intro.c
-	// 1. Draw horizontal blue gradient lines above/below center
-	// 2. Scroll red text from right to center
-	// 3. Flash klaxon 6 times with inverted rect
-	// 4. Play Mars again, scroll text off to the left
-	//
-	// Mac original: fcolor starts at (0,0,0xFFFF) and subtracts 4096 per pair of lines.
-	// Text is drawn in red (0xFFFF,0,0) on black background.
-
-	_gfx->clear(_gfx->black());
-
-	Graphics::DosFont dosFont;
-	const Graphics::Font *font = macFont ? macFont : (const Graphics::Font *)&dosFont;
-	int swidth = font->getStringWidth(str);
-
-	int centery = _height / 2 - 10;
-
-	// Set up gradient palette entries (160-175) for the gradient lines
-	// Mac original: blue starts at 0xFFFF and decreases by 4096 per line pair
-	// B&W Mac: white gradient instead of blue
-	const bool bwMac = (macFont && !_hasMacColors);
-	for (int i = 0; i < 16; i++) {
-		int val = 255 - i * 16; // 255, 239, 223, ... 15
-		if (val < 0)
-			val = 0;
-		byte pal[3] = { (byte)(bwMac ? val : 0), (byte)(bwMac ? val : 0), (byte)val };
-		_gfx->setPalette(pal, 160 + i, 1);
-	}
-	// Set palette entry 176 for text (red in color, white in B&W)
-	{
-		byte pal[3] = { 255, (byte)(bwMac ? 255 : 0), (byte)(bwMac ? 255 : 0) };
-		_gfx->setPalette(pal, 176, 1);
-	}
-
-	// Draw blue gradient lines above/below center band
-	for (int i = 0; i < 16; i++) {
-		_gfx->drawLine(0, centery - 2 - i * 2, _width, centery - 2 - i * 2, 160 + i);
-		_gfx->drawLine(0, centery - 2 - (i * 2 + 1), _width, centery - 2 - (i * 2 + 1), 160 + i);
-		_gfx->drawLine(0, centery + 16 + i * 2, _width, centery + 16 + i * 2, 160 + i);
-		_gfx->drawLine(0, centery + 16 + i * 2 + 1, _width, centery + 16 + i * 2 + 1, 160 + i);
-	}
-	_gfx->copyToScreen();
-
-	// Phase 1: Scroll text in from the right to center
-	// Original: if(Button()) if(qt=OptionKey()) break;
-	int targetX = (_width - swidth) / 2;
-	for (int x = _width; x > targetX; x -= 2) {
-		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), 0);
-		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft);
-		_gfx->copyToScreen();
-
-		if (checkSkipRequested()) return true;
-		_system->delayMillis(8);
-	}
-
-	// Phase 2: Klaxon flash  original: EndCSound(); then 6 iterations of:
-	//   if(Button()) if(qt=OptionKey()) break;
-	//   while(!SoundDone()); StopSound(); PlayKlaxon(); InvertRect(&invrt);
-	_sound->stop(); // EndCSound()
-	for (int i = 0; i < 6; i++) {
-		if (checkSkipRequested()) return true;
-
-		// Wait for previous klaxon to finish
-		while (_sound->isPlaying() && !shouldQuit())
-			_system->delayMillis(10);
-		_sound->stop();
-
-		_sound->play(Sound::kKlaxon);
-
-		// InvertRect(&invrt)  toggle the text band
-		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), i % 2 ? 0 : 15);
-		_gfx->drawString(font, str, targetX, centery + 2, i % 2 ? 15 : 0, Graphics::kTextAlignLeft);
-		_gfx->copyToScreen();
-	}
-	// Wait for last klaxon
-	while (_sound->isPlaying() && !shouldQuit())
-		_system->delayMillis(10);
-	_sound->stop();
-
-	// Phase 3: PlayMars(), scroll text off to the left
-	_sound->play(Sound::kMars);
-	for (int x = targetX; x > -swidth; x -= 2) {
-		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), 0);
-		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft);
-		_gfx->copyToScreen();
-
-		if (checkSkipRequested()) return true;
-		_system->delayMillis(8);
-	}
-
-	return false;
-}
-
-bool ColonyEngine::drawPict(int resID) {
-	// Original: DoPicture() in intro.c, lines 861-886
-	// Loads a PICT resource, centers it in the screen rect, draws with srcCopy.
-	// Original applies clip rect inset by 1 pixel on all sides.
-	Common::SeekableReadStream *pictStream = nullptr;
-
-	// Try Color Colony resource fork first
-	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);
-
-	if (!pictStream) {
-		debug("drawPict: PICT %d not found", resID);
-		return false;
-	}
-
-	::Image::PICTDecoder decoder;
-	if (decoder.loadStream(*pictStream)) {
-		const Graphics::Surface *surface = decoder.getSurface();
-		if (surface) {
-			// Center PICT on screen (original: locate = centered within rScreen)
-			int x = (_width - surface->w) / 2;
-			int y = (_height - surface->h) / 2;
-			bool isCLUT8 = (surface->format == Graphics::PixelFormat::createFormatCLUT8());
-			const Graphics::Palette &pictPal = decoder.getPalette();
-
-			// Original DoPicture clips 1 pixel inset from locate rect
-			// clip.top = locate.top+1, clip.left = locate.left+1, etc.
-			int clipX1 = x + 1;
-			int clipY1 = y + 1;
-			int clipX2 = x + surface->w - 1;
-			int clipY2 = y + surface->h - 1;
-
-			debug("drawPict(%d): %dx%d at (%d,%d), format=%dbpp, palette=%d entries",
-			      resID, surface->w, surface->h, x, y,
-			      surface->format.bytesPerPixel * 8, pictPal.size());
-
-			// Draw PICT pixels using direct RGB (packRGB) for full color support.
-			for (int iy = 0; iy < surface->h; iy++) {
-				int sy = y + iy;
-				if (sy < clipY1 || sy >= clipY2)
-					continue;
-				for (int ix = 0; ix < surface->w; ix++) {
-					int sx = x + ix;
-					if (sx < clipX1 || sx >= clipX2)
-						continue;
-					byte r, g, b;
-					if (isCLUT8) {
-						byte idx = *((const byte *)surface->getBasePtr(ix, iy));
-						if (pictPal.size() > 0 && idx < (int)pictPal.size()) {
-							pictPal.get(idx, r, g, b);
-						} else {
-							// B&W PICT: 0=white, 1=black
-							r = g = b = (idx == 0) ? 255 : 0;
-						}
-					} else {
-						uint32 pixel = surface->getPixel(ix, iy);
-						surface->format.colorToRGB(pixel, r, g, b);
-					}
-					_gfx->setPixel(sx, sy, 0xFF000000 | ((uint32)r << 16) | ((uint32)g << 8) | b);
-				}
-			}
-			_gfx->copyToScreen();
-			delete pictStream;
-			return true;
-		}
-	} else {
-		warning("drawPict: failed to decode PICT %d", resID);
-	}
-	delete pictStream;
-	return false;
-}
-
-bool ColonyEngine::loadAnimation(const Common::String &name) {
-	_animationName = name;
-	for (int i = 0; i < 6; i++)
-		_animDisplay[i] = 1;
-
-	// Look up per-animation BMColor map (from ganimate.c).
-	_animBMColors.clear();
-	Common::String nameLower = name;
-	nameLower.toLowercase();
-	for (const AnimColorEntry *e = kAnimColors; e->name; e++) {
-		if (nameLower == e->name) {
-			_animBMColors.resize(e->count);
-			for (int i = 0; i < e->count; i++)
-				_animBMColors[i] = e->colors[i];
-			break;
-		}
-	}
-
-	Common::String fileName = name + ".pic";
-	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
-	if (!file) {
-		// Try lowercase for Mac
-		fileName = name;
-		fileName.toLowercase();
-		file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
-		if (!file) {
-			// Try CData directory
-			fileName = "CData/" + fileName;
-			file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
-			if (!file) {
-				warning("Could not open animation file %s", name.c_str());
-				return false;
-			}
-		}
-	}
-
-	deleteAnimation();
-
-	// Read background data
-	file->read(_topBG, 8);
-	file->read(_bottomBG, 8);
-	_divideBG = readSint16(*file);
-	_backgroundActive = readSint16(*file) != 0;
-	if (_backgroundActive) {
-		_backgroundClip = readRect(*file);
-		_backgroundLocate = readRect(*file);
-		_backgroundMask = loadImage(*file);
-		_backgroundFG = loadImage(*file);
-	}
-
-	// Read sprite data
-	int16 maxsprite = readSint16(*file);
-	readSint16(*file); // locSprite
-	for (int i = 0; i < maxsprite; i++) {
-		Sprite *s = new Sprite();
-		s->fg = loadImage(*file);
-		s->mask = loadImage(*file);
-		s->used = readSint16(*file) != 0;
-		s->clip = readRect(*file);
-		s->locate = readRect(*file);
-		_cSprites.push_back(s);
-	}
-
-	// Read complex sprite data
-	int16 maxLSprite = readSint16(*file);
-	readSint16(*file); // anum
-	for (int i = 0; i < maxLSprite; i++) {
-		ComplexSprite *ls = new ComplexSprite();
-		int16 size = readSint16(*file);
-		for (int j = 0; j < size; j++) {
-			ComplexSprite::SubObject sub;
-			sub.spritenum = readSint16(*file);
-			sub.xloc = readSint16(*file);
-			sub.yloc = readSint16(*file);
-			ls->objects.push_back(sub);
-		}
-		ls->bounds = readRect(*file);
-		ls->visible = readSint16(*file) != 0;
-		ls->current = readSint16(*file);
-		ls->xloc = readSint16(*file);
-		ls->yloc = readSint16(*file);
-		ls->acurrent = readSint16(*file);
-		ls->axloc = readSint16(*file);
-		ls->ayloc = readSint16(*file);
-		ls->type = file->readByte();
-		ls->frozen = file->readByte();
-		ls->locked = file->readByte();
-		ls->link = readSint16(*file);
-		ls->key = readSint16(*file);
-		ls->lock = readSint16(*file);
-		ls->onoff = true;
-		_lSprites.push_back(ls);
-	}
-
-	delete file;
-	return true;
-}
-
-void ColonyEngine::deleteAnimation() {
-	delete _backgroundMask;
-	_backgroundMask = nullptr;
-	delete _backgroundFG;
-	_backgroundFG = nullptr;
-	for (uint i = 0; i < _cSprites.size(); i++)
-		delete _cSprites[i];
-	_cSprites.clear();
-	for (uint i = 0; i < _lSprites.size(); i++)
-		delete _lSprites[i];
-	_lSprites.clear();
-}
-
-void ColonyEngine::playAnimation() {
-	// Clear movement flags so held keys don't re-trigger on return
-	_moveForward = false;
-	_moveBackward = false;
-	_strafeLeft = false;
-	_strafeRight = false;
-	_rotateLeft = false;
-	_rotateRight = false;
-
-	_animationRunning = true;
-	_system->lockMouse(false);
-	_system->showMouse(true);
-	_system->warpMouse(_centerX, _centerY);
-	CursorMan.setDefaultArrowCursor(true);
-	CursorMan.showMouse(true);
-	_system->updateScreen();
-
-	if (_animationName == "security" && !_unlocked) {
-		for (int i = 0; i < 4; i++) {
-			_decode1[i] = (uint8)(2 + _randomSource.getRandomNumber(3));
-			setObjectState(27 + i, _decode1[i]);
-		}
-	} else if (_animationName == "reactor") {
-		for (int i = 0; i < 6; i++) {
-			setObjectOnOff(14 + i * 2, false);
-			setObjectState(13 + i * 2, 1);
-		}
-	} else if (_animationName == "controls") {
-		switch (_corePower[_coreIndex]) {
-		case 0: setObjectState(2, 1); setObjectState(5, 1); break;
-		case 1: setObjectState(2, 1); setObjectState(5, 2); break;
-		case 2: setObjectState(2, 2); setObjectState(5, 1); break;
-		}
-	} else if (_animationName == "desk") {
-		if (!(_action0 == 11 || _action0 == 18)) {
-			for (int i = 1; i <= 5; i++)
-				setObjectOnOff(i, false);
-		} else {
-			uint8 *decode = (_action0 == 11) ? _decode2 : _decode3;
-			for (int i = 0; i < 4; i++) {
-				if (decode[i])
-					setObjectState(i + 2, decode[i]);
-				else
-					setObjectState(i + 2, 1);
-			}
-		}
-
-		if (_action0 != 10) {
-			setObjectOnOff(23, false);
-			setObjectOnOff(24, false);
-		}
-		if (_action0 != 30)
-			setObjectOnOff(6, false); // Teeth
-		if (_action0 != 33) { // Jack-in-the-box
-			for (int i = 18; i <= 21; i++)
-				setObjectOnOff(i, false);
-		}
-
-		int ntype = _action1 / 10;
-		switch (ntype) {
-		case 0:
-		case 1:
-		case 2:
-		case 3:
-			setObjectOnOff(7, false);
-			setObjectOnOff(8, false);
-			setObjectOnOff(9, false);
-			setObjectOnOff(22, false);
-			setObjectOnOff(25, false);
-			break;
-		case 4: // letters
-			setObjectOnOff(22, false);
-			setObjectOnOff(9, false);
-			setObjectOnOff(25, false);
-			break;
-		case 5: // book
-			setObjectOnOff(7, false);
-			setObjectOnOff(8, false);
-			setObjectOnOff(9, false);
-			setObjectOnOff(25, false);
-			break;
-		case 6: // clipboard
-			setObjectOnOff(22, false);
-			setObjectOnOff(7, false);
-			setObjectOnOff(8, false);
-			setObjectOnOff(25, false);
-			break;
-		case 7: // postit
-			setObjectOnOff(22, false);
-			setObjectOnOff(7, false);
-			setObjectOnOff(8, false);
-			setObjectOnOff(9, false);
-			break;
-		}
-	} else if (_animationName == "vanity") {
-		debug(0, "Vanity init: action0=%d action1=%d level=%d weapons=%d armor=%d", _action0, _action1, _level, _weapons, _armor);
-		for (int i = 0; i < (int)_lSprites.size(); i++) {
-			ComplexSprite *ls = _lSprites[i];
-			debug(0, "  Vanity sprite %d: type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d frames=%d",
-				i + 1, ls->type, ls->frozen, ls->locked, ls->current, (int)ls->onoff, ls->key, ls->lock, (int)ls->objects.size());
-		}
-		// DOS DoVanity: set suit state on mirror display (object 1)
-		if (_weapons && _armor)
-			setObjectState(1, 3);
-		else if (_weapons)
-			setObjectState(1, 2);
-		else if (_armor)
-			setObjectState(1, 1);
-		else
-			setObjectState(1, 4);
-		// Badge only visible on level 1
-		if (_level != 1)
-			setObjectOnOff(14, false);
-		// Hide items based on action0 (num parameter in DOS)
-		if (_action0 < 90) { // coffee cup only
-			setObjectOnOff(4, false);
-			setObjectOnOff(7, false);
-			setObjectOnOff(13, false);
-		} else if (_action0 < 100) { // paper
-			setObjectOnOff(12, false);
-			setObjectOnOff(4, false);
-			setObjectOnOff(7, false);
-		} else if (_action0 < 110) { // diary
-			setObjectOnOff(12, false);
-			setObjectOnOff(13, false);
-			setObjectOnOff(7, false);
-		} else if (_action0 < 120) { // book
-			setObjectOnOff(12, false);
-			setObjectOnOff(13, false);
-			setObjectOnOff(4, false);
-		}
-	}
-
-	while (_animationRunning && !shouldQuit()) {
-		updateAnimation();
-		drawAnimation();
-		_gfx->copyToScreen();
-
-		Common::Event event;
-		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONDOWN) {
-				int item = whichSprite(event.mouse);
-				if (item > 0) {
-					handleAnimationClick(item);
-				}
-			} else if (event.type == Common::EVENT_RBUTTONDOWN) {
-				// DOS: right-click exits animation (AnimControl returns FALSE on button-up)
-				_animationRunning = false;
-			} else if (event.type == Common::EVENT_MOUSEMOVE) {
-				debug(5, "Animation Mouse: %d, %d", event.mouse.x, event.mouse.y);
-			} else if (event.type == Common::EVENT_KEYDOWN) {
-				int item = 0;
-				if (event.kbd.keycode >= Common::KEYCODE_0 && event.kbd.keycode <= Common::KEYCODE_9) {
-					item = 1 + (event.kbd.keycode - Common::KEYCODE_0);
-				} else if (event.kbd.keycode >= Common::KEYCODE_KP0 && event.kbd.keycode <= Common::KEYCODE_KP9) {
-					item = 1 + (event.kbd.keycode - Common::KEYCODE_KP0);
-				} else if (event.kbd.keycode == Common::KEYCODE_RETURN || event.kbd.keycode == Common::KEYCODE_KP_ENTER) {
-					item = 12; // Enter
-				} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE || event.kbd.keycode == Common::KEYCODE_DELETE) {
-					item = 11; // Clear
-				} else if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					_animationRunning = false;
-				}
-
-				if (item > 0) {
-					handleAnimationClick(item);
-				}
-			}
-		}
-		_system->delayMillis(20);
-	}
-
-	_system->lockMouse(true);
-	_system->showMouse(false);
-	CursorMan.showMouse(false);
-	CursorMan.popAllCursors();
-	deleteAnimation();
-}
-
-void ColonyEngine::updateAnimation() {
-	uint32 now = _system->getMillis();
-	if (now - _lastAnimUpdate < 50) // Reduced to 50ms (20 fps) to make it "move"
-		return;
-	_lastAnimUpdate = now;
-
-	for (uint i = 0; i < _lSprites.size(); i++) {
-		ComplexSprite *ls = _lSprites[i];
-		// type 0 are displays that auto-animate
-		// Original NoShowIt ONLY checked !ls->locked
-		if (ls->onoff && ls->type == 0 && !ls->locked && ls->objects.size() > 1) {
-			ls->current++;
-			if (ls->current >= (int)ls->objects.size())
-				ls->current = 0;
-		}
-	}
-}
-
-// Resolve a BMColor entry to an ARGB color.
-// bmEntry > 0: cColor index → use _macColors[idx].bg
-// bmEntry < 0: negated Mac system color constant
-// bmEntry == 0: level-based character color (depends on corepower)
-uint32 ColonyEngine::resolveAnimColor(int16 bmEntry) const {
-	if (bmEntry < 0) {
-		return macSysColorToARGB(-bmEntry);
-	} else if (bmEntry > 0) {
-		if (bmEntry < 145)
-			return packMacColorBG(_macColors[bmEntry].bg);
-		return 0xFFFFFFFF;
-	} else {
-		// Zero = level-based (original gamesprt.c DrawlSprite/DrawBackGround):
-		//   if(corepower[coreindex]) RGBBackColor(&cColor[c_char0+level-1].f);
-		//   else RGBBackColor(&cColor[c_dwall].b);
-		if (_corePower[_coreIndex] > 0 && _level >= 1 && _level <= 7)
-			return packMacColorBG(_macColors[mc_char0 + _level - 1].fg);
-		return packMacColorBG(_macColors[mc_dwall].bg);
-	}
-}
-
-void ColonyEngine::drawAnimation() {
-	_gfx->clear(0);
-
-	// Center 416x264 animation area on screen (from original InitDejaVu)
-	int ox = _screenR.left + (_screenR.width() - 416) / 2;
-	ox = (ox / 8) * 8;
-	int oy = _screenR.top + (_screenR.height() - 264) / 2;
-
-	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh
-	                       && !_animBMColors.empty());
-
-	// Fill background patterns (416x264 area).
-	// Color mode: QuickDraw pattern bit 1 → ForeColor (black), bit 0 → BackColor.
-	// Original DrawBackGround():
-	//   Top: BMColor[0]<0 → system color; ==0 → powered:c_char0+level-1.f, else:c_dwall.b
-	//   Bottom: powered → c_lwall.f; unpowered → inherits top BackColor
-	// B&W/DOS: preserve existing palette-index behavior (bit 1 → 15, bit 0 → 0).
-	if (useColor) {
-		const bool powered = (_corePower[_coreIndex] > 0);
-		uint32 topBG = resolveAnimColor(_animBMColors[0]);
-		// Bottom: only uses c_lwall.f when powered; unpowered inherits top color
-		uint32 botBG = powered ? packMacColorBG(_macColors[mc_lwall].fg) : topBG;
-		for (int y = 0; y < 264; y++) {
-			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
-			byte row = pat[y % 8];
-			uint32 bg = (y < _divideBG) ? topBG : botBG;
-			for (int x = 0; x < 416; x++) {
-				bool set = (row & (0x80 >> (x % 8))) != 0;
-				_gfx->setPixel(ox + x, oy + y, set ? (uint32)0xFF000000 : bg);
-			}
-		}
-	} else {
-		for (int y = 0; y < 264; y++) {
-			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
-			byte row = pat[y % 8];
-			for (int x = 0; x < 416; x++) {
-				bool set = (row & (0x80 >> (x % 8))) != 0;
-				_gfx->setPixel(ox + x, oy + y, set ? 15 : 0);
-			}
-		}
-	}
-
-	// Draw background image if active.
-	// Original: BMColor[1] only applied when corepower[coreindex] > 0.
-	if (_backgroundActive && _backgroundFG) {
-		uint32 bgFill = 0xFFFFFFFF; // B&W default
-		if (useColor && _animBMColors.size() > 1) {
-			if (_corePower[_coreIndex] > 0)
-				bgFill = resolveAnimColor(_animBMColors[1]);
-			else
-				bgFill = resolveAnimColor(_animBMColors[0]); // unpowered: inherits top
-		}
-		drawAnimationImage(_backgroundFG, _backgroundMask,
-		                   ox + _backgroundLocate.left, oy + _backgroundLocate.top,
-		                   bgFill);
-	}
-
-	// Draw complex sprites
-	for (uint i = 0; i < _lSprites.size(); i++) {
-		if (_lSprites[i]->onoff)
-			drawComplexSprite(i, ox, oy);
-	}
-}
-
-void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
-	ComplexSprite *ls = _lSprites[index];
-	if (!ls->onoff)
-		return;
-
-	int cnum = ls->current;
-	if (cnum < 0 || cnum >= (int)ls->objects.size())
-		return;
-
-	int spriteIdx = ls->objects[cnum].spritenum;
-	if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size())
-		return;
-
-	Sprite *s = _cSprites[spriteIdx];
-	int x = ox + ls->xloc + ls->objects[cnum].xloc + s->clip.left;
-	int y = oy + ls->yloc + ls->objects[cnum].yloc + s->clip.top;
-
-	// Resolve fill color from BMColor[index+2] (ganimate.c DrawlSprite).
-	uint32 fillColor = 0xFFFFFFFF; // B&W default: white
-	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh
-	                       && !_animBMColors.empty());
-	if (useColor) {
-		int bmIdx = index + 2;
-		if (bmIdx < (int)_animBMColors.size())
-			fillColor = resolveAnimColor(_animBMColors[bmIdx]);
-		else
-			fillColor = resolveAnimColor(0); // fallback to level-based
-	}
-
-	drawAnimationImage(s->fg, s->mask, x, y, fillColor);
-}
-
-void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y, uint32 fillColor) {
-	if (!img || !img->data)
-		return;
-
-	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh);
-	// Mac QuickDraw srcBic+srcOr rendering:
-	//   mask bit=1 → opaque (part of sprite)
-	//   fg bit=1   → ForeColor (black outline)
-	//   fg bit=0   → BackColor (fillColor from BMColor)
-	// B&W/DOS fallback preserves existing palette-index behavior.
-	const uint32 fgColor = useColor ? (uint32)0xFF000000 : 15;
-	const uint32 bgColor = useColor ? fillColor : 0;
-
-	for (int iy = 0; iy < img->height; iy++) {
-		for (int ix = 0; ix < img->width; ix++) {
-			int byteIdx = iy * img->rowBytes + (ix / 8);
-			int bitIdx = 7 - (ix % 8);
-
-			bool maskSet = true;
-			if (mask && mask->data) {
-				int mByteIdx = iy * mask->rowBytes + (ix / 8);
-				int mBitIdx = 7 - (ix % 8);
-				maskSet = (mask->data[mByteIdx] & (1 << mBitIdx)) != 0;
-			}
-
-			if (!maskSet)
-				continue;
-
-			bool fgSet = (img->data[byteIdx] & (1 << bitIdx)) != 0;
-			uint32 color = fgSet ? fgColor : bgColor;
-
-			_gfx->setPixel(x + ix, y + iy, color);
-		}
-	}
-}
-
-Image *ColonyEngine::loadImage(Common::SeekableReadStream &file) {
-	Image *im = new Image();
-	if (getPlatform() == Common::kPlatformMacintosh) {
-		readUint32(file); // baseAddr placeholder
-		im->rowBytes = readSint16(file);
-		Common::Rect r = readRect(file);
-		im->width = r.width();
-		im->height = r.height();
-		im->align = 0;
-		im->bits = 1;
-		im->planes = 1;
-	} else {
-		im->width = readSint16(file);
-		im->height = readSint16(file);
-		im->align = readSint16(file);
-		im->rowBytes = readSint16(file);
-		im->bits = file.readByte();
-		im->planes = file.readByte();
-	}
-
-	int16 tf = readSint16(file);
-	uint32 size;
-	if (tf) {
-		// Mac original loadbitmap: reads bsize bytes into a buffer, then
-		// decompresses from that buffer. We must read exactly bsize bytes
-		// from the stream to keep file position aligned.
-		uint32 bsize = readUint32(file);
-		size = readUint32(file);
-		im->data = (byte *)malloc(size);
-		byte *packed = (byte *)calloc(bsize + 8, 1); // +8 matches original NewPtr(bsize+8)
-		file.read(packed, bsize);
-		// Decompress: exact match of Mac UnPackBytes(src, dst, len).
-		// Buffer is pairs of (count, value). Count is decremented in-place;
-		// when it reaches 0, advance to next pair.
-		byte *sp = packed;
-		for (uint32 di = 0; di < size; di++) {
-			if (*sp) {
-				im->data[di] = *(sp + 1);
-				(*sp)--;
-			} else {
-				sp += 2;
-				im->data[di] = *(sp + 1);
-				(*sp)--;
-			}
-		}
-		free(packed);
-	} else {
-		size = readUint32(file);
-		im->data = (byte *)malloc(size);
-		file.read(im->data, size);
-	}
-	return im;
-}
-
-void ColonyEngine::unpackBytes(Common::SeekableReadStream &file, byte *dst, uint32 len) {
-	uint32 i = 0;
-	while (i < len) {
-		byte count = file.readByte();
-		byte value = file.readByte();
-		for (int j = 0; j < count && i < len; j++) {
-			dst[i++] = value;
-		}
-	}
-}
-
-Common::Rect ColonyEngine::readRect(Common::SeekableReadStream &file) {
-	int16 top, left, bottom, right;
-	if (getPlatform() == Common::kPlatformMacintosh) {
-		top = readSint16(file);
-		left = readSint16(file);
-		bottom = readSint16(file);
-		right = readSint16(file);
-	} else {
-		left = readSint16(file);
-		top = readSint16(file);
-		right = readSint16(file);
-		bottom = readSint16(file);
-	}
-	// Guard against invalid rects from animation data
-	if (left > right || top > bottom)
-		return Common::Rect();
-	return Common::Rect(left, top, right, bottom);
-}
-
-int16 ColonyEngine::readSint16(Common::SeekableReadStream &s) {
-	if (getPlatform() == Common::kPlatformMacintosh)
-		return s.readSint16BE();
-	return s.readSint16LE();
-}
-
-uint16 ColonyEngine::readUint16(Common::SeekableReadStream &s) {
-	if (getPlatform() == Common::kPlatformMacintosh)
-		return s.readUint16BE();
-	return s.readUint16LE();
-}
-
-uint32 ColonyEngine::readUint32(Common::SeekableReadStream &s) {
-	if (getPlatform() == Common::kPlatformMacintosh)
-		return s.readUint32BE();
-	return s.readUint32LE();
-}
-
-int ColonyEngine::whichSprite(const Common::Point &p) {
-	int ox = _screenR.left + (_screenR.width() - 416) / 2;
-	ox = (ox / 8) * 8;
-	int oy = _screenR.top + (_screenR.height() - 264) / 2;
-	Common::Point pt(p.x - ox, p.y - oy);
-
-	debug(1, "Click at (%d, %d), relative (%d, %d)", p.x, p.y, pt.x, pt.y);
-
-	for (int i = _lSprites.size() - 1; i >= 0; i--) {
-		ComplexSprite *ls = _lSprites[i];
-		if (!ls->onoff)
-			continue;
-
-		int cnum = ls->current;
-		if (cnum < 0 || cnum >= (int)ls->objects.size())
-			continue;
-
-		int spriteIdx = ls->objects[cnum].spritenum;
-		if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size())
-			continue;
-
-		Sprite *s = _cSprites[spriteIdx];
-		int xloc = ls->xloc + ls->objects[cnum].xloc;
-		int yloc = ls->yloc + ls->objects[cnum].yloc;
-
-		Common::Rect r = s->clip;
-		r.translate(xloc, yloc);
-
-		if (!r.contains(pt))
-			continue;
-
-		// Pixel-perfect mask test (matches DOS WhichlSprite)
-		Image *mask = s->mask;
-		if (mask && mask->data) {
-			int row = pt.y - r.top;
-			int col = pt.x - r.left;
-			int bitCol = (col + mask->align) * mask->bits;
-			int maskIndex = row * mask->rowBytes + (bitCol / 8);
-			int shift = bitCol % 8;
-
-			if (maskIndex >= 0 && maskIndex < mask->rowBytes * mask->height) {
-				byte maskByte = mask->data[maskIndex];
-				if (mask->planes == 2)
-					maskByte |= mask->data[mask->rowBytes * mask->height + maskIndex];
-				maskByte = maskByte >> shift;
-				if (!(maskByte & ((1 << mask->bits) - 1))) {
-					debug(0, "  Sprite %d (type=%d frz=%d): bbox hit but mask transparent at row=%d col=%d bits=%d align=%d",
-						i + 1, ls->type, ls->frozen, row, col, mask->bits, mask->align);
-					continue; // Transparent pixel, skip this sprite
-				}
-			} else {
-				debug(0, "  Sprite %d: mask index %d out of bounds (max %d)", i + 1, maskIndex, mask->rowBytes * mask->height);
-			}
-		} else {
-			debug(0, "  Sprite %d: no mask data, using bbox", i + 1);
-		}
-
-		debug(0, "Sprite %d HIT. type=%d frozen=%d Frame %d, Sprite %d. Box: (%d,%d,%d,%d)",
-			i + 1, ls->type, ls->frozen, cnum, spriteIdx, r.left, r.top, r.right, r.bottom);
-		return i + 1;
-	}
-
-	// Dump accurately calculated bounds if debug is high enough
-	if (gDebugLevel >= 2) {
-		for (int i = 0; i < (int)_lSprites.size(); i++) {
-			ComplexSprite *ls = _lSprites[i];
-			if (ls->onoff) {
-				int cnum = ls->current;
-				if (cnum < 0 || cnum >= (int)ls->objects.size())
-					continue;
-				int spriteIdx = ls->objects[cnum].spritenum;
-				if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size())
-					continue;
-				Sprite *s = _cSprites[spriteIdx];
-
-				int xloc = ls->xloc + ls->objects[cnum].xloc;
-				int yloc = ls->yloc + ls->objects[cnum].yloc;
-				Common::Rect r = s->clip;
-				r.translate(xloc, yloc);
-
-				debug(2, "  Sprite %d: Frame=%d Box=(%d,%d,%d,%d)", i + 1,
-					cnum, r.left, r.top, r.right, r.bottom);
-			}
-		}
-	}
-
-	return 0;
-}
-
-void ColonyEngine::handleAnimationClick(int item) {
-	uint32 now = _system->getMillis();
-	if (now - _lastClickTime < 250) {
-		debug("Ignoring rapid click on item %d", item);
-		return;
-	}
-	_lastClickTime = now;
-	debug(0, "Animation click on item %d in %s", item, _animationName.c_str());
-
-	if (item > 0) {
-		dolSprite(item - 1);
-	}
-
-	if (_animationName == "desk") {
-		if (item >= 2 && item <= 5) {
-			int idx = item - 2;
-			uint8 *decode = (_level == 1) ? _decode2 : _decode3;
-			if (decode[idx] == 0) {
-				decode[idx] = (uint8)(2 + (_randomSource.getRandomNumber(3)));
-				_lSprites[item - 1]->current = decode[idx] - 1;
-				drawAnimation();
-				_gfx->copyToScreen();
-			}
-		} else if (item == 7) { // Letter
-			if (_lSprites[6]->current > 0)
-				doText(_action1, 0);
-		} else if (item == 9) { // Clipboard
-			doText(_action1, 0);
-		} else if (item == 17) { // Screen
-			doText(_action0, 0);
-		} else if (item == 22) { // Book
-			doText(_action1, 0);
-		} else if (item == 24) { // Cigarette
-			doText(55, 0);
-			terminateGame(false);
-		} else if (item == 25) { // Post-it
-			doText(_action1, 0);
-		}
-	} else if (_animationName == "vanity") {
-		debug(0, "Vanity item %d clicked. Sprite type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d size=%d",
-			item,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->type : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->frozen : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->locked : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->current : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? (int)_lSprites[item-1]->onoff : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->key : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->lock : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? (int)_lSprites[item-1]->objects.size() : -1);
-		if (item == 12) { // Coffee cup - spill animation
-			if (!_doorOpen) { // reuse _doorOpen as "spilled" flag
-				for (int i = 1; i < 6; i++) {
-					setObjectState(12, i);
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(50);
-				}
-				_doorOpen = true;
-			}
-		} else if (item == 13) { // Paper
-			doText(_action0, 0);
-		} else if (item == 14) { // Badge
-			doText(80, 0);
-		} else if (item == 4) { // Diary
-			doText(_action0, 0);
-		} else if (item == 7) { // Book
-			doText(_action0, 0);
-		} else {
-			debug(0, "Vanity: unhandled item %d", item);
-		}
-	} else if (_animationName == "slides") {
-		if (item == 2) { // Speaker
-			doText(261 + _creature, 0);
-		} else if (item == 5) { // Prev
-			_creature--;
-			if (_creature == 0)
-				_creature = 8;
-			setObjectState(1, _creature);
-		} else if (item == 6) { // Next
-			_creature++;
-			if (_creature == 9)
-				_creature = 1;
-			setObjectState(1, _creature);
-		}
-	} else if (_animationName == "teleshow") {
-		if (item == 2) { // Speaker
-			doText(269 + _creature, 0);
-		} else if (item == 5) { // Prev
-			_creature--;
-			if (_creature == 0)
-				_creature = 7;
-			setObjectState(1, _creature);
-		} else if (item == 6) { // Next
-			_creature++;
-			if (_creature == 8)
-				_creature = 1;
-			setObjectState(1, _creature);
-		}
-	} else if (_animationName == "reactor" || _animationName == "security" || _animationName == "suit") {
-		if (item >= 1 && item <= 10 && _animationName != "suit") {
-			for (int i = 5; i >= 1; i--)
-				_animDisplay[i] = _animDisplay[i - 1];
-			_animDisplay[0] = (uint8)(item + 1);
-			refreshAnimationDisplay();
-			drawAnimation();
-			_gfx->copyToScreen();
-			// Don't return, let dolSprite animate the button
-		} else if (item == 11 && _animationName != "suit") { // Clear
-			for (int i = 0; i < 6; i++)
-				_animDisplay[i] = 1;
-			// Reset keypad buttons to unpressed state
-			for (int i = 1; i <= 10; i++)
-				setObjectState(i, 1);
-			refreshAnimationDisplay();
-			drawAnimation();
-			_gfx->copyToScreen();
-		} else if (item == 12 && _animationName != "suit") { // Enter
-			uint8 testarray[6];
-			if (_animationName == "reactor") {
-				if (_level == 1)
-					crypt(testarray, _decode2[3] - 2, _decode2[2] - 2, _decode2[1] - 2, _decode2[0] - 2);
-				else
-					crypt(testarray, _decode3[3] - 2, _decode3[2] - 2, _decode3[1] - 2, _decode3[0] - 2);
-
-				bool match = true;
-				for (int i = 0; i < 6; i++) {
-					if (testarray[i] != _animDisplay[5 - i])
-						match = false;
-				}
-				if (match) {
-					if (_coreState[_coreIndex] == 0)
-						_coreState[_coreIndex] = 1;
-					else if (_coreState[_coreIndex] == 1)
-						_coreState[_coreIndex] = 0;
-					_gametest = true;
-				}
-				_animationRunning = false;
-			} else if (_animationName == "security") { // security
-				crypt(testarray, _decode1[0] - 2, _decode1[1] - 2, _decode1[2] - 2, _decode1[3] - 2);
-				bool match = true;
-				for (int i = 0; i < 6; i++) {
-					if (testarray[i] != _animDisplay[5 - i])
-						match = false;
-				}
-				if (match) {
-					_unlocked = true;
-					_gametest = true;
-				}
-				_animationRunning = false;
-			}
-		} else if (_animationName == "suit") {
-			if (item == 1) { // Armor
-				if (_armor == 3) {
-					for (int i = 6; i >= 1; i--) {
-						setObjectState(1, i);
-						setObjectState(3, i / 2 + 1);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(30);
-					}
-					_armor = 0;
-				} else {
-					setObjectState(1, (_armor * 2 + 1) + 1); // intermediate/pressed
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(50);
-					_armor++;
-				}
-				setObjectState(1, _armor * 2 + 1); // target state
-				setObjectState(3, _armor + 1); // display state
-				drawAnimation();
-				_gfx->copyToScreen();
-				if (_armor == 3 && _weapons == 3)
-					_corePower[_coreIndex] = 2;
-			} else if (item == 2) { // Weapons
-				if (_weapons == 3) {
-					for (int i = 6; i >= 1; i--) {
-						setObjectState(2, i);
-						setObjectState(4, i / 2 + 1);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(30);
-					}
-					_weapons = 0;
-				} else {
-					setObjectState(2, (_weapons * 2 + 1) + 1); // intermediate/pressed
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(50);
-					_weapons++;
-				}
-				setObjectState(2, _weapons * 2 + 1);
-				setObjectState(4, _weapons + 1);
-				drawAnimation();
-				_gfx->copyToScreen();
-				if (_armor == 3 && _weapons == 3)
-					_corePower[_coreIndex] = 2;
-			}
-		}
-		if (_animationName == "reactor" || _animationName == "security") {
-			if (item <= 12) {
-				// setObjectState(item, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
-				if (item > 10) // Clear/Enter should return to Off
-					setObjectState(item, 1);
-				drawAnimation();
-				_gfx->copyToScreen();
-			}
-		}
-	} else if (_animationName == "door" || _animationName == "bulkhead") {
-		// DOS DoDoor: item==3 toggles door open/close, item==1 or (item==101 && door open) exits
-		if (item == 3) {
-			_sound->play(Sound::kDoor);
-			if (_doorOpen) {
-				for (int i = 3; i >= 1; i--) {
-					_doorOpen = !_doorOpen;
-					setObjectState(2, i);
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(80);
-				}
-			} else {
-				for (int i = 1; i < 4; i++) {
-					_doorOpen = !_doorOpen;
-					setObjectState(2, i);
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(80);
-				}
-			}
-		}
-		if (item == 1 || (item == 101 && objectState(2) == 3)) {
-			_animationResult = 1;
-			_animationRunning = false;
-		}
-	} else if (_animationName == "airlock") {
-		// DOS DoAirLock: item==1 toggles airlock if power on && unlocked
-		// item==2 or (item==101 && airlock open) exits with pass-through
-		if ((item == 2 || item == 101) && _doorOpen) {
-			_animationResult = 1;
-			_animationRunning = false;
-		} else if (item == 1 && _corePower[_coreIndex] && _unlocked) {
-			_sound->play(Sound::kAirlock);
-			if (_doorOpen) {
-				for (int i = 3; i >= 1; i--) {
-					setObjectState(2, i);
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(80);
-				}
-			} else {
-				for (int i = 1; i < 4; i++) {
-					setObjectState(2, i);
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(80);
-				}
-			}
-			_doorOpen = !_doorOpen;
-		} else if (item == 101 && !_doorOpen) {
-			// Exit without opening
-			_animationRunning = false;
-		}
-	} else if (_animationName == "elev") {
-		// DOS DoElevator: two phases
-		// _doorOpen=false: Phase 1 (outside) - item==5 toggles doors
-		// _doorOpen=true: Phase 2 (inside) - items 6-10 select floor
-		// _animationResult tracks: 0=outside, 1=doors open, 2=inside
-		if (_animationResult < 2) {
-			// Phase 1: outside the elevator
-			if (item == 5) {
-				_sound->play(Sound::kElevator);
-				if (!_doorOpen) {
-					for (int i = 1; i < 4; i++) {
-						setObjectState(3, i);
-						setObjectState(4, i);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(80);
-					}
-					_doorOpen = true;
-				} else {
-					for (int i = 3; i >= 1; i--) {
-						setObjectState(4, i);
-						setObjectState(3, i);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(80);
-					}
-					_doorOpen = false;
-				}
-			} else if (item == 2 || (item == 101 && _doorOpen)) {
-				// Enter the elevator (transition to phase 2)
-				_animationResult = 2;
-				setObjectOnOff(6, true);
-				setObjectOnOff(7, true);
-				setObjectOnOff(8, true);
-				setObjectOnOff(9, true);
-				setObjectOnOff(10, true);
-				setObjectOnOff(2, false);
-				setObjectOnOff(5, false);
-				drawAnimation();
-				_gfx->copyToScreen();
-			} else if (item == 101 && !_doorOpen) {
-				// Exit without entering
-				_animationResult = 0;
-				_animationRunning = false;
-			}
-		} else {
-			// Phase 2: inside  floor selection
-			if (item >= 6 && item <= 10) {
-				int fl = item - 5;
-				if (fl == _elevatorFloor) {
-					setObjectState(item, 1); // already on this floor
-				} else {
-					_sound->play(Sound::kElevator);
-					for (int i = 3; i >= 1; i--) {
-						setObjectState(4, i);
-						setObjectState(3, i);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(80);
-					}
-					_elevatorFloor = fl;
-					for (int i = 1; i <= 3; i++) {
-						setObjectState(4, i);
-						setObjectState(3, i);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(80);
-					}
-					setObjectState(item, 1);
-				}
-			} else if (item == 1 || item == 101) {
-				// Exit elevator
-				_animationRunning = false;
-			}
-		}
-	} else if (_animationName == "controls") {
-		switch (item) {
-		case 4: // Accelerator
-			if (_corePower[_coreIndex] >= 2 && _coreState[_coreIndex] == 0 && !_orbit) {
-				_orbit = 1;
-				debug(0, "Taking off!");
-				// Animate the lever moving full range
-				for (int i = 1; i <= 6; i++) {
-					setObjectState(4, i);
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(30);
-				}
-				_animationRunning = false;
-				return; // Exit animation immediately on success
-			} else {
-				debug(0, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
-				// Fail animation click
-				setObjectState(4, 1);
-				// dolSprite(3); // Animate lever moving and returning - handled by top dolSprite
-				for (int i = 6; i > 0; i--) {
-					setObjectState(4, i);
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(20);
-				}
-			}
-			break;
-		case 5: // Emergency power
-			// setObjectState(5, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
-			// dolSprite(4); // Animate the button press - handled by top dolSprite
-			if (_coreState[_coreIndex] < 2) {
-				if (_corePower[_coreIndex] == 0)
-					_corePower[_coreIndex] = 1;
-				else if (_corePower[_coreIndex] == 1)
-					_corePower[_coreIndex] = 0;
-			}
-			// Finalize visual state according to power settings
-			switch (_corePower[_coreIndex]) {
-			case 0: setObjectState(2, 1); setObjectState(5, 1); break;
-			case 1: setObjectState(2, 1); setObjectState(5, 2); break;
-			case 2: setObjectState(2, 2); setObjectState(5, 1); break;
-			}
-			drawAnimation();
-			_gfx->copyToScreen();
-			break;
-		case 7: // Damage report
-		{
-			// dolSprite(6); // Button animation - handled by top dolSprite
-			if (_corePower[_coreIndex] < 2) {
-				doText(15, 0); // Critical status
-			} else if (!_orbit) {
-				doText(49, 0); // Ready for liftoff
-			} else {
-				doText(66, 0); // Orbital stabilization
-			}
-			
-			setObjectState(7, 1); // Reset button
-			drawAnimation();
-			_gfx->copyToScreen();
-			break;
-		}
-			break;
-		}
-	}
-}
-
-void ColonyEngine::terminateGame(bool blowup) {
-	debug(0, "YOU HAVE BEEN TERMINATED! (blowup=%d)", blowup);
-	if (blowup)
-		_sound->play(Sound::kExplode);
-	
-	const char *msg[] = {
-		"   YOU HAVE BEEN TERMINATED!   ",
-		" Type 'q' to quit the game.    ",
-		nullptr
-	};
-	printMessage(msg, true);
-	
-	_system->quit();
-}
-
-void ColonyEngine::moveObject(int index) {
-	if (index < 0 || index >= (int)_lSprites.size())
-		return;
-
-	ComplexSprite *ls = _lSprites[index];
-
-	// Build link group
-	Common::Array<int> linked;
-	if (ls->link) {
-		for (int i = 0; i < (int)_lSprites.size(); i++)
-			if (_lSprites[i]->link == ls->link)
-				linked.push_back(i);
-	} else {
-		linked.push_back(index);
-	}
-
-	// Get initial mouse position and animation origin
-	Common::Point old = _system->getEventManager()->getMousePos();
-	int ox = _screenR.left + (_screenR.width() - 416) / 2;
-	ox = (ox / 8) * 8;
-	int oy = _screenR.top + (_screenR.height() - 264) / 2;
-
-	// Drag loop: track mouse while left button held.
-	// NOTE: The original DOS hides dragged sprites during drag (setObjectOnOff FALSE)
-	// and redraws them separately on top. We improve on this by keeping them visible
-	// throughout, and drawing an extra copy on top so they render above drawers.
-	while (!shouldQuit()) {
-		Common::Event event;
-		bool buttonDown = true;
-		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONUP) {
-				buttonDown = false;
-				break;
-			}
-		}
-		if (!buttonDown)
-			break;
-
-		Common::Point cur = _system->getEventManager()->getMousePos();
-		int dx = cur.x - old.x;
-		int dy = cur.y - old.y;
-
-		if (dx != 0 || dy != 0) {
-			// Cycle frame for non-type-2 sprites
-			if (ls->type != 2 && (int)ls->objects.size() > 1) {
-				ls->current++;
-				if (ls->current >= (int)ls->objects.size())
-					ls->current = 0;
-			}
-
-			// Move all linked sprites
-			for (uint i = 0; i < linked.size(); i++) {
-				_lSprites[linked[i]]->xloc += dx;
-				_lSprites[linked[i]]->yloc += dy;
-			}
-
-			old = cur;
-		}
-
-		// Draw all sprites normally, then draw dragged sprites again on top
-		// so they appear above drawers and other overlapping sprites
-		drawAnimation();
-		for (uint i = 0; i < linked.size(); i++)
-			drawComplexSprite(linked[i], ox, oy);
-		_gfx->copyToScreen();
-
-		_system->delayMillis(20);
-	}
-
-	// Reset frame for non-type-2
-	if (ls->type != 2)
-		ls->current = 0;
-
-	drawAnimation();
-	_gfx->copyToScreen();
-}
-
-void ColonyEngine::dolSprite(int index) {
-	if (index < 0 || index >= (int)_lSprites.size())
-		return;
-
-	ComplexSprite *ls = _lSprites[index];
-	int maxFrames = (int)ls->objects.size();
-
-	switch (ls->type) {
-	case 0: // Display
-		if (!ls->frozen)
-			moveObject(index);
-		break;
-	case 1: // Key and control
-		if (ls->frozen) {
-			// Container: can open or close if not locked, or if slightly open
-			if (!ls->locked || ls->current) {
-				if (ls->current > 1) {
-					// Close: animate from current down to 0
-					while (ls->current > 0) {
-						ls->current--;
-						// Sync linked sprites via key/lock
-						if (ls->key) {
-							for (int i = 0; i < (int)_lSprites.size(); i++) {
-								if (i != index && _lSprites[i]->lock == ls->key) {
-									_lSprites[i]->current = ls->current;
-									if (_lSprites[i]->current >= (int)_lSprites[i]->objects.size())
-										_lSprites[i]->current = (int)_lSprites[i]->objects.size() - 1;
-								}
-							}
-						}
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(50);
-					}
-				} else {
-					// Open: animate from current up to max
-					while (ls->current < maxFrames - 1) {
-						ls->current++;
-						// Sync linked sprites via key/lock
-						if (ls->key) {
-							for (int i = 0; i < (int)_lSprites.size(); i++) {
-								if (i != index && _lSprites[i]->lock == ls->key) {
-									_lSprites[i]->current = ls->current;
-									if (_lSprites[i]->current >= (int)_lSprites[i]->objects.size())
-										_lSprites[i]->current = (int)_lSprites[i]->objects.size() - 1;
-								}
-							}
-						}
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(50);
-					}
-				}
-			}
-		} else {
-			moveObject(index);
-		}
-		break;
-	case 2: // Container and object
-		if (ls->frozen) {
-			if (!ls->locked) {
-				// Unlocked container: toggle open/close
-				if (ls->current > 0) {
-					while (ls->current > 0) {
-						ls->current--;
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(50);
-					}
-				} else {
-					while (ls->current < maxFrames - 1) {
-						ls->current++;
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(50);
-					}
-				}
-			} else {
-				// Locked container: current>1 closes, current==1 opens further
-				if (ls->current > 1) {
-					while (ls->current > 0) {
-						ls->current--;
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(50);
-					}
-				} else if (ls->current == 1) {
-					while (ls->current < maxFrames - 1) {
-						ls->current++;
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(50);
-					}
-				}
-			}
-		} else {
-			moveObject(index);
-		}
-		break;
-	default:
-		break;
-	}
-}
-
-void ColonyEngine::setObjectState(int num, int state) {
-	num--;
-	if (num >= 0 && num < (int)_lSprites.size())
-		_lSprites[num]->current = state - 1;
-}
-
-int ColonyEngine::objectState(int num) const {
-	num--;
-	if (num >= 0 && num < (int)_lSprites.size())
-		return _lSprites[num]->current + 1;
-	return 0;
-}
-
-void ColonyEngine::setObjectOnOff(int num, bool on) {
-	num--;
-	if (num >= 0 && num < (int)_lSprites.size())
-		_lSprites[num]->onoff = on;
-}
-
-void ColonyEngine::refreshAnimationDisplay() {
-	for (int i = 0; i < 6; i++) {
-		if (_animDisplay[i] < 9) {
-			setObjectOnOff(13 + i * 2, true);
-			setObjectOnOff(14 + i * 2, false);
-			setObjectState(13 + i * 2, _animDisplay[i]);
-		} else {
-			setObjectOnOff(14 + i * 2, true);
-			setObjectOnOff(13 + i * 2, false);
-			setObjectState(14 + i * 2, _animDisplay[i] - 8);
-		}
-	}
-}
-
-void ColonyEngine::crypt(uint8 sarray[6], int i, int j, int k, int l) {
-	int res[6];
-	res[0] = ((3 * l) ^ i ^ j ^ k) % 10;
-	res[1] = ((i * 3) ^ (j * 7) ^ (k * 11) ^ (l * 13)) % 10;
-	res[2] = (3 + (l * 17) ^ (j * 19) ^ (k * 23) ^ (i * 29)) % 10;
-	res[3] = ((l * 19) ^ (j * 23) ^ (k * 29) ^ (i * 31)) % 10;
-	res[4] = ((l * 17) | (j * 19) | (k * 23) | (i * 29)) % 10;
-	res[5] = (29 + (l * 17) - (j * 19) - (k * 23) - (i * 29)) % 10;
-	for (int m = 0; m < 6; m++) {
-		if (res[m] < 0)
-			res[m] = -res[m];
-		sarray[m] = (uint8)(res[m] + 2);
-	}
-}
-
 } // End of namespace Colony
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
new file mode 100644
index 00000000000..bdbd6eaba7d
--- /dev/null
+++ b/engines/colony/interaction.cpp
@@ -0,0 +1,277 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "colony/gfx.h"
+#include "common/system.h"
+#include "common/debug.h"
+
+namespace Colony {
+
+void ColonyEngine::interactWithObject(int objNum) {
+	if (objNum <= 0 || objNum > (int)_objects.size())
+		return;
+
+	const Thing &obj = _objects[objNum - 1];
+	if (!obj.alive)
+		return;
+
+	const int x = CLIP<int>(obj.where.xindex, 0, 30);
+	const int y = CLIP<int>(obj.where.yindex, 0, 30);
+	_action0 = _mapData[x][y][4][3];
+	_action1 = _mapData[x][y][4][4];
+	_creature = 1;
+
+	switch (obj.type) {
+	case kObjDesk:
+		if (loadAnimation("desk"))
+			playAnimation();
+		break;
+	case kObjConsole:
+		switch (_action0) {
+		case 1: // Reactor console
+			if (loadAnimation("reactor"))
+				playAnimation();
+			break;
+		case 2: // Main ship controls
+			if (loadAnimation("controls"))
+				playAnimation();
+			break;
+		case 3: // Security console
+			if (loadAnimation("security"))
+				playAnimation();
+			break;
+		default:
+			inform("IT DOES NOT SEEM TO BE WORKING.", true);
+			break;
+		}
+		break;
+	case kObjProjector:
+		switch (_action0) {
+		case 1:
+			if (loadAnimation("slides"))
+				playAnimation();
+			break;
+		case 2:
+			if (loadAnimation("teleshow")) // "teleshow" matches original interaction
+				playAnimation();
+			break;
+		default:
+			inform("PROJECTOR OFFLINE", true);
+			break;
+		}
+		break;
+	case kObjPowerSuit:
+		if (loadAnimation("suit"))
+			playAnimation();
+		break;
+	case kObjTeleport:
+	{
+		if (_fl == 1) {
+			// In empty forklift  pick up the teleporter itself
+			if (loadAnimation("lift")) {
+				_animationResult = 0;
+				playAnimation();
+				if (_animationResult) {
+					PassPatch from;
+					from.level = _level;
+					from.xindex = obj.where.xindex;
+					from.yindex = obj.where.yindex;
+					from.xloc = obj.where.xloc;
+					from.yloc = obj.where.yloc;
+					from.ang = obj.where.ang;
+					_carryPatch[1].level = 100;
+					_carryPatch[1].xindex = 1;
+					_carryPatch[1].yindex = 1;
+					_carryPatch[1].xloc = 1;
+					_carryPatch[1].yloc = 1;
+					_carryPatch[1].ang = 1;
+					newPatch(kObjTeleport, from, _carryPatch[1], _mapData[from.xindex][from.yindex][4]);
+					_carryType = kObjTeleport;
+					_robotArray[obj.where.xindex][obj.where.yindex] = 0;
+					_objects[objNum - 1].alive = 0;
+					_fl = 2;
+				}
+			}
+			break;
+		}
+		// fl==0 or fl==2: use the teleporter
+		_sound->play(Sound::kTeleport);
+		int targetLevelRaw = _mapData[x][y][4][2];
+		int targetX = _action0;
+		int targetY = _action1;
+		// Check if this teleporter was relocated via patch
+		PassPatch tp;
+		tp.level = _level;
+		tp.xindex = x;
+		tp.yindex = y;
+		uint8 patchData[5];
+		if (patchMapTo(tp, patchData)) {
+			targetLevelRaw = patchData[2];
+			targetX = patchData[3];
+			targetY = patchData[4];
+		}
+		const int targetLevel = (targetLevelRaw == 0) ? _level : targetLevelRaw;
+		if (targetLevel >= 100 || targetX <= 0 || targetX >= 31 || targetY <= 0 || targetY >= 31) {
+			inform("TELEPORTER INITIALIZATION FAILED", true);
+			break;
+		}
+		if (targetLevel != _level)
+			loadMap(targetLevel);
+		const int oldX = _me.xindex;
+		const int oldY = _me.yindex;
+		_me.xindex = targetX;
+		_me.yindex = targetY;
+		_me.xloc = (targetX << 8) + 128;
+		_me.yloc = (targetY << 8) + 128;
+		_me.ang = _me.look;
+		if (oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32)
+			_robotArray[oldX][oldY] = 0;
+		if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
+			_robotArray[_me.xindex][_me.yindex] = kMeNum;
+		break;
+	}
+	case kObjDrawer:
+		if (loadAnimation("vanity"))
+			playAnimation();
+		break;
+	case kObjScreen:
+		// original game shows "Full of stars" effect/text
+		_sound->play(Sound::kStars1);
+		inform("I CAN SEE THROUGH IT...", true);
+		break;
+
+	case kObjToilet:
+	case kObjPToilet:
+		_sound->play(Sound::kToilet);
+		inform("IT'S A TOILET.", true);
+		break;
+	case kObjTub:
+		_sound->play(Sound::kBath);
+		inform("A BATHTUB. NO TIME FOR A SOAK.", true);
+		break;
+
+	case kObjSink:
+		_sound->play(Sound::kSink);
+		inform("A SINK. IT'S DRY.", true);
+		break;
+	case kObjTV:
+		if (_level == 1)
+			doText(56, 0);
+		else
+			doText(16, 0);
+		break;
+	case kObjForkLift:
+		if (_fl == 0) {
+			// Enter forklift: play animation, if confirmed, patch it away
+			if (loadAnimation("forklift")) {
+				playAnimation();
+				if (_animationResult) {
+					PassPatch from;
+					from.level = _level;
+					from.xindex = obj.where.xindex;
+					from.yindex = obj.where.yindex;
+					from.xloc = obj.where.xloc;
+					from.yloc = obj.where.yloc;
+					from.ang = obj.where.ang;
+					_carryPatch[0].level = 100;
+					_carryPatch[0].xindex = 0;
+					_carryPatch[0].yindex = 0;
+					_carryPatch[0].xloc = 0;
+					_carryPatch[0].yloc = 0;
+					_carryPatch[0].ang = 0;
+					newPatch(kObjForkLift, from, _carryPatch[0], _mapData[from.xindex][from.yindex][4]);
+					_robotArray[obj.where.xindex][obj.where.yindex] = 0;
+					_objects[objNum - 1].alive = 0;
+					_me.look = _me.ang = obj.where.ang;
+					_fl = 1;
+				}
+			}
+		}
+		break;
+	case kObjBox1:
+	case kObjBox2:
+	case kObjCryo:
+		if (_fl == 1) {
+			// In empty forklift  pick up object
+			if (loadAnimation("lift")) {
+				_animationResult = 0;
+				playAnimation();
+				if (_animationResult) {
+					PassPatch from;
+					from.level = _level;
+					from.xindex = obj.where.xindex;
+					from.yindex = obj.where.yindex;
+					from.xloc = obj.where.xloc;
+					from.yloc = obj.where.yloc;
+					from.ang = obj.where.ang;
+					_carryPatch[1].level = 100;
+					_carryPatch[1].xindex = 1;
+					_carryPatch[1].yindex = 1;
+					_carryPatch[1].xloc = 1;
+					_carryPatch[1].yloc = 1;
+					_carryPatch[1].ang = 1;
+					newPatch(obj.type, from, _carryPatch[1], _mapData[from.xindex][from.yindex][4]);
+					_carryType = obj.type;
+					_robotArray[obj.where.xindex][obj.where.yindex] = 0;
+					_objects[objNum - 1].alive = 0;
+					_fl = 2;
+				}
+			}
+		} else if (_fl == 0 && obj.type == kObjCryo) {
+			// Not in forklift  read cryo text
+			doText(_action0, 0);
+		}
+		break;
+	case kObjReactor:
+		if (_fl == 1 && _coreState[_coreIndex] == 1) {
+			// Empty forklift at open reactor  pick up reactor core
+			if (loadAnimation("lift")) {
+				_animationResult = 0;
+				playAnimation();
+				if (_animationResult) {
+					_carryType = kObjReactor;
+					_corePower[2] = _corePower[_coreIndex];
+					_corePower[_coreIndex] = 0;
+					_coreState[_coreIndex] = 2;
+					_fl = 2;
+				}
+			}
+		} else if (_fl == 2 && _carryType == kObjReactor && _coreState[_coreIndex] == 2) {
+			// Carrying reactor core  drop it into reactor
+			if (loadAnimation("lift")) {
+				_animationResult = 0;
+				playAnimation();
+				if (!_animationResult) {
+					_carryType = 0;
+					_coreState[_coreIndex] = 1;
+					_corePower[_coreIndex] = _corePower[2];
+					_fl = 1;
+				}
+			}
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
new file mode 100644
index 00000000000..112dfea75de
--- /dev/null
+++ b/engines/colony/intro.cpp
@@ -0,0 +1,688 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "colony/gfx.h"
+#include "common/system.h"
+#include "common/events.h"
+#include "common/debug.h"
+#include "graphics/font.h"
+#include "graphics/fonts/dosfont.h"
+#include "graphics/fonts/macfont.h"
+#include "image/pict.h"
+#include <math.h>
+
+namespace Colony {
+
+void ColonyEngine::playIntro() {
+	if (getPlatform() == Common::kPlatformMacintosh) {
+		// Load the Mac "Commando" font (FOND 190, 12pt) from Colony resources.
+		// Original intro.c: TextFont(190); TextSize(12);
+		// FONT resource ID = FOND_ID * 128 + size = 190 * 128 + 12 = 24332
+		// Some builds store it as NFNT instead of FONT.
+		Graphics::MacFONTFont *macFont = nullptr;
+		if (_resMan) {
+			const uint16 fontResID = 24332;
+			Common::SeekableReadStream *fontStream = _resMan->getResource(MKTAG('N', 'F', 'N', 'T'), fontResID);
+			if (!fontStream)
+				fontStream = _resMan->getResource(MKTAG('F', 'O', 'N', 'T'), fontResID);
+			if (fontStream) {
+				macFont = new Graphics::MacFONTFont();
+				if (!macFont->loadFont(*fontStream)) {
+					warning("playIntro: failed to load Commando 12pt font");
+					delete macFont;
+					macFont = nullptr;
+				}
+				delete fontStream;
+			} else {
+				// List available font resources for debugging
+				Common::MacResIDArray nfntIDs = _resMan->getResIDArray(MKTAG('N', 'F', 'N', 'T'));
+				Common::MacResIDArray fontIDs = _resMan->getResIDArray(MKTAG('F', 'O', 'N', 'T'));
+				debug("playIntro: FONT/NFNT %d not found. Available NFNT IDs: %d, FONT IDs: %d",
+				      fontResID, nfntIDs.size(), fontIDs.size());
+				for (uint i = 0; i < nfntIDs.size(); i++)
+					debug("  NFNT %d", nfntIDs[i]);
+				for (uint i = 0; i < fontIDs.size(); i++)
+					debug("  FONT %d", fontIDs[i]);
+			}
+		}
+
+		// Original: intro() in intro.c, lines 40-119
+		// qt flag propagates through sections  only modifier+click sets it
+		bool qt = false;
+
+		// 1. ScrollInfo() - scrolling story text with BeamMe sound
+		qt = scrollInfo(macFont);
+
+		// 2. Wait for BeamMe sound to finish
+		// Original: if(!qt) while(!SoundDone());
+		while (!qt && !shouldQuit() && _sound->isPlaying())
+			_system->delayMillis(10);
+
+		// Original: if(Button()) qt=OptionKey();  check for skip
+		if (!qt)
+			qt = checkSkipRequested();
+
+		if (!qt) {
+			// 3. Logo 1 + PlayMars + makestars
+			// Original: FillRect black; DoPicture; PlayMars(); makestars()
+			_gfx->clear(_gfx->black());
+			if (!drawPict(-32565))  // Color Colony
+				drawPict(-32748);   // B&W Colony
+			_sound->play(Sound::kMars);
+			qt = makeStars(_screenR, 0);
+
+			if (!qt) {
+				// 4. Logo 2 + makestars (inside the same !qt block as original)
+				// Original: FillRect black; DoPicture(-32564); makestars()
+				_gfx->clear(_gfx->black());
+				if (!drawPict(-32564))  // Color Colony
+					drawPict(-32750);   // B&W Colony
+				qt = makeStars(_screenR, 0);
+			}
+
+			if (!qt) {
+				// 5. Empty starfield
+				// Original: FillRect black; makestars()
+				_gfx->clear(_gfx->black());
+				_gfx->copyToScreen();
+				qt = makeStars(_screenR, 0);
+			}
+
+			if (!qt) {
+				// 6. TimeSquare("...BLACK HOLE COLLISION...")
+				qt = timeSquare("...BLACK HOLE COLLISION...", macFont);
+			}
+
+			if (!qt) {
+				// 7. Makeblackhole()
+				_gfx->clear(_gfx->black());
+				_gfx->copyToScreen();
+				qt = makeBlackHole();
+			}
+
+			if (!qt) {
+				// 8. TimeSquare("...FUEL HAS BEEN DEPLETED...")
+				qt = timeSquare("...FUEL HAS BEEN DEPLETED...", macFont);
+			}
+
+			// Original: SetPort(&metaPort); before next TimeSquare (no !qt guard)
+			if (!qt) {
+				// 9. TimeSquare("...PREPARE FOR CRASH LANDING...")
+				qt = timeSquare("...PREPARE FOR CRASH LANDING...", macFont);
+			}
+
+			if (!qt) {
+				// 10. makeplanet() + EndCSound()
+				// Simplified: starfield + delay (makeplanet draws a rotating planet)
+				_gfx->clear(_gfx->black());
+				_gfx->copyToScreen();
+				qt = makeStars(_screenR, 0);
+				_sound->stop(); // EndCSound()
+			}
+		}
+
+		// 11. Final crash  always runs (even if qt)
+		// Original: FillRect black; if(!qt) while(!SoundDone());
+		_gfx->clear(_gfx->black());
+		_gfx->copyToScreen();
+		while (!qt && !shouldQuit() && _sound->isPlaying())
+			_system->delayMillis(10);
+
+		// Original: DoExplodeSound(); while(!SoundDone()) InvertRect(&rScreen); StopSound();
+		_sound->play(Sound::kExplode);
+		while (!shouldQuit() && _sound->isPlaying()) {
+			_gfx->clear(_gfx->white());
+			_gfx->copyToScreen();
+			_system->delayMillis(50);
+			_gfx->clear(_gfx->black());
+			_gfx->copyToScreen();
+			_system->delayMillis(50);
+		}
+		_sound->stop();
+		_gfx->clear(_gfx->black());
+		_gfx->copyToScreen();
+		delete macFont;
+		macFont = nullptr;
+
+		// Restore palette entries modified during intro (128, 160-176, 200-213)
+		// back to grayscale so normal gameplay rendering isn't affected
+		byte restorePal[256 * 3];
+		for (int i = 0; i < 256; i++) {
+			restorePal[i * 3 + 0] = i;
+			restorePal[i * 3 + 1] = i;
+			restorePal[i * 3 + 2] = i;
+		}
+		_gfx->setPalette(restorePal + 128 * 3, 128, 128);
+	} else {
+		scrollInfo();
+	}
+}
+
+bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
+	// Original: ScrollInfo() in intro.c, lines 138-221
+	// Renders story text in blue gradient to offscreen half-width buffer,
+	// scrolls it up from below screen with DoBeammeSound(),
+	// waits for click, then scrolls it off the top.
+	// Mac original: TextFont(190 = Commando); TextSize(12);
+	// Text blue starts at 0xFFFF and fades by -4096 per visible line.
+	const char *story[] = {
+		"",
+		"Mankind has left the",
+		"cradle of earth and",
+		"is beginning to eye",
+		"the galaxy. He has",
+		"begun to colonize",
+		"distant planets but has",
+		"yet to meet any alien",
+		"life forms.",
+		"",      // null separator in original
+		"Until now...",
+		"",      // null separator in original
+		"Click to begin",
+		"the Adventure..."
+	};
+	const int storyLength = ARRAYSIZE(story);
+
+	if (getPlatform() == Common::kPlatformMacintosh)
+		_sound->play(Sound::kBeamMe);
+
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+
+	Graphics::DosFont dosFont;
+	const Graphics::Font *font = macFont ? macFont : (const Graphics::Font *)&dosFont;
+
+	// Original uses 19px line height, centers vertically within height
+	int lineHeight = 19;
+	int totalHeight = lineHeight * storyLength;
+	int ht = (_height - totalHeight) / 2;
+
+	// 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);
+	byte pal[14 * 3]; // storyLength entries
+	memset(pal, 0, sizeof(pal));
+	for (int i = 0; i < storyLength; i++) {
+		int val = 255 - i * 16;
+		if (val < 0)
+			val = 0;
+		pal[i * 3 + 0] = bwMac ? val : 0;  // R
+		pal[i * 3 + 1] = bwMac ? val : 0;  // G
+		pal[i * 3 + 2] = val;               // B
+	}
+	_gfx->setPalette(pal, 200, storyLength);
+
+	// Phase 1: Scroll text up from below screen
+	// Original: scrollRect starts at bottom (stayRect.bottom..stayRect.bottom*2),
+	// moves up by inc=4 each frame until text is visible at its correct position.
+	// We simulate by drawing text with a y-offset that starts at _height and decreases to 0.
+	int inc = 4;
+	bool qt = false;
+
+	for (int scrollOff = _height; scrollOff > 0 && !qt; scrollOff -= inc) {
+		if (checkSkipRequested()) {
+			qt = true;
+			_sound->stop();
+			break;
+		}
+
+		_gfx->clear(_gfx->black());
+		for (int i = 0; i < storyLength; i++) {
+			int drawY = ht + lineHeight * i + scrollOff;
+			if (strlen(story[i]) > 0 && drawY >= 0 && drawY < _height)
+				_gfx->drawString(font, story[i], _width / 2, drawY, 200 + i, Graphics::kTextAlignCenter);
+		}
+		_gfx->copyToScreen();
+		_system->delayMillis(16);
+	}
+
+	// Draw final position (scrollOff = 0)
+	if (!qt) {
+		_gfx->clear(_gfx->black());
+		for (int i = 0; i < storyLength; i++) {
+			if (strlen(story[i]) > 0)
+				_gfx->drawString(font, story[i], _width / 2, ht + lineHeight * i, 200 + i, Graphics::kTextAlignCenter);
+		}
+		_gfx->copyToScreen();
+	}
+
+	// Wait for click (original: while(!Button()); while(Button()&&!qt);)
+	if (!qt)
+		qt = waitForInput();
+
+	// Phase 2: Scroll text off the top of the screen
+	// Original: scrollRect continues moving up, text slides upward
+	if (!qt) {
+		for (int scrollOff = 0; scrollOff > -_height && !qt; scrollOff -= inc) {
+			if (checkSkipRequested()) {
+			qt = true;
+			_sound->stop();
+			break;
+		}
+
+			_gfx->clear(_gfx->black());
+			for (int i = 0; i < storyLength; i++) {
+				int drawY = ht + lineHeight * i + scrollOff;
+				if (strlen(story[i]) > 0 && drawY >= -lineHeight && drawY < _height)
+					_gfx->drawString(font, story[i], _width / 2, drawY, 200 + i, Graphics::kTextAlignCenter);
+			}
+			_gfx->copyToScreen();
+			_system->delayMillis(16);
+		}
+	}
+
+	// Original does NOT stop the sound here  BeamMe continues playing
+	// and intro() waits for it with while(!SoundDone()) after ScrollInfo returns.
+	// Only stop if skipping (qt already stops in the modifier+click handlers above).
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+	return qt;
+}
+
+bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
+	// Original: makestars() in stars.c
+	// Uses 75 moving stars that streak outward from center using XOR lines.
+	const int MAXSTAR = 0x1FF;
+	const int NSTARS = 75;
+	const int deltapd = 0x008;
+
+	int centerX = r.width() / 2;
+	int centerY = r.height() / 2;
+
+	// Build perspective lookup table: rtable[i] = (128*128)/i
+	int rtable[MAXSTAR + 1];
+	rtable[0] = 32000;
+	for (int i = 1; i <= MAXSTAR; i++)
+		rtable[i] = (128 * 128) / i;
+
+	// First draw static background stars (150 random dots)
+	for (int i = 0; i < 150; i++) {
+		int s = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
+		int c = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
+		int d = _randomSource.getRandomNumber(MAXSTAR);
+		if (d < 1) d = 1;
+		int rr = rtable[d];
+		int xx = centerX + (int)(((long long)s * rr) >> 7);
+		int yy = centerY + (int)(((long long)c * rr) >> 7);
+		if (xx >= 0 && xx < _width && yy >= 0 && yy < _height)
+			_gfx->setPixel(xx, yy, 15);
+	}
+
+	// Initialize moving stars  original uses PenMode(patXor) so stars
+	// don't damage the logo underneath (XOR drawing the same line twice
+	// restores the original pixels).
+	int xang[NSTARS], yang[NSTARS], dist[NSTARS];
+	int xsave1[NSTARS], ysave1[NSTARS], xsave2[NSTARS], ysave2[NSTARS];
+
+	_gfx->setXorMode(true);
+
+	for (int i = 0; i < NSTARS; i++) {
+		int d = dist[i] = _randomSource.getRandomNumber(MAXSTAR);
+		if (d <= 0x030) d = dist[i] = MAXSTAR;
+		int s = xang[i] = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
+		int c = yang[i] = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
+
+		int rr = rtable[d];
+		xsave1[i] = centerX + (int)(((long long)s * rr) >> 7);
+		ysave1[i] = centerY + (int)(((long long)c * rr) >> 7);
+
+		int d2 = d - deltapd;
+		if (d2 < 1)
+			d2 = 1;
+		rr = rtable[d2];
+		xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
+		ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
+
+		_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
+	}
+	_gfx->copyToScreen();
+
+	// Animate: original loops ~200 frames or until Mars sound repeats 2x
+	for (int k = 0; k < 120; k++) {
+		if (checkSkipRequested()) {
+			_gfx->setXorMode(false);
+			return true;
+		}
+
+		for (int i = 0; i < NSTARS; i++) {
+			// Erase previous  XOR the same line again to restore underlying pixels
+			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
+
+			int s = xang[i];
+			int c = yang[i];
+
+			if (dist[i] <= 0x030) {
+				dist[i] = MAXSTAR;
+				int rr = rtable[MAXSTAR];
+				xsave1[i] = centerX + (int)(((long long)s * rr) >> 7);
+				ysave1[i] = centerY + (int)(((long long)c * rr) >> 7);
+			} else {
+				xsave1[i] = xsave2[i];
+				ysave1[i] = ysave2[i];
+			}
+
+			int d = (dist[i] -= deltapd);
+			if (d < 1) d = 1;
+			int rr = rtable[d];
+			xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
+			ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
+
+			// Draw new star position
+			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
+		}
+		_gfx->copyToScreen();
+		_system->delayMillis(16);
+	}
+
+	// Fade-out phase: stars fly off without resetting (trails accumulate via XOR)
+	int nstars = 2 * ((MAXSTAR - 0x030) / deltapd);
+	if (nstars > 200) nstars = 200;
+	for (int k = 0; k < nstars; k++) {
+		if (checkSkipRequested()) {
+			_gfx->setXorMode(false);
+			return true;
+		}
+
+		for (int i = 0; i < NSTARS; i++) {
+			int d = dist[i];
+			int s = xang[i];
+			int c = yang[i];
+			dist[i] -= deltapd;
+			if (dist[i] <= 0x030) dist[i] = MAXSTAR;
+
+			if (d >= 1 && d <= MAXSTAR) {
+				int rr1 = rtable[d];
+				int d2 = d - deltapd;
+				if (d2 < 1)
+			d2 = 1;
+				int rr2 = rtable[d2];
+				int x1 = centerX + (int)(((long long)s * rr1) >> 7);
+				int y1 = centerY + (int)(((long long)c * rr1) >> 7);
+				int x2 = centerX + (int)(((long long)s * rr2) >> 7);
+				int y2 = centerY + (int)(((long long)c * rr2) >> 7);
+				_gfx->drawLine(x1, y1, x2, y2, 15);
+			}
+		}
+		_gfx->copyToScreen();
+		_system->delayMillis(8);
+	}
+
+	_gfx->setXorMode(false);
+	return false;
+}
+
+bool ColonyEngine::makeBlackHole() {
+	// Original: Makeblackhole() in intro.c
+	// Mac original draws spiral lines with fading colors:
+	// bcolor starts at (0,0,0) and subtracts (rd=2048, gd=1024, bd=4096) per step,
+	// which wraps around creating shifting color gradients.
+	// We use palette entries 128-191 for the gradient colors.
+	int centerX = _width / 2;
+	int centerY = _height / 2;
+	int dec = 16;
+	int starcnt = 0;
+
+	// Build a lookup table matching the original rtable: rtable[i] = (128*128)/i
+	int rtable[1024];
+	rtable[0] = 32000;
+	for (int i = 1; i < 1024; i++)
+		rtable[i] = (128 * 128) / i;
+
+	for (int k = 0; k < 17; k += 4) {
+		// Reset color per k-iteration (matches Mac: bcolor = {0,0,0} at start of each k)
+		int colorR = 0, colorG = 0, colorB = 0;
+		int rd = 2048, gd = 1024, bd = 4096;
+
+		for (int i = 1000; i > 32; i -= dec) {
+			// Mac original subtracts from color channels (wrapping as uint16);
+			// We simulate this as a gradient from dark to bright
+			// Since Mac uses unsigned wrap: 0 - 4096 = 0xF000 = bright.
+			// After one full cycle (16 steps), the colors cycle.
+			// Map to palette entry based on step
+			colorB = (colorB - bd) & 0xFFFF;
+			colorR = (colorR - rd) & 0xFFFF;
+			colorG = (colorG - gd) & 0xFFFF;
+
+			// Map Mac 16-bit color to 8-bit
+			uint8 palR = (colorR >> 8) & 0xFF;
+			uint8 palG = (colorG >> 8) & 0xFF;
+			uint8 palB = (colorB >> 8) & 0xFF;
+
+			// Use palette entry 128 for current step color
+			byte pal[3] = { palR, palG, palB };
+			_gfx->setPalette(pal, 128, 1);
+
+			starcnt++;
+			if (starcnt == 8) starcnt = 0;
+
+			for (int j = 0; j < 256; j += 8) {
+				int idx = (j + starcnt) & 0xFF;
+				int rt1 = rtable[MIN(i + k, 1023)];
+				int x1 = centerX + (int)(((long long)rt1 * _sint[idx]) >> 7);
+				int y1 = centerY + (int)(((long long)rt1 * _cost[idx]) >> 7);
+
+				int rt2 = rtable[MIN(i + k + 8, 1023)];
+				int x2 = centerX + (int)(((long long)rt2 * _sint[idx]) >> 7);
+				int y2 = centerY + (int)(((long long)rt2 * _cost[idx]) >> 7);
+
+				_gfx->drawLine(x1, y1, x2, y2, 128);
+			}
+
+			// Update screen every step and add a small delay
+			// to simulate the original 68k rendering speed
+			_gfx->copyToScreen();
+			_system->delayMillis(16);
+
+			if (checkSkipRequested()) return true;
+		}
+	}
+	_gfx->copyToScreen();
+	return false;
+}
+
+bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *macFont) {
+	// Original: TimeSquare() in intro.c
+	// 1. Draw horizontal blue gradient lines above/below center
+	// 2. Scroll red text from right to center
+	// 3. Flash klaxon 6 times with inverted rect
+	// 4. Play Mars again, scroll text off to the left
+	//
+	// Mac original: fcolor starts at (0,0,0xFFFF) and subtracts 4096 per pair of lines.
+	// Text is drawn in red (0xFFFF,0,0) on black background.
+
+	_gfx->clear(_gfx->black());
+
+	Graphics::DosFont dosFont;
+	const Graphics::Font *font = macFont ? macFont : (const Graphics::Font *)&dosFont;
+	int swidth = font->getStringWidth(str);
+
+	int centery = _height / 2 - 10;
+
+	// Set up gradient palette entries (160-175) for the gradient lines
+	// Mac original: blue starts at 0xFFFF and decreases by 4096 per line pair
+	// B&W Mac: white gradient instead of blue
+	const bool bwMac = (macFont && !_hasMacColors);
+	for (int i = 0; i < 16; i++) {
+		int val = 255 - i * 16; // 255, 239, 223, ... 15
+		if (val < 0)
+			val = 0;
+		byte pal[3] = { (byte)(bwMac ? val : 0), (byte)(bwMac ? val : 0), (byte)val };
+		_gfx->setPalette(pal, 160 + i, 1);
+	}
+	// Set palette entry 176 for text (red in color, white in B&W)
+	{
+		byte pal[3] = { 255, (byte)(bwMac ? 255 : 0), (byte)(bwMac ? 255 : 0) };
+		_gfx->setPalette(pal, 176, 1);
+	}
+
+	// Draw blue gradient lines above/below center band
+	for (int i = 0; i < 16; i++) {
+		_gfx->drawLine(0, centery - 2 - i * 2, _width, centery - 2 - i * 2, 160 + i);
+		_gfx->drawLine(0, centery - 2 - (i * 2 + 1), _width, centery - 2 - (i * 2 + 1), 160 + i);
+		_gfx->drawLine(0, centery + 16 + i * 2, _width, centery + 16 + i * 2, 160 + i);
+		_gfx->drawLine(0, centery + 16 + i * 2 + 1, _width, centery + 16 + i * 2 + 1, 160 + i);
+	}
+	_gfx->copyToScreen();
+
+	// Phase 1: Scroll text in from the right to center
+	// Original: if(Button()) if(qt=OptionKey()) break;
+	int targetX = (_width - swidth) / 2;
+	for (int x = _width; x > targetX; x -= 2) {
+		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), 0);
+		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft);
+		_gfx->copyToScreen();
+
+		if (checkSkipRequested()) return true;
+		_system->delayMillis(8);
+	}
+
+	// Phase 2: Klaxon flash  original: EndCSound(); then 6 iterations of:
+	//   if(Button()) if(qt=OptionKey()) break;
+	//   while(!SoundDone()); StopSound(); PlayKlaxon(); InvertRect(&invrt);
+	_sound->stop(); // EndCSound()
+	for (int i = 0; i < 6; i++) {
+		if (checkSkipRequested()) return true;
+
+		// Wait for previous klaxon to finish
+		while (_sound->isPlaying() && !shouldQuit())
+			_system->delayMillis(10);
+		_sound->stop();
+
+		_sound->play(Sound::kKlaxon);
+
+		// InvertRect(&invrt)  toggle the text band
+		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), i % 2 ? 0 : 15);
+		_gfx->drawString(font, str, targetX, centery + 2, i % 2 ? 15 : 0, Graphics::kTextAlignLeft);
+		_gfx->copyToScreen();
+	}
+	// Wait for last klaxon
+	while (_sound->isPlaying() && !shouldQuit())
+		_system->delayMillis(10);
+	_sound->stop();
+
+	// Phase 3: PlayMars(), scroll text off to the left
+	_sound->play(Sound::kMars);
+	for (int x = targetX; x > -swidth; x -= 2) {
+		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), 0);
+		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft);
+		_gfx->copyToScreen();
+
+		if (checkSkipRequested()) return true;
+		_system->delayMillis(8);
+	}
+
+	return false;
+}
+
+bool ColonyEngine::drawPict(int resID) {
+	// Original: DoPicture() in intro.c, lines 861-886
+	// Loads a PICT resource, centers it in the screen rect, draws with srcCopy.
+	// Original applies clip rect inset by 1 pixel on all sides.
+	Common::SeekableReadStream *pictStream = nullptr;
+
+	// Try Color Colony resource fork first
+	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);
+
+	if (!pictStream) {
+		debug("drawPict: PICT %d not found", resID);
+		return false;
+	}
+
+	::Image::PICTDecoder decoder;
+	if (decoder.loadStream(*pictStream)) {
+		const Graphics::Surface *surface = decoder.getSurface();
+		if (surface) {
+			// Center PICT on screen (original: locate = centered within rScreen)
+			int x = (_width - surface->w) / 2;
+			int y = (_height - surface->h) / 2;
+			bool isCLUT8 = (surface->format == Graphics::PixelFormat::createFormatCLUT8());
+			const Graphics::Palette &pictPal = decoder.getPalette();
+
+			// Original DoPicture clips 1 pixel inset from locate rect
+			// clip.top = locate.top+1, clip.left = locate.left+1, etc.
+			int clipX1 = x + 1;
+			int clipY1 = y + 1;
+			int clipX2 = x + surface->w - 1;
+			int clipY2 = y + surface->h - 1;
+
+			debug("drawPict(%d): %dx%d at (%d,%d), format=%dbpp, palette=%d entries",
+			      resID, surface->w, surface->h, x, y,
+			      surface->format.bytesPerPixel * 8, pictPal.size());
+
+			// Draw PICT pixels using direct RGB (packRGB) for full color support.
+			for (int iy = 0; iy < surface->h; iy++) {
+				int sy = y + iy;
+				if (sy < clipY1 || sy >= clipY2)
+					continue;
+				for (int ix = 0; ix < surface->w; ix++) {
+					int sx = x + ix;
+					if (sx < clipX1 || sx >= clipX2)
+						continue;
+					byte r, g, b;
+					if (isCLUT8) {
+						byte idx = *((const byte *)surface->getBasePtr(ix, iy));
+						if (pictPal.size() > 0 && idx < (int)pictPal.size()) {
+							pictPal.get(idx, r, g, b);
+						} else {
+							// B&W PICT: 0=white, 1=black
+							r = g = b = (idx == 0) ? 255 : 0;
+						}
+					} else {
+						uint32 pixel = surface->getPixel(ix, iy);
+						surface->format.colorToRGB(pixel, r, g, b);
+					}
+					_gfx->setPixel(sx, sy, 0xFF000000 | ((uint32)r << 16) | ((uint32)g << 8) | b);
+				}
+			}
+			_gfx->copyToScreen();
+			delete pictStream;
+			return true;
+		}
+	} else {
+		warning("drawPict: failed to decode PICT %d", resID);
+	}
+	delete pictStream;
+	return false;
+}
+
+void ColonyEngine::terminateGame(bool blowup) {
+	debug(0, "YOU HAVE BEEN TERMINATED! (blowup=%d)", blowup);
+	if (blowup)
+		_sound->play(Sound::kExplode);
+
+	const char *msg[] = {
+		"   YOU HAVE BEEN TERMINATED!   ",
+		" Type 'q' to quit the game.    ",
+		nullptr
+	};
+	printMessage(msg, true);
+
+	_system->quit();
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
new file mode 100644
index 00000000000..7e7a020a1b8
--- /dev/null
+++ b/engines/colony/map.cpp
@@ -0,0 +1,353 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "common/file.h"
+#include "common/debug.h"
+
+namespace Colony {
+
+void ColonyEngine::loadMap(int mnum) {
+	Common::Path mapPath(Common::String::format("MAP.%d", mnum));
+	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(mapPath);
+	if (!file) {
+		// Try Mac-style path
+		mapPath = Common::Path(Common::String::format("CData/map.%d", mnum));
+		file = Common::MacResManager::openFileOrDataFork(mapPath);
+		if (!file) {
+			warning("Could not open map file %s", mapPath.toString().c_str());
+			return;
+		}
+	}
+
+	file->readUint32BE(); // "DAVE" header
+	int16 mapDefs[10];
+	for (int i = 0; i < 10; i++) {
+		mapDefs[i] = file->readSint16BE();
+	}
+
+	uint16 bLength = file->readUint16BE();
+	uint8 *buffer = (uint8 *)malloc(bLength);
+	if (!buffer) {
+		delete file;
+		error("Out of memory loading map");
+	}
+	file->read(buffer, bLength);
+	delete file;
+
+	memset(_mapData, 0, sizeof(_mapData));
+	memset(_robotArray, 0, sizeof(_robotArray));
+	memset(_foodArray, 0, sizeof(_foodArray));
+	_objects.clear();
+
+	// expand logic
+	int c = 0;
+	_robotNum = kMeNum + 1;
+	for (int i = 0; i < 32; i++) {
+		for (int j = 0; j < 32; j++) {
+			_wall[i][j] = buffer[c++];
+			if (i < 31 && j < 31) {
+				for (int k = 0; k < 5; k++) {
+					if (_wall[i][j] & (1 << (k + 2))) {
+						for (int l = 0; l < 5; l++) {
+							_mapData[i][j][k][l] = buffer[c++];
+						}
+						// PACKIT.C: center feature type 6 marks static map objects.
+						if (k == 4 && _mapData[i][j][4][0] == 6 && i < 31 && j < 31) {
+							Thing obj;
+							memset(&obj, 0, sizeof(obj));
+							obj.alive = 1;
+							obj.visible = 0;
+							obj.type = _mapData[i][j][4][1] + kBaseObject;
+							obj.where.xloc = (i << 8) + 128;
+							obj.where.yloc = (j << 8) + 128;
+							obj.where.xindex = i;
+							obj.where.yindex = j;
+							obj.where.ang = (uint8)(_mapData[i][j][4][2] + 32);
+							obj.where.look = obj.where.ang;
+							_objects.push_back(obj);
+							const int objNum = (int)_objects.size(); // 1-based, DOS-style robot slots
+							if (objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
+								_robotArray[i][j] = (uint8)objNum;
+						}
+					} else {
+						_mapData[i][j][k][0] = 0;
+					}
+				}
+			}
+		}
+	}
+	free(buffer);
+	_level = mnum;
+	_me.type = kMeNum;
+
+	getWall();  // restore saved wall state changes (airlocks)
+	doPatch();  // apply object relocations from patch table
+	initRobots();  // spawn robot objects for this level
+
+	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
+		_robotArray[_me.xindex][_me.yindex] = kMeNum;
+	debug("Successfully loaded map %d (objects: %d)", mnum, (int)_objects.size());
+}
+
+// PATCH.C: Create a new object in _objects and register in _robotArray.
+// Mirrors DOS CreateObject()  sets basic Thing fields for static objects.
+void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
+	Thing obj;
+	memset(&obj, 0, sizeof(obj));
+	while (ang > 255)
+		ang -= 256;
+	obj.alive = 1;
+	obj.visible = 0;
+	obj.type = type;
+	obj.where.xloc = xloc;
+	obj.where.yloc = yloc;
+	obj.where.xindex = xloc >> 8;
+	obj.where.yindex = yloc >> 8;
+	obj.where.delta = 4;
+	obj.where.ang = ang;
+	obj.where.look = ang;
+
+	// Try to reuse a dead slot (starting after kMeNum)
+	int slot = -1;
+	for (int j = kMeNum; j < (int)_objects.size(); j++) {
+		if (!_objects[j].alive) {
+			slot = j;
+			break;
+		}
+	}
+	if (slot >= 0) {
+		_objects[slot] = obj;
+	} else {
+		_objects.push_back(obj);
+		slot = (int)_objects.size() - 1;
+	}
+	int objNum = slot + 1; // 1-based for _robotArray
+	if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+	    obj.where.yindex >= 0 && obj.where.yindex < 32)
+		_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)objNum;
+	if (slot + 1 > _robotNum)
+		_robotNum = slot + 1;
+}
+
+// PATCH.C: DoPatch()  remove originals and install relocated objects.
+void ColonyEngine::doPatch() {
+	// Pass 1: remove objects that were moved away from this level
+	for (uint i = 0; i < _patches.size(); i++) {
+		if (_level == _patches[i].from.level) {
+			int robot = _robotArray[_patches[i].from.xindex][_patches[i].from.yindex];
+			if (robot > 0 && robot <= (int)_objects.size()) {
+				_robotArray[_objects[robot - 1].where.xindex][_objects[robot - 1].where.yindex] = 0;
+				_objects[robot - 1].alive = 0;
+			}
+		}
+	}
+	// Pass 2: install objects that were moved to this level
+	for (uint i = 0; i < _patches.size(); i++) {
+		if (_level == _patches[i].to.level) {
+			createObject(
+				(int)_patches[i].type,
+				(int)_patches[i].to.xloc,
+				(int)_patches[i].to.yloc,
+				_patches[i].to.ang);
+		}
+	}
+}
+
+// PATCH.C: savewall()  save 5 bytes of map feature data for persistence across level loads.
+void ColonyEngine::saveWall(int x, int y, int direction) {
+	if (_level < 1 || _level > 8)
+		return;
+	LevelData &ld = _levelData[_level - 1];
+
+	// Search for existing entry at this location
+	for (int i = 0; i < ld.size; i++) {
+		if (ld.location[i][0] == x && ld.location[i][1] == y && ld.location[i][2] == direction) {
+			for (int j = 0; j < 5; j++)
+				ld.data[i][j] = _mapData[x][y][direction][j];
+			return;
+		}
+	}
+	// Add new entry (max 10)
+	if (ld.size >= 10) {
+		warning("saveWall: too many wall changes for level %d", _level);
+		return;
+	}
+	int i = ld.size;
+	for (int j = 0; j < 5; j++)
+		ld.data[i][j] = _mapData[x][y][direction][j];
+	ld.location[i][0] = x;
+	ld.location[i][1] = y;
+	ld.location[i][2] = direction;
+	ld.size++;
+}
+
+// PATCH.C: getwall()  restore saved wall bytes into _mapData after level load.
+void ColonyEngine::getWall() {
+	if (_level < 1 || _level > 8)
+		return;
+	const LevelData &ld = _levelData[_level - 1];
+	for (int i = 0; i < ld.size; i++) {
+		int x = ld.location[i][0];
+		int y = ld.location[i][1];
+		int dir = ld.location[i][2];
+		if (x < 31 && y < 31 && dir < 5) {
+			for (int j = 0; j < 5; j++)
+				_mapData[x][y][dir][j] = ld.data[i][j];
+		}
+	}
+}
+
+// PATCH.C: newpatch()  create or update a patch entry.
+void ColonyEngine::newPatch(int type, const PassPatch &from, const PassPatch &to, const uint8 *mapdata) {
+	// Search for existing patch where 'from' matches an existing 'to'
+	for (uint i = 0; i < _patches.size(); i++) {
+		if (from.level == _patches[i].to.level &&
+		    from.xindex == _patches[i].to.xindex &&
+		    from.yindex == _patches[i].to.yindex) {
+			_patches[i].to.level = to.level;
+			_patches[i].to.xindex = to.xindex;
+			_patches[i].to.yindex = to.yindex;
+			_patches[i].to.xloc = to.xloc;
+			_patches[i].to.yloc = to.yloc;
+			_patches[i].to.ang = to.ang;
+			return;
+		}
+	}
+	// Create new patch entry (max 100)
+	if (_patches.size() >= 100)
+		return;
+	PatchEntry pe;
+	pe.type = type;
+	pe.from.level = from.level;
+	pe.from.xindex = from.xindex;
+	pe.from.yindex = from.yindex;
+	pe.to.level = to.level;
+	pe.to.xindex = to.xindex;
+	pe.to.yindex = to.yindex;
+	pe.to.xloc = to.xloc;
+	pe.to.yloc = to.yloc;
+	pe.to.ang = to.ang;
+	if (mapdata) {
+		for (int j = 0; j < 5; j++)
+			pe.mapdata[j] = mapdata[j];
+	} else {
+		memset(pe.mapdata, 0, sizeof(pe.mapdata));
+	}
+	_patches.push_back(pe);
+}
+
+// PATCH.C: patchmapto()  find patch entry by destination, fill mapdata.
+bool ColonyEngine::patchMapTo(const PassPatch &to, uint8 *mapdata) {
+	for (uint i = 0; i < _patches.size(); i++) {
+		if (to.level == _patches[i].to.level &&
+		    to.xindex == _patches[i].to.xindex &&
+		    to.yindex == _patches[i].to.yindex) {
+			for (int j = 0; j < 5; j++)
+				mapdata[j] = _patches[i].mapdata[j];
+			return true;
+		}
+	}
+	return false;
+}
+
+// PATCH.C: patchmapfrom()  find patch entry by source, fill destination into mapdata.
+bool ColonyEngine::patchMapFrom(const PassPatch &from, uint8 *mapdata) {
+	for (uint i = 0; i < _patches.size(); i++) {
+		if (from.level == _patches[i].from.level &&
+		    from.xindex == _patches[i].from.xindex &&
+		    from.yindex == _patches[i].from.yindex) {
+			mapdata[2] = _patches[i].to.level;
+			mapdata[3] = _patches[i].to.xindex;
+			mapdata[4] = _patches[i].to.yindex;
+			return true;
+		}
+	}
+	return false;
+}
+
+// DOS InitObject()  spawn robots for the current level.
+// Level 1 = no robots; Level 2 = 25; Level 3-4 = 30; Level 5-7 = 35.
+// Robot #1 = QUEEN, #2 = SNOOP, rest = random type weighted by level.
+void ColonyEngine::initRobots() {
+	if (_level == 1)
+		return;  // Level 1 has no robots
+
+	int maxrob;
+	switch (_level) {
+	case 2:  maxrob = 25; break;
+	case 3:
+	case 4:  maxrob = 30; break;
+	default: maxrob = 35; break;
+	}
+
+	int lvl = _level - 1;
+	if (lvl > 5)
+		lvl = 5;
+
+	for (int i = 1; i <= maxrob; i++) {
+		uint8 ang = _randomSource.getRandomNumber(255);
+		int xloc, yloc;
+
+		// Find unoccupied cell (avoiding borders)
+		do {
+			if (_level == 7 && i == 1) {
+				// Queen on level 7 has fixed position
+				xloc = 27;
+				yloc = 10;
+			} else {
+				xloc = 2 + _randomSource.getRandomNumber(26);  // 2..28
+				yloc = 2 + _randomSource.getRandomNumber(26);  // 2..28
+			}
+		} while (_robotArray[xloc][yloc] != 0);
+
+		// Convert grid coords to world coords (center of cell)
+		int wxloc = (xloc << 8) + 128;
+		int wyloc = (yloc << 8) + 128;
+
+		int type;
+		if (i == 1)
+			type = kRobQueen;
+		else if (i == 2)
+			type = kRobSnoop;
+		else {
+			// Random type weighted by level difficulty
+			int rnd = _randomSource.getRandomNumber(lvl);
+			if (rnd > 5)
+				rnd = 5;
+			switch (rnd) {
+			case 0: type = kRobCube; break;
+			case 1: type = kRobPyramid; break;
+			case 2: type = kRobUPyramid; break;
+			case 3: type = kRobEye; break;
+			case 4: type = kRobDrone; break;
+			case 5: type = kRobSoldier; break;
+			default: type = kRobCube; break;
+			}
+		}
+
+		createObject(type, wxloc, wyloc, ang);
+	}
+
+	debug("initRobots: spawned %d robots on level %d", maxrob, _level);
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/module.mk b/engines/colony/module.mk
index 97383af05a2..1f404c47c90 100644
--- a/engines/colony/module.mk
+++ b/engines/colony/module.mk
@@ -1,9 +1,14 @@
 MODULE := engines/colony
 
 MODULE_OBJS := \
+	animation.o \
 	colony.o \
 	gfx.o \
+	interaction.o \
+	intro.o \
+	map.o \
 	metaengine.o \
+	movement.o \
 	render.o \
 	renderer_opengl.o \
 	sound.o \
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
new file mode 100644
index 00000000000..7bd0367845d
--- /dev/null
+++ b/engines/colony/movement.cpp
@@ -0,0 +1,676 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "colony/gfx.h"
+#include "common/system.h"
+#include "common/debug.h"
+#include <math.h>
+
+namespace Colony {
+
+int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
+	if (x < 0 || x >= 32 || y < 0 || y >= 32)
+		return -1;
+	const int rnum = _robotArray[x][y];
+	if (rnum <= 0)
+		return 0;
+	if (pobject == &_me && rnum <= (int)_objects.size()) {
+		Thing &obj = _objects[rnum - 1];
+		if (obj.type <= kBaseObject)
+			obj.where.look = obj.where.ang = _me.ang + 128;
+	}
+	return rnum;
+}
+
+int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
+	const int xind2 = xnew >> 8;
+	const int yind2 = ynew >> 8;
+
+	// Clamp position to maintain minimum distance from walls within a cell.
+	// This prevents the camera from clipping through thin walls.
+	// Skip clamping for walls that have any interactive feature (door, airlock,
+	// stairs, etc.) so the player can reach the boundary and trigger the interaction.
+	// The wall itself still blocks via checkwall; clamping only prevents camera clipping
+	// on plain walls with no features.
+	static const int kWallPad = 40;
+	auto hasFeature = [this](int cx, int cy, int dir) -> bool {
+		if (cx < 0 || cx >= 31 || cy < 0 || cy >= 31)
+			return false;
+		uint8 type = _mapData[cx][cy][dir][0];
+		return type == kWallFeatureDoor || type == kWallFeatureAirlock ||
+			type == kWallFeatureUpStairs || type == kWallFeatureDnStairs ||
+			type == kWallFeatureTunnel || type == kWallFeatureElevator;
+	};
+	auto clampToWalls = [this, &hasFeature](Locate *p) {
+		int cx = p->xindex;
+		int cy = p->yindex;
+		int cellMinX = cx << 8;
+		int cellMinY = cy << 8;
+		int cellMaxX = cellMinX + 255;
+		int cellMaxY = cellMinY + 255;
+
+		// South wall of this cell (at cellMinY boundary)
+		if ((wallAt(cx, cy) & 0x01) && !hasFeature(cx, cy, kDirSouth))
+			p->yloc = MAX(p->yloc, cellMinY + kWallPad);
+		// North wall (at cellMaxY+1 boundary = south wall of cell above)
+		if ((wallAt(cx, cy + 1) & 0x01) && !hasFeature(cx, cy, kDirNorth))
+			p->yloc = MIN(p->yloc, cellMaxY - kWallPad);
+		// West wall of this cell (at cellMinX boundary)
+		if ((wallAt(cx, cy) & 0x02) && !hasFeature(cx, cy, kDirWest))
+			p->xloc = MAX(p->xloc, cellMinX + kWallPad);
+		// East wall (at cellMaxX+1 boundary = west wall of cell to right)
+		if ((wallAt(cx + 1, cy) & 0x02) && !hasFeature(cx, cy, kDirEast))
+			p->xloc = MIN(p->xloc, cellMaxX - kWallPad);
+	};
+
+	auto occupied = [&]() -> int {
+		return occupiedObjectAt(xind2, yind2, pobject);
+	};
+	auto moveTo = [&]() -> int {
+		const int rnum = occupied();
+		if (rnum)
+			return rnum;
+		pobject->yindex = yind2;
+		pobject->xindex = xind2;
+		pobject->dx = xnew - pobject->xloc;
+		pobject->dy = ynew - pobject->yloc;
+		pobject->xloc = xnew;
+		pobject->yloc = ynew;
+		clampToWalls(pobject);
+		return 0;
+	};
+
+	// tryPassThroughFeature returns: 0=blocked, 1=pass through, 2=teleported (position already set)
+	auto tryFeature = [&](int dir) -> int {
+		int r = tryPassThroughFeature(pobject->xindex, pobject->yindex, dir, pobject);
+		if (r == 2)
+			return 0; // teleported  position already updated by the feature
+		if (r == 1)
+			return moveTo();
+		return -2; // blocked, caller handles
+	};
+
+	if (xind2 == pobject->xindex) {
+		if (yind2 == pobject->yindex) {
+			pobject->dx = xnew - pobject->xloc;
+			pobject->dy = ynew - pobject->yloc;
+			pobject->xloc = xnew;
+			pobject->yloc = ynew;
+			clampToWalls(pobject);
+			return 0;
+		}
+
+		if (yind2 > pobject->yindex) {
+			if (!(_wall[pobject->xindex][yind2] & 1))
+				return moveTo();
+			{
+				int r = tryFeature(kDirNorth);
+				if (r != -2)
+					return r;
+			}
+			debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
+			_sound->play(Sound::kBang);
+			return -1;
+
+		}
+
+		if (!(_wall[pobject->xindex][pobject->yindex] & 1))
+			return moveTo();
+		{
+			int r = tryFeature(kDirSouth);
+			if (r != -2)
+				return r;
+		}
+		debug("Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
+		_sound->play(Sound::kBang);
+		return -1;
+
+	}
+
+	if (yind2 == pobject->yindex) {
+		if (xind2 > pobject->xindex) {
+			if (!(_wall[xind2][pobject->yindex] & 2))
+				return moveTo();
+			{
+				int r = tryFeature(kDirEast);
+				if (r != -2)
+					return r;
+			}
+			debug("Collision East at x=%d y=%d", xind2, pobject->yindex);
+			_sound->play(Sound::kBang);
+			return -1;
+
+		}
+
+		if (!(_wall[pobject->xindex][pobject->yindex] & 2))
+			return moveTo();
+		{
+			int r = tryFeature(kDirWest);
+			if (r != -2)
+				return r;
+		}
+		debug("Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
+		_sound->play(Sound::kBang);
+		return -1;
+
+	}
+
+	// Diagonal
+	if (xind2 > pobject->xindex) {
+		if (yind2 > pobject->yindex) {
+			if ((_wall[pobject->xindex][yind2] & 1) || (_wall[xind2][pobject->yindex] & 2) || (_wall[xind2][yind2] & 3)) {
+				debug("Collision Diagonal SE");
+				return -1;
+			}
+		} else {
+			if ((_wall[pobject->xindex][pobject->yindex] & 1) || (_wall[xind2][yind2] & 2) || (_wall[xind2][pobject->yindex] & 3)) {
+				debug("Collision Diagonal NE");
+				return -1;
+			}
+		}
+	} else {
+		if (yind2 > pobject->yindex) {
+			if ((_wall[xind2][yind2] & 1) || (_wall[pobject->xindex][pobject->yindex] & 2) || (_wall[pobject->xindex][yind2] & 3)) {
+				debug("Collision Diagonal SW");
+				return -1;
+			}
+		} else {
+			if ((_wall[xind2][pobject->yindex] & 1) || (_wall[pobject->xindex][yind2] & 2) || (_wall[pobject->xindex][pobject->yindex] & 3)) {
+				debug("Collision Diagonal NW");
+				return -1;
+			}
+		}
+	}
+
+	return moveTo();
+}
+
+bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
+	if (x < 0 || x >= 31 || y < 0 || y >= 31 || direction < 0 || direction > 3)
+		return false;
+
+	const uint8 wallType = _mapData[x][y][direction][0];
+	if (wallType != kWallFeatureDoor && wallType != kWallFeatureAirlock)
+		return false;
+
+	_mapData[x][y][direction][1] = (uint8)state;
+	if (state == 0)
+		_sound->play(Sound::kDoor);
+
+
+	int nx = x;
+	int ny = y;
+	int opposite = -1;
+	switch (direction) {
+	case kDirNorth:
+		ny += 1;
+		opposite = kDirSouth;
+		break;
+	case kDirEast:
+		nx += 1;
+		opposite = kDirWest;
+		break;
+	case kDirWest:
+		nx -= 1;
+		opposite = kDirEast;
+		break;
+	case kDirSouth:
+		ny -= 1;
+		opposite = kDirNorth;
+		break;
+	default:
+		return true;
+	}
+
+	if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31 && opposite >= 0) {
+		if (_mapData[nx][ny][opposite][0] == wallType)
+			_mapData[nx][ny][opposite][1] = (uint8)state;
+	}
+
+	// Persist airlock state changes across level loads
+	if (wallType == kWallFeatureAirlock) {
+		saveWall(x, y, direction);
+		if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31 && opposite >= 0)
+			saveWall(nx, ny, opposite);
+	}
+
+	return true;
+}
+
+int ColonyEngine::openAdjacentDoors(int x, int y) {
+	int opened = 0;
+	for (int dir = 0; dir < 4; dir++) {
+		const uint8 *map = mapFeatureAt(x, y, dir);
+		if (!map)
+			continue;
+		if (map[0] == kWallFeatureDoor && map[1] != 0) {
+			if (setDoorState(x, y, dir, 0))
+				opened++;
+		}
+	}
+	return opened;
+}
+
+// Returns: 0 = blocked, 1 = can pass through normally, 2 = teleported (position already set)
+int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject) {
+	const uint8 *map = mapFeatureAt(fromX, fromY, direction);
+	if (!map || map[0] == kWallFeatureNone)
+		return 0;
+
+	switch (map[0]) {
+	case kWallFeatureDoor:
+		if (map[1] == 0)
+			return 1; // already open  pass through
+		if (pobject != &_me)
+			return 0; // robots can't open doors
+		// DOS DoDoor: play door animation, player clicks handle to open
+		if (_corePower[_coreIndex] == 0)
+			return 0; // no power
+		{
+			const char *animName = (_level == 1 || _level == 5 || _level == 6) ? "bulkhead" : "door";
+			if (!loadAnimation(animName))
+				return 0;
+			_animationResult = 0;
+			_doorOpen = false;
+			setObjectState(2, 1); // door starts closed
+			playAnimation();
+			if (_animationResult) {
+				setDoorState(fromX, fromY, direction, 0);
+				return 1; // pass through
+			}
+			return 0; // player didn't open the door
+		}
+	case kWallFeatureAirlock:
+		if (map[1] == 0)
+			return 1; // already open  pass through
+		if (pobject != &_me)
+			return 0;
+		// DOS DoAirLock: play airlock animation
+		{
+			if (!loadAnimation("airlock"))
+				return 0;
+			_animationResult = 0;
+			_doorOpen = false;
+			setObjectState(2, 1); // airlock starts closed
+			setObjectState(1, 1);
+			playAnimation();
+			if (_animationResult) {
+				setDoorState(fromX, fromY, direction, 0);
+				return 1;
+			}
+			return 0;
+		}
+
+	case kWallFeatureUpStairs:
+	case kWallFeatureDnStairs:
+	case kWallFeatureTunnel: {
+		if (pobject != &_me)
+			return 0; // robots don't use stairs/tunnels
+
+		// DOS GoTo(): mapdata[2]=level, [3]=xcell, [4]=ycell
+		// Must copy values BEFORE loadMap, which overwrites _mapData
+		const int targetMap = map[2];
+		const int targetX = map[3];
+		const int targetY = map[4];
+
+		if (targetMap == 0 && targetX == 0 && targetY == 0)
+			return 1; // no destination data  just pass through
+
+		// Special cases from DOS
+		if (targetMap == 100) {
+			doText(78, 0); // Dimension error
+			return 0;
+		}
+
+		// Play appropriate sound
+		if (map[0] == kWallFeatureDnStairs)
+			_sound->play(Sound::kClatter);
+
+		// Move player to target position (preserve sub-cell offset like DOS)
+		if (targetX > 0 && targetX < 31 && targetY > 0 && targetY < 31) {
+			const int xmod = pobject->xloc - (pobject->xindex << 8);
+			const int ymod = pobject->yloc - (pobject->yindex << 8);
+			_robotArray[pobject->xindex][pobject->yindex] = 0;
+			pobject->xloc = (targetX << 8) + xmod;
+			pobject->xindex = targetX;
+			pobject->yloc = (targetY << 8) + ymod;
+			pobject->yindex = targetY;
+		}
+
+		// Load new map if target level specified
+		if (targetMap > 0 && targetMap != _level) {
+			loadMap(targetMap);
+			// DOS: coreindex depends on level
+			_coreIndex = (targetMap == 1) ? 0 : 1;
+		}
+
+		if (pobject->xindex >= 0 && pobject->xindex < 32 &&
+		    pobject->yindex >= 0 && pobject->yindex < 32)
+			_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
+
+		debug("Level change via %s: level=%d pos=(%d,%d)",
+		      map[0] == kWallFeatureUpStairs ? "upstairs" :
+		      map[0] == kWallFeatureDnStairs ? "downstairs" : "tunnel",
+		      _level, pobject->xindex, pobject->yindex);
+		return 2; // teleported
+	}
+
+	case kWallFeatureElevator: {
+		if (pobject != &_me)
+			return 0;
+		if (_corePower[1] == 0) {
+			inform("ELEVATOR HAS NO POWER.", true);
+			return 0;
+		}
+
+		// DOS DoElevator: play elevator animation with floor selection
+		if (!loadAnimation("elev"))
+			return 0;
+		_animationResult = 0;
+		_doorOpen = false;
+		_elevatorFloor = _level - 1; // DOS: fl = level-1
+		setObjectOnOff(6, false);
+		setObjectOnOff(7, false);
+		setObjectOnOff(8, false);
+		setObjectOnOff(9, false);
+		setObjectOnOff(10, false);
+		playAnimation();
+
+		bool entered = (_animationResult >= 2);
+		if (entered && _elevatorFloor + 1 != _level) {
+			// Player selected a different floor
+			int targetMap = _elevatorFloor + 1;
+			const int targetX = map[3];
+			const int targetY = map[4];
+
+			if (targetX > 0 && targetX < 31 && targetY > 0 && targetY < 31) {
+				_robotArray[pobject->xindex][pobject->yindex] = 0;
+				pobject->xloc = (targetX << 8) + 128;
+				pobject->xindex = targetX;
+				pobject->yloc = (targetY << 8) + 128;
+				pobject->yindex = targetY;
+				pobject->ang += 128;
+				pobject->look = pobject->ang;
+			}
+
+			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)
+				_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
+
+			debug("Elevator: level=%d pos=(%d,%d)", _level, pobject->xindex, pobject->yindex);
+			return 2; // teleported
+		}
+		// Player entered but stayed on same floor  turn around
+		if (entered) {
+			pobject->ang += 128;
+			pobject->look = pobject->ang;
+		}
+		return 0;
+	}
+
+	default:
+		return 0;
+	}
+}
+
+void ColonyEngine::fallThroughHole() {
+	// DOS tunnel(TRUE, mapdata) + GoTo(mapdata)
+	// Called when player falls through a floor hole (SMHOLEFLR or LGHOLEFLR)
+	const uint8 *mapdata = _mapData[_me.xindex][_me.yindex][4];
+	int targetMap = mapdata[2];
+	int targetX = mapdata[3];
+	int targetY = mapdata[4];
+
+	if (targetMap == 0 && targetX == 0 && targetY == 0) {
+		terminateGame(false); // you're dead
+		return;
+	}
+
+	// DOS tunnel(TRUE,...): power damage from falling
+	int damage = -(_level << 7);
+	for (int i = 0; i < 3; i++)
+		_corePower[i] += damage;
+
+	_sound->play(Sound::kClatter);
+
+	// DOS tunnel(pt=TRUE): falling animation  nested rectangles shrinking toward
+	// center, simulating falling down a shaft. White outlines on black background.
+	// DOS runs 10 steps × 2 frames = 20 display frames at ~15fps = ~1.3 seconds.
+	// At 60fps we use 80 frames for the same duration, paced by the frame limiter.
+	{
+		const int cx = (_screenR.left + _screenR.right) / 2;
+		const int cy = (_screenR.top + _screenR.bottom) / 2;
+		const int hw = _screenR.width() / 2;
+		const int hh = _screenR.height() / 2;
+		const int totalFrames = 80;
+		const int maxRings = 10;
+
+		for (int frame = 0; frame < totalFrames && !shouldQuit(); frame++) {
+			_frameLimiter->startFrame();
+
+			_gfx->fillRect(_screenR, 0); // black background
+
+			float progress = (float)frame / totalFrames;
+
+			// Draw nested rectangles  outer ring shrinks in, inner rings follow
+			// The number of visible rings decreases as we fall deeper
+			int visibleRings = maxRings - (int)(progress * (maxRings - 1));
+			for (int ring = 0; ring < visibleRings; ring++) {
+				// Each ring's depth combines the overall fall progress with per-ring spacing
+				float depth = progress * 0.6f + (float)ring / (maxRings + 2.0f);
+				if (depth >= 1.0f)
+					break;
+				float scale = 1.0f - depth;
+				int rw = (int)(hw * scale);
+				int rh = (int)(hh * scale);
+				if (rw < 2 || rh < 2)
+					break;
+				Common::Rect r(cx - rw, cy - rh, cx + rw, cy + rh);
+				_gfx->drawRect(r, 15); // white outline
+			}
+
+			_frameLimiter->delayBeforeSwap();
+			_gfx->copyToScreen();
+		}
+	}
+
+	// DOS GoTo(): preserve sub-cell offset, move to destination
+	if (targetX > 0 && targetY > 0) {
+		// Don't go if destination is occupied on same level (DOS: (!map) && robotarray check)
+		if (targetMap == 0 && _robotArray[targetX][targetY] != 0)
+			return;
+
+		_robotArray[_me.xindex][_me.yindex] = 0;
+
+		// Preserve sub-cell offset (DOS: xmod = xloc - (xindex<<8))
+		int xmod = _me.xloc - (_me.xindex << 8);
+		int ymod = _me.yloc - (_me.yindex << 8);
+		_me.xloc = (targetX << 8) + xmod;
+		_me.xindex = targetX;
+		_me.yloc = (targetY << 8) + ymod;
+		_me.yindex = targetY;
+
+		_robotArray[targetX][targetY] = kMeNum;
+	}
+
+	// DOS: if(map) load_mapnum(map, TRUE)  always reload when map != 0
+	if (targetMap > 0)
+		loadMap(targetMap);
+
+	debug("Fell through hole: level=%d pos=(%d,%d)", _level, _me.xindex, _me.yindex);
+}
+
+void ColonyEngine::checkCenter() {
+	// DOS CCenter(): check if player is standing on a floor hole or hotfoot
+	if (_me.xindex < 0 || _me.xindex >= 31 || _me.yindex < 0 || _me.yindex >= 31)
+		return;
+
+	const uint8 cellType = _mapData[_me.xindex][_me.yindex][4][0];
+	if (cellType == 0)
+		return;
+
+	switch (cellType) {
+	case 1: { // SMHOLEFLR  small floor hole, must be near center
+		// DOS: xcheck=abs(xloc-(xindex<<8)); if(xcheck>64&&xcheck<192)
+		int xcheck = ABS(_me.xloc - (_me.xindex << 8));
+		int ycheck = ABS(_me.yloc - (_me.yindex << 8));
+		if (xcheck > 64 && xcheck < 192 && ycheck > 64 && ycheck < 192)
+			fallThroughHole();
+		break;
+	}
+	case 2: // LGHOLEFLR  large floor hole, full cell
+		fallThroughHole();
+		break;
+	case 5: // HOTFOOT  electric floor, damages power
+		// DOS: SetPower(-(5<<level),-(5<<level),-(5<<level))
+		for (int i = 0; i < 3; i++)
+			_corePower[i] -= (5 << _level);
+		break;
+	default:
+		break;
+	}
+}
+
+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 robot = checkwall(xnew, ynew, &_me);
+	if (robot > 0 && allowInteraction)
+		interactWithObject(robot);
+
+	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
+		_robotArray[_me.xindex][_me.yindex] = kMeNum;
+}
+
+// DOS ExitFL(): step back one cell and drop the forklift.
+void ColonyEngine::exitForklift() {
+	if (_fl != 1)
+		return;
+
+	int xloc = _me.xloc;
+	int yloc = _me.yloc;
+	int xindex = _me.xindex;
+	int yindex = _me.yindex;
+
+	// Walk backward until we move into a different cell
+	while (_me.xindex == xindex && _me.yindex == yindex) {
+		int xnew = _me.xloc - _cost[_me.ang];
+		int ynew = _me.yloc - _sint[_me.ang];
+		_me.type = 2; // temporary small collision type
+		if (checkwall(xnew, ynew, &_me)) {
+			_sound->play(Sound::kChime);
+			_me.type = kMeNum;
+			return;
+		}
+		_me.type = kMeNum;
+	}
+
+	// Snap to cell center for the dropped forklift
+	xloc = (xloc >> 8);
+	xloc = (xloc << 8) + 128;
+	yloc = (yloc >> 8);
+	yloc = (yloc << 8) + 128;
+
+	createObject(kObjForkLift, xloc, yloc, _me.ang);
+
+	PassPatch to;
+	to.level = _level;
+	to.xindex = xindex;
+	to.yindex = yindex;
+	to.xloc = xloc;
+	to.yloc = yloc;
+	to.ang = _me.ang;
+	newPatch(kObjForkLift, _carryPatch[0], to, nullptr);
+
+	_fl = 0;
+}
+
+// DOS DropFL(): step back one cell and drop the carried object, then return to fl=1.
+void ColonyEngine::dropCarriedObject() {
+	if (_fl != 2)
+		return;
+
+	// Special case: carrying reactor core
+	if (_carryType == kObjReactor) {
+		_sound->play(Sound::kChime); // glass break sound
+		_carryType = 0;
+		_fl = 1;
+		return;
+	}
+
+	// Play the drop animation
+	if (loadAnimation("lift")) {
+		_animationResult = 0;
+		playAnimation();
+		if (!_animationResult) {
+			// Animation was cancelled  don't drop
+			return;
+		}
+	}
+
+	int xloc = _me.xloc;
+	int yloc = _me.yloc;
+	int xindex = _me.xindex;
+	int yindex = _me.yindex;
+
+	// Walk backward until we move into a different cell
+	while (_me.xindex == xindex && _me.yindex == yindex) {
+		int xnew = _me.xloc - _cost[_me.ang];
+		int ynew = _me.yloc - _sint[_me.ang];
+		_me.type = 2;
+		if (checkwall(xnew, ynew, &_me)) {
+			_sound->play(Sound::kChime);
+			_me.type = kMeNum;
+			return;
+		}
+		_me.type = kMeNum;
+	}
+
+	// DOS: teleport always drops at ang=0; other objects use player's angle
+	uint8 ang = (_carryType == kObjTeleport) ? 0 : _me.ang;
+
+	xloc = (xloc >> 8);
+	xloc = (xloc << 8) + 128;
+	yloc = (yloc >> 8);
+	yloc = (yloc << 8) + 128;
+
+	createObject(_carryType, xloc, yloc, ang);
+
+	PassPatch to;
+	to.level = _level;
+	to.xindex = xindex;
+	to.yindex = yindex;
+	to.xloc = xloc;
+	to.yloc = yloc;
+	to.ang = _me.ang;
+	newPatch(_carryType, _carryPatch[1], to, nullptr);
+
+	_fl = 1;
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 02e644aadfe..54daf200ea0 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -590,664 +590,6 @@ void ColonyEngine::drawCrosshair() {
 	}
 }
 
-int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
-	if (x < 0 || x >= 32 || y < 0 || y >= 32)
-		return -1;
-	const int rnum = _robotArray[x][y];
-	if (rnum <= 0)
-		return 0;
-	if (pobject == &_me && rnum <= (int)_objects.size()) {
-		Thing &obj = _objects[rnum - 1];
-		if (obj.type <= kBaseObject)
-			obj.where.look = obj.where.ang = _me.ang + 128;
-	}
-	return rnum;
-}
-
-int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
-	const int xind2 = xnew >> 8;
-	const int yind2 = ynew >> 8;
-
-	// Clamp position to maintain minimum distance from walls within a cell.
-	// This prevents the camera from clipping through thin walls.
-	// Skip clamping for walls that have any interactive feature (door, airlock,
-	// stairs, etc.) so the player can reach the boundary and trigger the interaction.
-	// The wall itself still blocks via checkwall; clamping only prevents camera clipping
-	// on plain walls with no features.
-	static const int kWallPad = 40;
-	auto hasFeature = [this](int cx, int cy, int dir) -> bool {
-		if (cx < 0 || cx >= 31 || cy < 0 || cy >= 31)
-			return false;
-		uint8 type = _mapData[cx][cy][dir][0];
-		return type == kWallFeatureDoor || type == kWallFeatureAirlock ||
-			type == kWallFeatureUpStairs || type == kWallFeatureDnStairs ||
-			type == kWallFeatureTunnel || type == kWallFeatureElevator;
-	};
-	auto clampToWalls = [this, &hasFeature](Locate *p) {
-		int cx = p->xindex;
-		int cy = p->yindex;
-		int cellMinX = cx << 8;
-		int cellMinY = cy << 8;
-		int cellMaxX = cellMinX + 255;
-		int cellMaxY = cellMinY + 255;
-
-		// South wall of this cell (at cellMinY boundary)
-		if ((wallAt(cx, cy) & 0x01) && !hasFeature(cx, cy, kDirSouth))
-			p->yloc = MAX(p->yloc, cellMinY + kWallPad);
-		// North wall (at cellMaxY+1 boundary = south wall of cell above)
-		if ((wallAt(cx, cy + 1) & 0x01) && !hasFeature(cx, cy, kDirNorth))
-			p->yloc = MIN(p->yloc, cellMaxY - kWallPad);
-		// West wall of this cell (at cellMinX boundary)
-		if ((wallAt(cx, cy) & 0x02) && !hasFeature(cx, cy, kDirWest))
-			p->xloc = MAX(p->xloc, cellMinX + kWallPad);
-		// East wall (at cellMaxX+1 boundary = west wall of cell to right)
-		if ((wallAt(cx + 1, cy) & 0x02) && !hasFeature(cx, cy, kDirEast))
-			p->xloc = MIN(p->xloc, cellMaxX - kWallPad);
-	};
-
-	auto occupied = [&]() -> int {
-		return occupiedObjectAt(xind2, yind2, pobject);
-	};
-	auto moveTo = [&]() -> int {
-		const int rnum = occupied();
-		if (rnum)
-			return rnum;
-		pobject->yindex = yind2;
-		pobject->xindex = xind2;
-		pobject->dx = xnew - pobject->xloc;
-		pobject->dy = ynew - pobject->yloc;
-		pobject->xloc = xnew;
-		pobject->yloc = ynew;
-		clampToWalls(pobject);
-		return 0;
-	};
-
-	// tryPassThroughFeature returns: 0=blocked, 1=pass through, 2=teleported (position already set)
-	auto tryFeature = [&](int dir) -> int {
-		int r = tryPassThroughFeature(pobject->xindex, pobject->yindex, dir, pobject);
-		if (r == 2)
-			return 0; // teleported  position already updated by the feature
-		if (r == 1)
-			return moveTo();
-		return -2; // blocked, caller handles
-	};
-
-	if (xind2 == pobject->xindex) {
-		if (yind2 == pobject->yindex) {
-			pobject->dx = xnew - pobject->xloc;
-			pobject->dy = ynew - pobject->yloc;
-			pobject->xloc = xnew;
-			pobject->yloc = ynew;
-			clampToWalls(pobject);
-			return 0;
-		}
-
-		if (yind2 > pobject->yindex) {
-			if (!(_wall[pobject->xindex][yind2] & 1))
-				return moveTo();
-			{
-				int r = tryFeature(kDirNorth);
-				if (r != -2)
-					return r;
-			}
-			debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
-			_sound->play(Sound::kBang);
-			return -1;
-
-		}
-
-		if (!(_wall[pobject->xindex][pobject->yindex] & 1))
-			return moveTo();
-		{
-			int r = tryFeature(kDirSouth);
-			if (r != -2)
-				return r;
-		}
-		debug("Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
-		_sound->play(Sound::kBang);
-		return -1;
-
-	}
-
-	if (yind2 == pobject->yindex) {
-		if (xind2 > pobject->xindex) {
-			if (!(_wall[xind2][pobject->yindex] & 2))
-				return moveTo();
-			{
-				int r = tryFeature(kDirEast);
-				if (r != -2)
-					return r;
-			}
-			debug("Collision East at x=%d y=%d", xind2, pobject->yindex);
-			_sound->play(Sound::kBang);
-			return -1;
-
-		}
-
-		if (!(_wall[pobject->xindex][pobject->yindex] & 2))
-			return moveTo();
-		{
-			int r = tryFeature(kDirWest);
-			if (r != -2)
-				return r;
-		}
-		debug("Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
-		_sound->play(Sound::kBang);
-		return -1;
-
-	}
-
-	// Diagonal
-	if (xind2 > pobject->xindex) {
-		if (yind2 > pobject->yindex) {
-			if ((_wall[pobject->xindex][yind2] & 1) || (_wall[xind2][pobject->yindex] & 2) || (_wall[xind2][yind2] & 3)) {
-				debug("Collision Diagonal SE");
-				return -1;
-			}
-		} else {
-			if ((_wall[pobject->xindex][pobject->yindex] & 1) || (_wall[xind2][yind2] & 2) || (_wall[xind2][pobject->yindex] & 3)) {
-				debug("Collision Diagonal NE");
-				return -1;
-			}
-		}
-	} else {
-		if (yind2 > pobject->yindex) {
-			if ((_wall[xind2][yind2] & 1) || (_wall[pobject->xindex][pobject->yindex] & 2) || (_wall[pobject->xindex][yind2] & 3)) {
-				debug("Collision Diagonal SW");
-				return -1;
-			}
-		} else {
-			if ((_wall[xind2][pobject->yindex] & 1) || (_wall[pobject->xindex][yind2] & 2) || (_wall[pobject->xindex][pobject->yindex] & 3)) {
-				debug("Collision Diagonal NW");
-				return -1;
-			}
-		}
-	}
-
-	return moveTo();
-}
-
-bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
-	if (x < 0 || x >= 31 || y < 0 || y >= 31 || direction < 0 || direction > 3)
-		return false;
-
-	const uint8 wallType = _mapData[x][y][direction][0];
-	if (wallType != kWallFeatureDoor && wallType != kWallFeatureAirlock)
-		return false;
-
-	_mapData[x][y][direction][1] = (uint8)state;
-	if (state == 0)
-		_sound->play(Sound::kDoor);
-
-
-	int nx = x;
-	int ny = y;
-	int opposite = -1;
-	switch (direction) {
-	case kDirNorth:
-		ny += 1;
-		opposite = kDirSouth;
-		break;
-	case kDirEast:
-		nx += 1;
-		opposite = kDirWest;
-		break;
-	case kDirWest:
-		nx -= 1;
-		opposite = kDirEast;
-		break;
-	case kDirSouth:
-		ny -= 1;
-		opposite = kDirNorth;
-		break;
-	default:
-		return true;
-	}
-
-	if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31 && opposite >= 0) {
-		if (_mapData[nx][ny][opposite][0] == wallType)
-			_mapData[nx][ny][opposite][1] = (uint8)state;
-	}
-
-	// Persist airlock state changes across level loads
-	if (wallType == kWallFeatureAirlock) {
-		saveWall(x, y, direction);
-		if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31 && opposite >= 0)
-			saveWall(nx, ny, opposite);
-	}
-
-	return true;
-}
-
-int ColonyEngine::openAdjacentDoors(int x, int y) {
-	int opened = 0;
-	for (int dir = 0; dir < 4; dir++) {
-		const uint8 *map = mapFeatureAt(x, y, dir);
-		if (!map)
-			continue;
-		if (map[0] == kWallFeatureDoor && map[1] != 0) {
-			if (setDoorState(x, y, dir, 0))
-				opened++;
-		}
-	}
-	return opened;
-}
-
-// Returns: 0 = blocked, 1 = can pass through normally, 2 = teleported (position already set)
-int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject) {
-	const uint8 *map = mapFeatureAt(fromX, fromY, direction);
-	if (!map || map[0] == kWallFeatureNone)
-		return 0;
-
-	switch (map[0]) {
-	case kWallFeatureDoor:
-		if (map[1] == 0)
-			return 1; // already open  pass through
-		if (pobject != &_me)
-			return 0; // robots can't open doors
-		// DOS DoDoor: play door animation, player clicks handle to open
-		if (_corePower[_coreIndex] == 0)
-			return 0; // no power
-		{
-			const char *animName = (_level == 1 || _level == 5 || _level == 6) ? "bulkhead" : "door";
-			if (!loadAnimation(animName))
-				return 0;
-			_animationResult = 0;
-			_doorOpen = false;
-			setObjectState(2, 1); // door starts closed
-			playAnimation();
-			if (_animationResult) {
-				setDoorState(fromX, fromY, direction, 0);
-				return 1; // pass through
-			}
-			return 0; // player didn't open the door
-		}
-	case kWallFeatureAirlock:
-		if (map[1] == 0)
-			return 1; // already open  pass through
-		if (pobject != &_me)
-			return 0;
-		// DOS DoAirLock: play airlock animation
-		{
-			if (!loadAnimation("airlock"))
-				return 0;
-			_animationResult = 0;
-			_doorOpen = false;
-			setObjectState(2, 1); // airlock starts closed
-			setObjectState(1, 1);
-			playAnimation();
-			if (_animationResult) {
-				setDoorState(fromX, fromY, direction, 0);
-				return 1;
-			}
-			return 0;
-		}
-
-	case kWallFeatureUpStairs:
-	case kWallFeatureDnStairs:
-	case kWallFeatureTunnel: {
-		if (pobject != &_me)
-			return 0; // robots don't use stairs/tunnels
-
-		// DOS GoTo(): mapdata[2]=level, [3]=xcell, [4]=ycell
-		// Must copy values BEFORE loadMap, which overwrites _mapData
-		const int targetMap = map[2];
-		const int targetX = map[3];
-		const int targetY = map[4];
-
-		if (targetMap == 0 && targetX == 0 && targetY == 0)
-			return 1; // no destination data  just pass through
-
-		// Special cases from DOS
-		if (targetMap == 100) {
-			doText(78, 0); // Dimension error
-			return 0;
-		}
-
-		// Play appropriate sound
-		if (map[0] == kWallFeatureDnStairs)
-			_sound->play(Sound::kClatter);
-
-		// Move player to target position (preserve sub-cell offset like DOS)
-		if (targetX > 0 && targetX < 31 && targetY > 0 && targetY < 31) {
-			const int xmod = pobject->xloc - (pobject->xindex << 8);
-			const int ymod = pobject->yloc - (pobject->yindex << 8);
-			_robotArray[pobject->xindex][pobject->yindex] = 0;
-			pobject->xloc = (targetX << 8) + xmod;
-			pobject->xindex = targetX;
-			pobject->yloc = (targetY << 8) + ymod;
-			pobject->yindex = targetY;
-		}
-
-		// Load new map if target level specified
-		if (targetMap > 0 && targetMap != _level) {
-			loadMap(targetMap);
-			// DOS: coreindex depends on level
-			_coreIndex = (targetMap == 1) ? 0 : 1;
-		}
-
-		if (pobject->xindex >= 0 && pobject->xindex < 32 &&
-		    pobject->yindex >= 0 && pobject->yindex < 32)
-			_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
-
-		debug("Level change via %s: level=%d pos=(%d,%d)",
-		      map[0] == kWallFeatureUpStairs ? "upstairs" :
-		      map[0] == kWallFeatureDnStairs ? "downstairs" : "tunnel",
-		      _level, pobject->xindex, pobject->yindex);
-		return 2; // teleported
-	}
-
-	case kWallFeatureElevator: {
-		if (pobject != &_me)
-			return 0;
-		if (_corePower[1] == 0) {
-			inform("ELEVATOR HAS NO POWER.", true);
-			return 0;
-		}
-
-		// DOS DoElevator: play elevator animation with floor selection
-		if (!loadAnimation("elev"))
-			return 0;
-		_animationResult = 0;
-		_doorOpen = false;
-		_elevatorFloor = _level - 1; // DOS: fl = level-1
-		setObjectOnOff(6, false);
-		setObjectOnOff(7, false);
-		setObjectOnOff(8, false);
-		setObjectOnOff(9, false);
-		setObjectOnOff(10, false);
-		playAnimation();
-
-		bool entered = (_animationResult >= 2);
-		if (entered && _elevatorFloor + 1 != _level) {
-			// Player selected a different floor
-			int targetMap = _elevatorFloor + 1;
-			const int targetX = map[3];
-			const int targetY = map[4];
-
-			if (targetX > 0 && targetX < 31 && targetY > 0 && targetY < 31) {
-				_robotArray[pobject->xindex][pobject->yindex] = 0;
-				pobject->xloc = (targetX << 8) + 128;
-				pobject->xindex = targetX;
-				pobject->yloc = (targetY << 8) + 128;
-				pobject->yindex = targetY;
-				pobject->ang += 128;
-				pobject->look = pobject->ang;
-			}
-
-			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)
-				_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
-
-			debug("Elevator: level=%d pos=(%d,%d)", _level, pobject->xindex, pobject->yindex);
-			return 2; // teleported
-		}
-		// Player entered but stayed on same floor  turn around
-		if (entered) {
-			pobject->ang += 128;
-			pobject->look = pobject->ang;
-		}
-		return 0;
-	}
-
-	default:
-		return 0;
-	}
-}
-
-void ColonyEngine::interactWithObject(int objNum) {
-	if (objNum <= 0 || objNum > (int)_objects.size())
-		return;
-
-	const Thing &obj = _objects[objNum - 1];
-	if (!obj.alive)
-		return;
-
-	const int x = CLIP<int>(obj.where.xindex, 0, 30);
-	const int y = CLIP<int>(obj.where.yindex, 0, 30);
-	_action0 = _mapData[x][y][4][3];
-	_action1 = _mapData[x][y][4][4];
-	_creature = 1;
-
-	switch (obj.type) {
-	case kObjDesk:
-		if (loadAnimation("desk"))
-			playAnimation();
-		break;
-	case kObjConsole:
-		switch (_action0) {
-		case 1: // Reactor console
-			if (loadAnimation("reactor"))
-				playAnimation();
-			break;
-		case 2: // Main ship controls
-			if (loadAnimation("controls"))
-				playAnimation();
-			break;
-		case 3: // Security console
-			if (loadAnimation("security"))
-				playAnimation();
-			break;
-		default:
-			inform("IT DOES NOT SEEM TO BE WORKING.", true);
-			break;
-		}
-		break;
-	case kObjProjector:
-		switch (_action0) {
-		case 1:
-			if (loadAnimation("slides"))
-				playAnimation();
-			break;
-		case 2:
-			if (loadAnimation("teleshow")) // "teleshow" matches original interaction
-				playAnimation();
-			break;
-		default:
-			inform("PROJECTOR OFFLINE", true);
-			break;
-		}
-		break;
-	case kObjPowerSuit:
-		if (loadAnimation("suit"))
-			playAnimation();
-		break;
-	case kObjTeleport:
-	{
-		if (_fl == 1) {
-			// In empty forklift  pick up the teleporter itself
-			if (loadAnimation("lift")) {
-				_animationResult = 0;
-				playAnimation();
-				if (_animationResult) {
-					PassPatch from;
-					from.level = _level;
-					from.xindex = obj.where.xindex;
-					from.yindex = obj.where.yindex;
-					from.xloc = obj.where.xloc;
-					from.yloc = obj.where.yloc;
-					from.ang = obj.where.ang;
-					_carryPatch[1].level = 100;
-					_carryPatch[1].xindex = 1;
-					_carryPatch[1].yindex = 1;
-					_carryPatch[1].xloc = 1;
-					_carryPatch[1].yloc = 1;
-					_carryPatch[1].ang = 1;
-					newPatch(kObjTeleport, from, _carryPatch[1], _mapData[from.xindex][from.yindex][4]);
-					_carryType = kObjTeleport;
-					_robotArray[obj.where.xindex][obj.where.yindex] = 0;
-					_objects[objNum - 1].alive = 0;
-					_fl = 2;
-				}
-			}
-			break;
-		}
-		// fl==0 or fl==2: use the teleporter
-		_sound->play(Sound::kTeleport);
-		int targetLevelRaw = _mapData[x][y][4][2];
-		int targetX = _action0;
-		int targetY = _action1;
-		// Check if this teleporter was relocated via patch
-		PassPatch tp;
-		tp.level = _level;
-		tp.xindex = x;
-		tp.yindex = y;
-		uint8 patchData[5];
-		if (patchMapTo(tp, patchData)) {
-			targetLevelRaw = patchData[2];
-			targetX = patchData[3];
-			targetY = patchData[4];
-		}
-		const int targetLevel = (targetLevelRaw == 0) ? _level : targetLevelRaw;
-		if (targetLevel >= 100 || targetX <= 0 || targetX >= 31 || targetY <= 0 || targetY >= 31) {
-			inform("TELEPORTER INITIALIZATION FAILED", true);
-			break;
-		}
-		if (targetLevel != _level)
-			loadMap(targetLevel);
-		const int oldX = _me.xindex;
-		const int oldY = _me.yindex;
-		_me.xindex = targetX;
-		_me.yindex = targetY;
-		_me.xloc = (targetX << 8) + 128;
-		_me.yloc = (targetY << 8) + 128;
-		_me.ang = _me.look;
-		if (oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32)
-			_robotArray[oldX][oldY] = 0;
-		if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-			_robotArray[_me.xindex][_me.yindex] = kMeNum;
-		break;
-	}
-	case kObjDrawer:
-		if (loadAnimation("vanity"))
-			playAnimation();
-		break;
-	case kObjScreen:
-		// original game shows "Full of stars" effect/text
-		_sound->play(Sound::kStars1);
-		inform("I CAN SEE THROUGH IT...", true);
-		break;
-
-	case kObjToilet:
-	case kObjPToilet:
-		_sound->play(Sound::kToilet);
-		inform("IT'S A TOILET.", true); 
-		break;
-	case kObjTub:
-		_sound->play(Sound::kBath);
-		inform("A BATHTUB. NO TIME FOR A SOAK.", true);
-		break;
-
-	case kObjSink:
-		_sound->play(Sound::kSink);
-		inform("A SINK. IT'S DRY.", true);
-		break;
-	case kObjTV:
-		if (_level == 1)
-			doText(56, 0);
-		else
-			doText(16, 0);
-		break;
-	case kObjForkLift:
-		if (_fl == 0) {
-			// Enter forklift: play animation, if confirmed, patch it away
-			if (loadAnimation("forklift")) {
-				playAnimation();
-				if (_animationResult) {
-					PassPatch from;
-					from.level = _level;
-					from.xindex = obj.where.xindex;
-					from.yindex = obj.where.yindex;
-					from.xloc = obj.where.xloc;
-					from.yloc = obj.where.yloc;
-					from.ang = obj.where.ang;
-					_carryPatch[0].level = 100;
-					_carryPatch[0].xindex = 0;
-					_carryPatch[0].yindex = 0;
-					_carryPatch[0].xloc = 0;
-					_carryPatch[0].yloc = 0;
-					_carryPatch[0].ang = 0;
-					newPatch(kObjForkLift, from, _carryPatch[0], _mapData[from.xindex][from.yindex][4]);
-					_robotArray[obj.where.xindex][obj.where.yindex] = 0;
-					_objects[objNum - 1].alive = 0;
-					_me.look = _me.ang = obj.where.ang;
-					_fl = 1;
-				}
-			}
-		}
-		break;
-	case kObjBox1:
-	case kObjBox2:
-	case kObjCryo:
-		if (_fl == 1) {
-			// In empty forklift  pick up object
-			if (loadAnimation("lift")) {
-				_animationResult = 0;
-				playAnimation();
-				if (_animationResult) {
-					PassPatch from;
-					from.level = _level;
-					from.xindex = obj.where.xindex;
-					from.yindex = obj.where.yindex;
-					from.xloc = obj.where.xloc;
-					from.yloc = obj.where.yloc;
-					from.ang = obj.where.ang;
-					_carryPatch[1].level = 100;
-					_carryPatch[1].xindex = 1;
-					_carryPatch[1].yindex = 1;
-					_carryPatch[1].xloc = 1;
-					_carryPatch[1].yloc = 1;
-					_carryPatch[1].ang = 1;
-					newPatch(obj.type, from, _carryPatch[1], _mapData[from.xindex][from.yindex][4]);
-					_carryType = obj.type;
-					_robotArray[obj.where.xindex][obj.where.yindex] = 0;
-					_objects[objNum - 1].alive = 0;
-					_fl = 2;
-				}
-			}
-		} else if (_fl == 0 && obj.type == kObjCryo) {
-			// Not in forklift  read cryo text
-			doText(_action0, 0);
-		}
-		break;
-	case kObjReactor:
-		if (_fl == 1 && _coreState[_coreIndex] == 1) {
-			// Empty forklift at open reactor  pick up reactor core
-			if (loadAnimation("lift")) {
-				_animationResult = 0;
-				playAnimation();
-				if (_animationResult) {
-					_carryType = kObjReactor;
-					_corePower[2] = _corePower[_coreIndex];
-					_corePower[_coreIndex] = 0;
-					_coreState[_coreIndex] = 2;
-					_fl = 2;
-				}
-			}
-		} else if (_fl == 2 && _carryType == kObjReactor && _coreState[_coreIndex] == 2) {
-			// Carrying reactor core  drop it into reactor
-			if (loadAnimation("lift")) {
-				_animationResult = 0;
-				playAnimation();
-				if (!_animationResult) {
-					_carryType = 0;
-					_coreState[_coreIndex] = 1;
-					_corePower[_coreIndex] = _corePower[2];
-					_fl = 1;
-				}
-			}
-		}
-		break;
-	default:
-		break;
-	}
-}
-
 void ColonyEngine::inform(const char *text, bool hold) {
 	const char *msg[3];
 	msg[0] = text;
@@ -1260,7 +602,7 @@ void ColonyEngine::printMessage(const char *text[], bool hold) {
 	int numLines = 0;
 	int width = 0;
 	Graphics::DosFont font;
-	
+
 	while (text[numLines] != nullptr) {
 		int w = font.getStringWidth(text[numLines]);
 		if (w > width)
@@ -1385,7 +727,7 @@ void ColonyEngine::doText(int entry, int center) {
 	r.right = _centerX + (width / 2);
 
 	_gfx->fillDitherRect(_screenR, 0, 15);
-	
+
 	// Draw shadow/border (original draws 3 frames total)
 	for (int i = 0; i < 2; i++) {
 		_gfx->drawRect(r, 0);
@@ -1411,242 +753,6 @@ void ColonyEngine::doText(int entry, int center) {
 	free(page);
 }
 
-void ColonyEngine::fallThroughHole() {
-	// DOS tunnel(TRUE, mapdata) + GoTo(mapdata)
-	// Called when player falls through a floor hole (SMHOLEFLR or LGHOLEFLR)
-	const uint8 *mapdata = _mapData[_me.xindex][_me.yindex][4];
-	int targetMap = mapdata[2];
-	int targetX = mapdata[3];
-	int targetY = mapdata[4];
-
-	if (targetMap == 0 && targetX == 0 && targetY == 0) {
-		terminateGame(false); // you're dead
-		return;
-	}
-
-	// DOS tunnel(TRUE,...): power damage from falling
-	int damage = -(_level << 7);
-	for (int i = 0; i < 3; i++)
-		_corePower[i] += damage;
-
-	_sound->play(Sound::kClatter);
-
-	// DOS tunnel(pt=TRUE): falling animation  nested rectangles shrinking toward
-	// center, simulating falling down a shaft. White outlines on black background.
-	// DOS runs 10 steps × 2 frames = 20 display frames at ~15fps = ~1.3 seconds.
-	// At 60fps we use 80 frames for the same duration, paced by the frame limiter.
-	{
-		const int cx = (_screenR.left + _screenR.right) / 2;
-		const int cy = (_screenR.top + _screenR.bottom) / 2;
-		const int hw = _screenR.width() / 2;
-		const int hh = _screenR.height() / 2;
-		const int totalFrames = 80;
-		const int maxRings = 10;
-
-		for (int frame = 0; frame < totalFrames && !shouldQuit(); frame++) {
-			_frameLimiter->startFrame();
-
-			_gfx->fillRect(_screenR, 0); // black background
-
-			float progress = (float)frame / totalFrames;
-
-			// Draw nested rectangles  outer ring shrinks in, inner rings follow
-			// The number of visible rings decreases as we fall deeper
-			int visibleRings = maxRings - (int)(progress * (maxRings - 1));
-			for (int ring = 0; ring < visibleRings; ring++) {
-				// Each ring's depth combines the overall fall progress with per-ring spacing
-				float depth = progress * 0.6f + (float)ring / (maxRings + 2.0f);
-				if (depth >= 1.0f)
-					break;
-				float scale = 1.0f - depth;
-				int rw = (int)(hw * scale);
-				int rh = (int)(hh * scale);
-				if (rw < 2 || rh < 2)
-					break;
-				Common::Rect r(cx - rw, cy - rh, cx + rw, cy + rh);
-				_gfx->drawRect(r, 15); // white outline
-			}
-
-			_frameLimiter->delayBeforeSwap();
-			_gfx->copyToScreen();
-		}
-	}
-
-	// DOS GoTo(): preserve sub-cell offset, move to destination
-	if (targetX > 0 && targetY > 0) {
-		// Don't go if destination is occupied on same level (DOS: (!map) && robotarray check)
-		if (targetMap == 0 && _robotArray[targetX][targetY] != 0)
-			return;
-
-		_robotArray[_me.xindex][_me.yindex] = 0;
-
-		// Preserve sub-cell offset (DOS: xmod = xloc - (xindex<<8))
-		int xmod = _me.xloc - (_me.xindex << 8);
-		int ymod = _me.yloc - (_me.yindex << 8);
-		_me.xloc = (targetX << 8) + xmod;
-		_me.xindex = targetX;
-		_me.yloc = (targetY << 8) + ymod;
-		_me.yindex = targetY;
-
-		_robotArray[targetX][targetY] = kMeNum;
-	}
-
-	// DOS: if(map) load_mapnum(map, TRUE)  always reload when map != 0
-	if (targetMap > 0)
-		loadMap(targetMap);
-
-	debug("Fell through hole: level=%d pos=(%d,%d)", _level, _me.xindex, _me.yindex);
-}
-
-void ColonyEngine::checkCenter() {
-	// DOS CCenter(): check if player is standing on a floor hole or hotfoot
-	if (_me.xindex < 0 || _me.xindex >= 31 || _me.yindex < 0 || _me.yindex >= 31)
-		return;
-
-	const uint8 cellType = _mapData[_me.xindex][_me.yindex][4][0];
-	if (cellType == 0)
-		return;
-
-	switch (cellType) {
-	case 1: { // SMHOLEFLR  small floor hole, must be near center
-		// DOS: xcheck=abs(xloc-(xindex<<8)); if(xcheck>64&&xcheck<192)
-		int xcheck = ABS(_me.xloc - (_me.xindex << 8));
-		int ycheck = ABS(_me.yloc - (_me.yindex << 8));
-		if (xcheck > 64 && xcheck < 192 && ycheck > 64 && ycheck < 192)
-			fallThroughHole();
-		break;
-	}
-	case 2: // LGHOLEFLR  large floor hole, full cell
-		fallThroughHole();
-		break;
-	case 5: // HOTFOOT  electric floor, damages power
-		// DOS: SetPower(-(5<<level),-(5<<level),-(5<<level))
-		for (int i = 0; i < 3; i++)
-			_corePower[i] -= (5 << _level);
-		break;
-	default:
-		break;
-	}
-}
-
-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 robot = checkwall(xnew, ynew, &_me);
-	if (robot > 0 && allowInteraction)
-		interactWithObject(robot);
-
-	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-		_robotArray[_me.xindex][_me.yindex] = kMeNum;
-}
-
-// DOS ExitFL(): step back one cell and drop the forklift.
-void ColonyEngine::exitForklift() {
-	if (_fl != 1)
-		return;
-
-	int xloc = _me.xloc;
-	int yloc = _me.yloc;
-	int xindex = _me.xindex;
-	int yindex = _me.yindex;
-
-	// Walk backward until we move into a different cell
-	while (_me.xindex == xindex && _me.yindex == yindex) {
-		int xnew = _me.xloc - _cost[_me.ang];
-		int ynew = _me.yloc - _sint[_me.ang];
-		_me.type = 2; // temporary small collision type
-		if (checkwall(xnew, ynew, &_me)) {
-			_sound->play(Sound::kChime);
-			_me.type = kMeNum;
-			return;
-		}
-		_me.type = kMeNum;
-	}
-
-	// Snap to cell center for the dropped forklift
-	xloc = (xloc >> 8);
-	xloc = (xloc << 8) + 128;
-	yloc = (yloc >> 8);
-	yloc = (yloc << 8) + 128;
-
-	createObject(kObjForkLift, xloc, yloc, _me.ang);
-
-	PassPatch to;
-	to.level = _level;
-	to.xindex = xindex;
-	to.yindex = yindex;
-	to.xloc = xloc;
-	to.yloc = yloc;
-	to.ang = _me.ang;
-	newPatch(kObjForkLift, _carryPatch[0], to, nullptr);
-
-	_fl = 0;
-}
-
-// DOS DropFL(): step back one cell and drop the carried object, then return to fl=1.
-void ColonyEngine::dropCarriedObject() {
-	if (_fl != 2)
-		return;
-
-	// Special case: carrying reactor core
-	if (_carryType == kObjReactor) {
-		_sound->play(Sound::kChime); // glass break sound
-		_carryType = 0;
-		_fl = 1;
-		return;
-	}
-
-	// Play the drop animation
-	if (loadAnimation("lift")) {
-		_animationResult = 0;
-		playAnimation();
-		if (!_animationResult) {
-			// Animation was cancelled  don't drop
-			return;
-		}
-	}
-
-	int xloc = _me.xloc;
-	int yloc = _me.yloc;
-	int xindex = _me.xindex;
-	int yindex = _me.yindex;
-
-	// Walk backward until we move into a different cell
-	while (_me.xindex == xindex && _me.yindex == yindex) {
-		int xnew = _me.xloc - _cost[_me.ang];
-		int ynew = _me.yloc - _sint[_me.ang];
-		_me.type = 2;
-		if (checkwall(xnew, ynew, &_me)) {
-			_sound->play(Sound::kChime);
-			_me.type = kMeNum;
-			return;
-		}
-		_me.type = kMeNum;
-	}
-
-	// DOS: teleport always drops at ang=0; other objects use player's angle
-	uint8 ang = (_carryType == kObjTeleport) ? 0 : _me.ang;
-
-	xloc = (xloc >> 8);
-	xloc = (xloc << 8) + 128;
-	yloc = (yloc >> 8);
-	yloc = (yloc << 8) + 128;
-
-	createObject(_carryType, xloc, yloc, ang);
-
-	PassPatch to;
-	to.level = _level;
-	to.xindex = xindex;
-	to.yindex = yindex;
-	to.xloc = xloc;
-	to.yloc = yloc;
-	to.ang = _me.ang;
-	newPatch(_carryType, _carryPatch[1], to, nullptr);
-
-	_fl = 1;
-}
-
 bool ColonyEngine::clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const {
 	if (clip.left >= clip.right || clip.top >= clip.bottom)
 		return false;


Commit: 31e4f99a8ca6837cbcfcb7fd0498950eb7cddbdb
    https://github.com/scummvm/scummvm/commit/31e4f99a8ca6837cbcfcb7fd0498950eb7cddbdb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:32+02:00

Commit Message:
COLONY: refactored ColonyEngine::handleAnimationClick

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


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 7c855872d8c..364c5174aae 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -844,393 +844,431 @@ void ColonyEngine::handleAnimationClick(int item) {
 	}
 
 	if (_animationName == "desk") {
-		if (item >= 2 && item <= 5) {
-			int idx = item - 2;
-			uint8 *decode = (_level == 1) ? _decode2 : _decode3;
-			if (decode[idx] == 0) {
-				decode[idx] = (uint8)(2 + (_randomSource.getRandomNumber(3)));
-				_lSprites[item - 1]->current = decode[idx] - 1;
+		handleDeskClick(item);
+	} else if (_animationName == "vanity") {
+		handleVanityClick(item);
+	} else if (_animationName == "slides") {
+		handleSlidesClick(item);
+	} else if (_animationName == "teleshow") {
+		handleTeleshowClick(item);
+	} else if (_animationName == "reactor" || _animationName == "security") {
+		handleKeypadClick(item);
+	} else if (_animationName == "suit") {
+		handleSuitClick(item);
+	} else if (_animationName == "door" || _animationName == "bulkhead") {
+		handleDoorClick(item);
+	} else if (_animationName == "airlock") {
+		handleAirlockClick(item);
+	} else if (_animationName == "elev") {
+		handleElevatorClick(item);
+	} else if (_animationName == "controls") {
+		handleControlsClick(item);
+	}
+}
+
+void ColonyEngine::handleDeskClick(int item) {
+	if (item >= 2 && item <= 5) {
+		int idx = item - 2;
+		uint8 *decode = (_level == 1) ? _decode2 : _decode3;
+		if (decode[idx] == 0) {
+			decode[idx] = (uint8)(2 + (_randomSource.getRandomNumber(3)));
+			_lSprites[item - 1]->current = decode[idx] - 1;
+			drawAnimation();
+			_gfx->copyToScreen();
+		}
+	} else if (item == 7) { // Letter
+		if (_lSprites[6]->current > 0)
+			doText(_action1, 0);
+	} else if (item == 9) { // Clipboard
+		doText(_action1, 0);
+	} else if (item == 17) { // Screen
+		doText(_action0, 0);
+	} else if (item == 22) { // Book
+		doText(_action1, 0);
+	} else if (item == 24) { // Cigarette
+		doText(55, 0);
+		terminateGame(false);
+	} else if (item == 25) { // Post-it
+		doText(_action1, 0);
+	}
+}
+
+void ColonyEngine::handleVanityClick(int item) {
+	debug(0, "Vanity item %d clicked. Sprite type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d size=%d",
+		item,
+		(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->type : -1,
+		(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->frozen : -1,
+		(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->locked : -1,
+		(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->current : -1,
+		(item > 0 && item <= (int)_lSprites.size()) ? (int)_lSprites[item-1]->onoff : -1,
+		(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->key : -1,
+		(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->lock : -1,
+		(item > 0 && item <= (int)_lSprites.size()) ? (int)_lSprites[item-1]->objects.size() : -1);
+	if (item == 12) { // Coffee cup - spill animation
+		if (!_doorOpen) { // reuse _doorOpen as "spilled" flag
+			for (int i = 1; i < 6; i++) {
+				setObjectState(12, i);
 				drawAnimation();
 				_gfx->copyToScreen();
+				_system->delayMillis(50);
 			}
-		} else if (item == 7) { // Letter
-			if (_lSprites[6]->current > 0)
-				doText(_action1, 0);
-		} else if (item == 9) { // Clipboard
-			doText(_action1, 0);
-		} else if (item == 17) { // Screen
-			doText(_action0, 0);
-		} else if (item == 22) { // Book
-			doText(_action1, 0);
-		} else if (item == 24) { // Cigarette
-			doText(55, 0);
-			terminateGame(false);
-		} else if (item == 25) { // Post-it
-			doText(_action1, 0);
+			_doorOpen = true;
 		}
-	} else if (_animationName == "vanity") {
-		debug(0, "Vanity item %d clicked. Sprite type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d size=%d",
-			item,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->type : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->frozen : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->locked : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->current : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? (int)_lSprites[item-1]->onoff : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->key : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->lock : -1,
-			(item > 0 && item <= (int)_lSprites.size()) ? (int)_lSprites[item-1]->objects.size() : -1);
-		if (item == 12) { // Coffee cup - spill animation
-			if (!_doorOpen) { // reuse _doorOpen as "spilled" flag
-				for (int i = 1; i < 6; i++) {
-					setObjectState(12, i);
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(50);
-				}
-				_doorOpen = true;
+	} else if (item == 13) { // Paper
+		doText(_action0, 0);
+	} else if (item == 14) { // Badge
+		doText(80, 0);
+	} else if (item == 4) { // Diary
+		doText(_action0, 0);
+	} else if (item == 7) { // Book
+		doText(_action0, 0);
+	} else {
+		debug(0, "Vanity: unhandled item %d", item);
+	}
+}
+
+void ColonyEngine::handleSlidesClick(int item) {
+	if (item == 2) { // Speaker
+		doText(261 + _creature, 0);
+	} else if (item == 5) { // Prev
+		_creature--;
+		if (_creature == 0)
+			_creature = 8;
+		setObjectState(1, _creature);
+	} else if (item == 6) { // Next
+		_creature++;
+		if (_creature == 9)
+			_creature = 1;
+		setObjectState(1, _creature);
+	}
+}
+
+void ColonyEngine::handleTeleshowClick(int item) {
+	if (item == 2) { // Speaker
+		doText(269 + _creature, 0);
+	} else if (item == 5) { // Prev
+		_creature--;
+		if (_creature == 0)
+			_creature = 7;
+		setObjectState(1, _creature);
+	} else if (item == 6) { // Next
+		_creature++;
+		if (_creature == 8)
+			_creature = 1;
+		setObjectState(1, _creature);
+	}
+}
+
+void ColonyEngine::handleKeypadClick(int item) {
+	if (item >= 1 && item <= 10) {
+		for (int i = 5; i >= 1; i--)
+			_animDisplay[i] = _animDisplay[i - 1];
+		_animDisplay[0] = (uint8)(item + 1);
+		refreshAnimationDisplay();
+		drawAnimation();
+		_gfx->copyToScreen();
+		// Don't return, let dolSprite animate the button
+	} else if (item == 11) { // Clear
+		for (int i = 0; i < 6; i++)
+			_animDisplay[i] = 1;
+		// Reset keypad buttons to unpressed state
+		for (int i = 1; i <= 10; i++)
+			setObjectState(i, 1);
+		refreshAnimationDisplay();
+		drawAnimation();
+		_gfx->copyToScreen();
+	} else if (item == 12) { // Enter
+		uint8 testarray[6];
+		if (_animationName == "reactor") {
+			if (_level == 1)
+				crypt(testarray, _decode2[3] - 2, _decode2[2] - 2, _decode2[1] - 2, _decode2[0] - 2);
+			else
+				crypt(testarray, _decode3[3] - 2, _decode3[2] - 2, _decode3[1] - 2, _decode3[0] - 2);
+
+			bool match = true;
+			for (int i = 0; i < 6; i++) {
+				if (testarray[i] != _animDisplay[5 - i])
+					match = false;
 			}
-		} else if (item == 13) { // Paper
-			doText(_action0, 0);
-		} else if (item == 14) { // Badge
-			doText(80, 0);
-		} else if (item == 4) { // Diary
-			doText(_action0, 0);
-		} else if (item == 7) { // Book
-			doText(_action0, 0);
-		} else {
-			debug(0, "Vanity: unhandled item %d", item);
-		}
-	} else if (_animationName == "slides") {
-		if (item == 2) { // Speaker
-			doText(261 + _creature, 0);
-		} else if (item == 5) { // Prev
-			_creature--;
-			if (_creature == 0)
-				_creature = 8;
-			setObjectState(1, _creature);
-		} else if (item == 6) { // Next
-			_creature++;
-			if (_creature == 9)
-				_creature = 1;
-			setObjectState(1, _creature);
-		}
-	} else if (_animationName == "teleshow") {
-		if (item == 2) { // Speaker
-			doText(269 + _creature, 0);
-		} else if (item == 5) { // Prev
-			_creature--;
-			if (_creature == 0)
-				_creature = 7;
-			setObjectState(1, _creature);
-		} else if (item == 6) { // Next
-			_creature++;
-			if (_creature == 8)
-				_creature = 1;
-			setObjectState(1, _creature);
+			if (match) {
+				if (_coreState[_coreIndex] == 0)
+					_coreState[_coreIndex] = 1;
+				else if (_coreState[_coreIndex] == 1)
+					_coreState[_coreIndex] = 0;
+				_gametest = true;
+			}
+			_animationRunning = false;
+		} else if (_animationName == "security") {
+			crypt(testarray, _decode1[0] - 2, _decode1[1] - 2, _decode1[2] - 2, _decode1[3] - 2);
+			bool match = true;
+			for (int i = 0; i < 6; i++) {
+				if (testarray[i] != _animDisplay[5 - i])
+					match = false;
+			}
+			if (match) {
+				_unlocked = true;
+				_gametest = true;
+			}
+			_animationRunning = false;
 		}
-	} else if (_animationName == "reactor" || _animationName == "security" || _animationName == "suit") {
-		if (item >= 1 && item <= 10 && _animationName != "suit") {
-			for (int i = 5; i >= 1; i--)
-				_animDisplay[i] = _animDisplay[i - 1];
-			_animDisplay[0] = (uint8)(item + 1);
-			refreshAnimationDisplay();
+	}
+	if (item <= 12) {
+		// setObjectState(item, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
+		if (item > 10) // Clear/Enter should return to Off
+			setObjectState(item, 1);
+		drawAnimation();
+		_gfx->copyToScreen();
+	}
+}
+
+void ColonyEngine::handleSuitClick(int item) {
+	if (item == 1) { // Armor
+		if (_armor == 3) {
+			for (int i = 6; i >= 1; i--) {
+				setObjectState(1, i);
+				setObjectState(3, i / 2 + 1);
+				drawAnimation();
+				_gfx->copyToScreen();
+				_system->delayMillis(30);
+			}
+			_armor = 0;
+		} else {
+			setObjectState(1, (_armor * 2 + 1) + 1); // intermediate/pressed
 			drawAnimation();
 			_gfx->copyToScreen();
-			// Don't return, let dolSprite animate the button
-		} else if (item == 11 && _animationName != "suit") { // Clear
-			for (int i = 0; i < 6; i++)
-				_animDisplay[i] = 1;
-			// Reset keypad buttons to unpressed state
-			for (int i = 1; i <= 10; i++)
-				setObjectState(i, 1);
-			refreshAnimationDisplay();
+			_system->delayMillis(50);
+			_armor++;
+		}
+		setObjectState(1, _armor * 2 + 1); // target state
+		setObjectState(3, _armor + 1); // display state
+		drawAnimation();
+		_gfx->copyToScreen();
+		if (_armor == 3 && _weapons == 3)
+			_corePower[_coreIndex] = 2;
+	} else if (item == 2) { // Weapons
+		if (_weapons == 3) {
+			for (int i = 6; i >= 1; i--) {
+				setObjectState(2, i);
+				setObjectState(4, i / 2 + 1);
+				drawAnimation();
+				_gfx->copyToScreen();
+				_system->delayMillis(30);
+			}
+			_weapons = 0;
+		} else {
+			setObjectState(2, (_weapons * 2 + 1) + 1); // intermediate/pressed
 			drawAnimation();
 			_gfx->copyToScreen();
-		} else if (item == 12 && _animationName != "suit") { // Enter
-			uint8 testarray[6];
-			if (_animationName == "reactor") {
-				if (_level == 1)
-					crypt(testarray, _decode2[3] - 2, _decode2[2] - 2, _decode2[1] - 2, _decode2[0] - 2);
-				else
-					crypt(testarray, _decode3[3] - 2, _decode3[2] - 2, _decode3[1] - 2, _decode3[0] - 2);
+			_system->delayMillis(50);
+			_weapons++;
+		}
+		setObjectState(2, _weapons * 2 + 1);
+		setObjectState(4, _weapons + 1);
+		drawAnimation();
+		_gfx->copyToScreen();
+		if (_armor == 3 && _weapons == 3)
+			_corePower[_coreIndex] = 2;
+	}
+}
 
-				bool match = true;
-				for (int i = 0; i < 6; i++) {
-					if (testarray[i] != _animDisplay[5 - i])
-						match = false;
-				}
-				if (match) {
-					if (_coreState[_coreIndex] == 0)
-						_coreState[_coreIndex] = 1;
-					else if (_coreState[_coreIndex] == 1)
-						_coreState[_coreIndex] = 0;
-					_gametest = true;
-				}
-				_animationRunning = false;
-			} else if (_animationName == "security") { // security
-				crypt(testarray, _decode1[0] - 2, _decode1[1] - 2, _decode1[2] - 2, _decode1[3] - 2);
-				bool match = true;
-				for (int i = 0; i < 6; i++) {
-					if (testarray[i] != _animDisplay[5 - i])
-						match = false;
-				}
-				if (match) {
-					_unlocked = true;
-					_gametest = true;
-				}
-				_animationRunning = false;
-			}
-		} else if (_animationName == "suit") {
-			if (item == 1) { // Armor
-				if (_armor == 3) {
-					for (int i = 6; i >= 1; i--) {
-						setObjectState(1, i);
-						setObjectState(3, i / 2 + 1);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(30);
-					}
-					_armor = 0;
-				} else {
-					setObjectState(1, (_armor * 2 + 1) + 1); // intermediate/pressed
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(50);
-					_armor++;
-				}
-				setObjectState(1, _armor * 2 + 1); // target state
-				setObjectState(3, _armor + 1); // display state
+void ColonyEngine::handleDoorClick(int item) {
+	// DOS DoDoor: item==3 toggles door open/close, item==1 or (item==101 && door open) exits
+	if (item == 3) {
+		_sound->play(Sound::kDoor);
+		if (_doorOpen) {
+			for (int i = 3; i >= 1; i--) {
+				_doorOpen = !_doorOpen;
+				setObjectState(2, i);
 				drawAnimation();
 				_gfx->copyToScreen();
-				if (_armor == 3 && _weapons == 3)
-					_corePower[_coreIndex] = 2;
-			} else if (item == 2) { // Weapons
-				if (_weapons == 3) {
-					for (int i = 6; i >= 1; i--) {
-						setObjectState(2, i);
-						setObjectState(4, i / 2 + 1);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(30);
-					}
-					_weapons = 0;
-				} else {
-					setObjectState(2, (_weapons * 2 + 1) + 1); // intermediate/pressed
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(50);
-					_weapons++;
-				}
-				setObjectState(2, _weapons * 2 + 1);
-				setObjectState(4, _weapons + 1);
+				_system->delayMillis(80);
+			}
+		} else {
+			for (int i = 1; i < 4; i++) {
+				_doorOpen = !_doorOpen;
+				setObjectState(2, i);
 				drawAnimation();
 				_gfx->copyToScreen();
-				if (_armor == 3 && _weapons == 3)
-					_corePower[_coreIndex] = 2;
+				_system->delayMillis(80);
 			}
 		}
-		if (_animationName == "reactor" || _animationName == "security") {
-			if (item <= 12) {
-				// setObjectState(item, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
-				if (item > 10) // Clear/Enter should return to Off
-					setObjectState(item, 1);
+	}
+	if (item == 1 || (item == 101 && objectState(2) == 3)) {
+		_animationResult = 1;
+		_animationRunning = false;
+	}
+}
+
+void ColonyEngine::handleAirlockClick(int item) {
+	// DOS DoAirLock: item==1 toggles airlock if power on && unlocked
+	// item==2 or (item==101 && airlock open) exits with pass-through
+	if ((item == 2 || item == 101) && _doorOpen) {
+		_animationResult = 1;
+		_animationRunning = false;
+	} else if (item == 1 && _corePower[_coreIndex] && _unlocked) {
+		_sound->play(Sound::kAirlock);
+		if (_doorOpen) {
+			for (int i = 3; i >= 1; i--) {
+				setObjectState(2, i);
 				drawAnimation();
 				_gfx->copyToScreen();
+				_system->delayMillis(80);
+			}
+		} else {
+			for (int i = 1; i < 4; i++) {
+				setObjectState(2, i);
+				drawAnimation();
+				_gfx->copyToScreen();
+				_system->delayMillis(80);
 			}
 		}
-	} else if (_animationName == "door" || _animationName == "bulkhead") {
-		// DOS DoDoor: item==3 toggles door open/close, item==1 or (item==101 && door open) exits
-		if (item == 3) {
-			_sound->play(Sound::kDoor);
-			if (_doorOpen) {
-				for (int i = 3; i >= 1; i--) {
-					_doorOpen = !_doorOpen;
-					setObjectState(2, i);
+		_doorOpen = !_doorOpen;
+	} else if (item == 101 && !_doorOpen) {
+		// Exit without opening
+		_animationRunning = false;
+	}
+}
+
+void ColonyEngine::handleElevatorClick(int item) {
+	// DOS DoElevator: two phases
+	// _doorOpen=false: Phase 1 (outside) - item==5 toggles doors
+	// _doorOpen=true: Phase 2 (inside) - items 6-10 select floor
+	// _animationResult tracks: 0=outside, 1=doors open, 2=inside
+	if (_animationResult < 2) {
+		// Phase 1: outside the elevator
+		if (item == 5) {
+			_sound->play(Sound::kElevator);
+			if (!_doorOpen) {
+				for (int i = 1; i < 4; i++) {
+					setObjectState(3, i);
+					setObjectState(4, i);
 					drawAnimation();
 					_gfx->copyToScreen();
 					_system->delayMillis(80);
 				}
+				_doorOpen = true;
 			} else {
-				for (int i = 1; i < 4; i++) {
-					_doorOpen = !_doorOpen;
-					setObjectState(2, i);
+				for (int i = 3; i >= 1; i--) {
+					setObjectState(4, i);
+					setObjectState(3, i);
 					drawAnimation();
 					_gfx->copyToScreen();
 					_system->delayMillis(80);
 				}
+				_doorOpen = false;
 			}
-		}
-		if (item == 1 || (item == 101 && objectState(2) == 3)) {
-			_animationResult = 1;
+		} else if (item == 2 || (item == 101 && _doorOpen)) {
+			// Enter the elevator (transition to phase 2)
+			_animationResult = 2;
+			setObjectOnOff(6, true);
+			setObjectOnOff(7, true);
+			setObjectOnOff(8, true);
+			setObjectOnOff(9, true);
+			setObjectOnOff(10, true);
+			setObjectOnOff(2, false);
+			setObjectOnOff(5, false);
+			drawAnimation();
+			_gfx->copyToScreen();
+		} else if (item == 101 && !_doorOpen) {
+			// Exit without entering
+			_animationResult = 0;
 			_animationRunning = false;
 		}
-	} else if (_animationName == "airlock") {
-		// DOS DoAirLock: item==1 toggles airlock if power on && unlocked
-		// item==2 or (item==101 && airlock open) exits with pass-through
-		if ((item == 2 || item == 101) && _doorOpen) {
-			_animationResult = 1;
-			_animationRunning = false;
-		} else if (item == 1 && _corePower[_coreIndex] && _unlocked) {
-			_sound->play(Sound::kAirlock);
-			if (_doorOpen) {
+	} else {
+		// Phase 2: inside  floor selection
+		if (item >= 6 && item <= 10) {
+			int fl = item - 5;
+			if (fl == _elevatorFloor) {
+				setObjectState(item, 1); // already on this floor
+			} else {
+				_sound->play(Sound::kElevator);
 				for (int i = 3; i >= 1; i--) {
-					setObjectState(2, i);
+					setObjectState(4, i);
+					setObjectState(3, i);
 					drawAnimation();
 					_gfx->copyToScreen();
 					_system->delayMillis(80);
 				}
-			} else {
-				for (int i = 1; i < 4; i++) {
-					setObjectState(2, i);
+				_elevatorFloor = fl;
+				for (int i = 1; i <= 3; i++) {
+					setObjectState(4, i);
+					setObjectState(3, i);
 					drawAnimation();
 					_gfx->copyToScreen();
 					_system->delayMillis(80);
 				}
+				setObjectState(item, 1);
 			}
-			_doorOpen = !_doorOpen;
-		} else if (item == 101 && !_doorOpen) {
-			// Exit without opening
+		} else if (item == 1 || item == 101) {
+			// Exit elevator
 			_animationRunning = false;
 		}
-	} else if (_animationName == "elev") {
-		// DOS DoElevator: two phases
-		// _doorOpen=false: Phase 1 (outside) - item==5 toggles doors
-		// _doorOpen=true: Phase 2 (inside) - items 6-10 select floor
-		// _animationResult tracks: 0=outside, 1=doors open, 2=inside
-		if (_animationResult < 2) {
-			// Phase 1: outside the elevator
-			if (item == 5) {
-				_sound->play(Sound::kElevator);
-				if (!_doorOpen) {
-					for (int i = 1; i < 4; i++) {
-						setObjectState(3, i);
-						setObjectState(4, i);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(80);
-					}
-					_doorOpen = true;
-				} else {
-					for (int i = 3; i >= 1; i--) {
-						setObjectState(4, i);
-						setObjectState(3, i);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(80);
-					}
-					_doorOpen = false;
-				}
-			} else if (item == 2 || (item == 101 && _doorOpen)) {
-				// Enter the elevator (transition to phase 2)
-				_animationResult = 2;
-				setObjectOnOff(6, true);
-				setObjectOnOff(7, true);
-				setObjectOnOff(8, true);
-				setObjectOnOff(9, true);
-				setObjectOnOff(10, true);
-				setObjectOnOff(2, false);
-				setObjectOnOff(5, false);
+	}
+}
+
+void ColonyEngine::handleControlsClick(int item) {
+	switch (item) {
+	case 4: // Accelerator
+		if (_corePower[_coreIndex] >= 2 && _coreState[_coreIndex] == 0 && !_orbit) {
+			_orbit = 1;
+			debug(0, "Taking off!");
+			// Animate the lever moving full range
+			for (int i = 1; i <= 6; i++) {
+				setObjectState(4, i);
 				drawAnimation();
 				_gfx->copyToScreen();
-			} else if (item == 101 && !_doorOpen) {
-				// Exit without entering
-				_animationResult = 0;
-				_animationRunning = false;
+				_system->delayMillis(30);
 			}
+			_animationRunning = false;
+			return; // Exit animation immediately on success
 		} else {
-			// Phase 2: inside  floor selection
-			if (item >= 6 && item <= 10) {
-				int fl = item - 5;
-				if (fl == _elevatorFloor) {
-					setObjectState(item, 1); // already on this floor
-				} else {
-					_sound->play(Sound::kElevator);
-					for (int i = 3; i >= 1; i--) {
-						setObjectState(4, i);
-						setObjectState(3, i);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(80);
-					}
-					_elevatorFloor = fl;
-					for (int i = 1; i <= 3; i++) {
-						setObjectState(4, i);
-						setObjectState(3, i);
-						drawAnimation();
-						_gfx->copyToScreen();
-						_system->delayMillis(80);
-					}
-					setObjectState(item, 1);
-				}
-			} else if (item == 1 || item == 101) {
-				// Exit elevator
-				_animationRunning = false;
+			debug(0, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
+			// Fail animation click
+			setObjectState(4, 1);
+			// dolSprite(3); // Animate lever moving and returning - handled by top dolSprite
+			for (int i = 6; i > 0; i--) {
+				setObjectState(4, i);
+				drawAnimation();
+				_gfx->copyToScreen();
+				_system->delayMillis(20);
 			}
 		}
-	} else if (_animationName == "controls") {
-		switch (item) {
-		case 4: // Accelerator
-			if (_corePower[_coreIndex] >= 2 && _coreState[_coreIndex] == 0 && !_orbit) {
-				_orbit = 1;
-				debug(0, "Taking off!");
-				// Animate the lever moving full range
-				for (int i = 1; i <= 6; i++) {
-					setObjectState(4, i);
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(30);
-				}
-				_animationRunning = false;
-				return; // Exit animation immediately on success
-			} else {
-				debug(0, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
-				// Fail animation click
-				setObjectState(4, 1);
-				// dolSprite(3); // Animate lever moving and returning - handled by top dolSprite
-				for (int i = 6; i > 0; i--) {
-					setObjectState(4, i);
-					drawAnimation();
-					_gfx->copyToScreen();
-					_system->delayMillis(20);
-				}
-			}
-			break;
-		case 5: // Emergency power
-			// setObjectState(5, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
-			// dolSprite(4); // Animate the button press - handled by top dolSprite
-			if (_coreState[_coreIndex] < 2) {
-				if (_corePower[_coreIndex] == 0)
-					_corePower[_coreIndex] = 1;
-				else if (_corePower[_coreIndex] == 1)
-					_corePower[_coreIndex] = 0;
-			}
-			// Finalize visual state according to power settings
-			switch (_corePower[_coreIndex]) {
-			case 0: setObjectState(2, 1); setObjectState(5, 1); break;
-			case 1: setObjectState(2, 1); setObjectState(5, 2); break;
-			case 2: setObjectState(2, 2); setObjectState(5, 1); break;
-			}
-			drawAnimation();
-			_gfx->copyToScreen();
-			break;
-		case 7: // Damage report
-		{
-			// dolSprite(6); // Button animation - handled by top dolSprite
-			if (_corePower[_coreIndex] < 2) {
-				doText(15, 0); // Critical status
-			} else if (!_orbit) {
-				doText(49, 0); // Ready for liftoff
-			} else {
-				doText(66, 0); // Orbital stabilization
-			}
-
-			setObjectState(7, 1); // Reset button
-			drawAnimation();
-			_gfx->copyToScreen();
-			break;
+		break;
+	case 5: // Emergency power
+		// setObjectState(5, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
+		// dolSprite(4); // Animate the button press - handled by top dolSprite
+		if (_coreState[_coreIndex] < 2) {
+			if (_corePower[_coreIndex] == 0)
+				_corePower[_coreIndex] = 1;
+			else if (_corePower[_coreIndex] == 1)
+				_corePower[_coreIndex] = 0;
 		}
-			break;
+		// Finalize visual state according to power settings
+		switch (_corePower[_coreIndex]) {
+		case 0: setObjectState(2, 1); setObjectState(5, 1); break;
+		case 1: setObjectState(2, 1); setObjectState(5, 2); break;
+		case 2: setObjectState(2, 2); setObjectState(5, 1); break;
+		}
+		drawAnimation();
+		_gfx->copyToScreen();
+		break;
+	case 7: // Damage report
+	{
+		// dolSprite(6); // Button animation - handled by top dolSprite
+		if (_corePower[_coreIndex] < 2) {
+			doText(15, 0); // Critical status
+		} else if (!_orbit) {
+			doText(49, 0); // Ready for liftoff
+		} else {
+			doText(66, 0); // Orbital stabilization
 		}
+
+		setObjectState(7, 1); // Reset button
+		drawAnimation();
+		_gfx->copyToScreen();
+		break;
+	}
+		break;
 	}
 }
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 10ae5f4b552..e6db4910ce6 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -557,6 +557,16 @@ private:
 	uint32 readUint32(Common::SeekableReadStream &s);
 	int whichSprite(const Common::Point &p);
 	void handleAnimationClick(int item);
+	void handleDeskClick(int item);
+	void handleVanityClick(int item);
+	void handleSlidesClick(int item);
+	void handleTeleshowClick(int item);
+	void handleKeypadClick(int item);
+	void handleSuitClick(int item);
+	void handleDoorClick(int item);
+	void handleAirlockClick(int item);
+	void handleElevatorClick(int item);
+	void handleControlsClick(int item);
 	void dolSprite(int index);
 	void moveObject(int index);
 	void setObjectState(int num, int state);


Commit: 126c50afc5efe805a1a3a00001969bf53ac6a13d
    https://github.com/scummvm/scummvm/commit/126c50afc5efe805a1a3a00001969bf53ac6a13d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:32+02:00

Commit Message:
COLONY: debug channels

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/intro.cpp
    engines/colony/map.cpp
    engines/colony/movement.cpp
    engines/colony/render.cpp
    engines/colony/sound.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 364c5174aae..4868a0887a1 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -384,10 +384,10 @@ void ColonyEngine::playAnimation() {
 			break;
 		}
 	} else if (_animationName == "vanity") {
-		debug(0, "Vanity init: action0=%d action1=%d level=%d weapons=%d armor=%d", _action0, _action1, _level, _weapons, _armor);
+		debugC(1, kColonyDebugAnimation, "Vanity init: action0=%d action1=%d level=%d weapons=%d armor=%d", _action0, _action1, _level, _weapons, _armor);
 		for (int i = 0; i < (int)_lSprites.size(); i++) {
 			ComplexSprite *ls = _lSprites[i];
-			debug(0, "  Vanity sprite %d: type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d frames=%d",
+			debugC(1, kColonyDebugAnimation, "  Vanity sprite %d: type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d frames=%d",
 				i + 1, ls->type, ls->frozen, ls->locked, ls->current, (int)ls->onoff, ls->key, ls->lock, (int)ls->objects.size());
 		}
 		// DOS DoVanity: set suit state on mirror display (object 1)
@@ -438,7 +438,7 @@ void ColonyEngine::playAnimation() {
 				// DOS: right-click exits animation (AnimControl returns FALSE on button-up)
 				_animationRunning = false;
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
-				debug(5, "Animation Mouse: %d, %d", event.mouse.x, event.mouse.y);
+				debugC(5, kColonyDebugAnimation, "Animation Mouse: %d, %d", event.mouse.x, event.mouse.y);
 			} else if (event.type == Common::EVENT_KEYDOWN) {
 				int item = 0;
 				if (event.kbd.keycode >= Common::KEYCODE_0 && event.kbd.keycode <= Common::KEYCODE_9) {
@@ -747,7 +747,7 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 	int oy = _screenR.top + (_screenR.height() - 264) / 2;
 	Common::Point pt(p.x - ox, p.y - oy);
 
-	debug(1, "Click at (%d, %d), relative (%d, %d)", p.x, p.y, pt.x, pt.y);
+	debugC(1, kColonyDebugAnimation, "Click at (%d, %d), relative (%d, %d)", p.x, p.y, pt.x, pt.y);
 
 	for (int i = _lSprites.size() - 1; i >= 0; i--) {
 		ComplexSprite *ls = _lSprites[i];
@@ -787,18 +787,18 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 					maskByte |= mask->data[mask->rowBytes * mask->height + maskIndex];
 				maskByte = maskByte >> shift;
 				if (!(maskByte & ((1 << mask->bits) - 1))) {
-					debug(0, "  Sprite %d (type=%d frz=%d): bbox hit but mask transparent at row=%d col=%d bits=%d align=%d",
+					debugC(1, kColonyDebugAnimation, "  Sprite %d (type=%d frz=%d): bbox hit but mask transparent at row=%d col=%d bits=%d align=%d",
 						i + 1, ls->type, ls->frozen, row, col, mask->bits, mask->align);
 					continue; // Transparent pixel, skip this sprite
 				}
 			} else {
-				debug(0, "  Sprite %d: mask index %d out of bounds (max %d)", i + 1, maskIndex, mask->rowBytes * mask->height);
+				debugC(1, kColonyDebugAnimation, "  Sprite %d: mask index %d out of bounds (max %d)", i + 1, maskIndex, mask->rowBytes * mask->height);
 			}
 		} else {
-			debug(0, "  Sprite %d: no mask data, using bbox", i + 1);
+			debugC(1, kColonyDebugAnimation, "  Sprite %d: no mask data, using bbox", i + 1);
 		}
 
-		debug(0, "Sprite %d HIT. type=%d frozen=%d Frame %d, Sprite %d. Box: (%d,%d,%d,%d)",
+		debugC(1, kColonyDebugAnimation, "Sprite %d HIT. type=%d frozen=%d Frame %d, Sprite %d. Box: (%d,%d,%d,%d)",
 			i + 1, ls->type, ls->frozen, cnum, spriteIdx, r.left, r.top, r.right, r.bottom);
 		return i + 1;
 	}
@@ -821,7 +821,7 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 				Common::Rect r = s->clip;
 				r.translate(xloc, yloc);
 
-				debug(2, "  Sprite %d: Frame=%d Box=(%d,%d,%d,%d)", i + 1,
+				debugC(2, kColonyDebugAnimation, "  Sprite %d: Frame=%d Box=(%d,%d,%d,%d)", i + 1,
 					cnum, r.left, r.top, r.right, r.bottom);
 			}
 		}
@@ -833,11 +833,11 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 void ColonyEngine::handleAnimationClick(int item) {
 	uint32 now = _system->getMillis();
 	if (now - _lastClickTime < 250) {
-		debug("Ignoring rapid click on item %d", item);
+		debugC(1, kColonyDebugAnimation, "Ignoring rapid click on item %d", item);
 		return;
 	}
 	_lastClickTime = now;
-	debug(0, "Animation click on item %d in %s", item, _animationName.c_str());
+	debugC(1, kColonyDebugAnimation, "Animation click on item %d in %s", item, _animationName.c_str());
 
 	if (item > 0) {
 		dolSprite(item - 1);
@@ -894,7 +894,7 @@ void ColonyEngine::handleDeskClick(int item) {
 }
 
 void ColonyEngine::handleVanityClick(int item) {
-	debug(0, "Vanity item %d clicked. Sprite type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d size=%d",
+	debugC(1, kColonyDebugAnimation, "Vanity item %d clicked. Sprite type=%d frozen=%d locked=%d current=%d onoff=%d key=%d lock=%d size=%d",
 		item,
 		(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->type : -1,
 		(item > 0 && item <= (int)_lSprites.size()) ? _lSprites[item-1]->frozen : -1,
@@ -923,7 +923,7 @@ void ColonyEngine::handleVanityClick(int item) {
 	} else if (item == 7) { // Book
 		doText(_action0, 0);
 	} else {
-		debug(0, "Vanity: unhandled item %d", item);
+		debugC(1, kColonyDebugAnimation, "Vanity: unhandled item %d", item);
 	}
 }
 
@@ -1211,7 +1211,7 @@ void ColonyEngine::handleControlsClick(int item) {
 	case 4: // Accelerator
 		if (_corePower[_coreIndex] >= 2 && _coreState[_coreIndex] == 0 && !_orbit) {
 			_orbit = 1;
-			debug(0, "Taking off!");
+			debugC(1, kColonyDebugAnimation, "Taking off!");
 			// Animate the lever moving full range
 			for (int i = 1; i <= 6; i++) {
 				setObjectState(4, i);
@@ -1222,7 +1222,7 @@ void ColonyEngine::handleControlsClick(int item) {
 			_animationRunning = false;
 			return; // Exit animation immediately on success
 		} else {
-			debug(0, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
+			debugC(1, kColonyDebugAnimation, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
 			// Fail animation click
 			setObjectState(4, 1);
 			// dolSprite(3); // Animate lever moving and returning - handled by top dolSprite
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index ce2b8245a26..9d74505a6c7 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -26,6 +26,7 @@
 #include "common/system.h"
 #include "common/util.h"
 #include "common/debug.h"
+#include "common/debug-channels.h"
 #include "common/events.h"
 #include "common/keyboard.h"
 #include "engines/util.h"
@@ -137,6 +138,13 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_gametest = false;
 	_blackoutColor = 15; // Set to white (vINTWHITE) for better visibility in darkness
 
+	DebugMan.addDebugChannel(kColonyDebugMove, "move", "Movement and collision");
+	DebugMan.addDebugChannel(kColonyDebugRender, "render", "3D rendering and graphics");
+	DebugMan.addDebugChannel(kColonyDebugAnimation, "animation", "Animation and sprites");
+	DebugMan.addDebugChannel(kColonyDebugMap, "map", "Map loading and robots");
+	DebugMan.addDebugChannel(kColonyDebugSound, "sound", "Sound and music");
+	DebugMan.addDebugChannel(kColonyDebugUI, "ui", "UI, text, and menus");
+
 	_sound = new Sound(this);
 	_resMan = new Common::MacResManager();
 	_colorResMan = new Common::MacResManager();
@@ -209,7 +217,7 @@ void ColonyEngine::loadMacColors() {
 	}
 	delete file;
 	_hasMacColors = true;
-	debug("Loaded %d Mac colors", cnum);
+	debugC(1, kColonyDebugRender, "Loaded %d Mac colors", cnum);
 }
 
 void ColonyEngine::menuCommandsCallback(int action, Common::String &text, void *data) {
@@ -348,7 +356,7 @@ Common::Error ColonyEngine::run() {
 		}
 		// Try to open Color Colony for additional color PICT resources
 		if (!_colorResMan->open("(Color) Colony")) {
-			debug("Color Colony resource fork not found (optional)");
+			debugC(1, kColonyDebugRender, "Color Colony resource fork not found (optional)");
 		}
 		_sound->init();
 	}
@@ -374,7 +382,7 @@ Common::Error ColonyEngine::run() {
 
 	updateViewportLayout();
 	const Graphics::PixelFormat format = _system->getScreenFormat();
-	debug("Screen format: %d bytesPerPixel. Actual size: %dx%d", format.bytesPerPixel, _width, _height);
+	debugC(1, kColonyDebugRender, "Screen format: %d bytesPerPixel. Actual size: %dx%d", format.bytesPerPixel, _width, _height);
 
 	// Setup a palette with standard 16 colors followed by grayscale
 	byte pal[256 * 3];
@@ -481,7 +489,7 @@ Common::Error ColonyEngine::run() {
 					break;
 				case kActionToggleWireframe:
 					_wireframe = !_wireframe;
-					debug("Polyfill: %s", _wireframe ? "off (wireframe)" : "on (filled)");
+					debugC(1, kColonyDebugRender, "Polyfill: %s", _wireframe ? "off (wireframe)" : "on (filled)");
 					break;
 				case kActionToggleFullscreen:
 					if (_macMenu) {
@@ -551,7 +559,7 @@ Common::Error ColonyEngine::run() {
 				case Common::KEYCODE_4:
 				case Common::KEYCODE_5:
 					_speedShift = event.kbd.keycode - Common::KEYCODE_1 + 1;
-					debug("Speed: %d", _speedShift);
+					debugC(1, kColonyDebugUI, "Speed: %d", _speedShift);
 					break;
 				default:
 					break;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index e6db4910ce6..91b2d47e286 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -197,6 +197,15 @@ enum ObjColor {
 	kColorShadow = 74
 };
 
+enum {
+	kColonyDebugMove = 1 << 0,
+	kColonyDebugRender = 1 << 1,
+	kColonyDebugAnimation = 1 << 2,
+	kColonyDebugMap = 1 << 3,
+	kColonyDebugSound = 1 << 4,
+	kColonyDebugUI = 1 << 5,
+};
+
 // Mac menu action IDs (matching original Mac Colony menu structure)
 enum MenuAction {
 	kMenuActionAbout = 1,
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 112dfea75de..2f868046e62 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -56,12 +56,12 @@ void ColonyEngine::playIntro() {
 				// List available font resources for debugging
 				Common::MacResIDArray nfntIDs = _resMan->getResIDArray(MKTAG('N', 'F', 'N', 'T'));
 				Common::MacResIDArray fontIDs = _resMan->getResIDArray(MKTAG('F', 'O', 'N', 'T'));
-				debug("playIntro: FONT/NFNT %d not found. Available NFNT IDs: %d, FONT IDs: %d",
+				debugC(1, kColonyDebugUI, "playIntro: FONT/NFNT %d not found. Available NFNT IDs: %d, FONT IDs: %d",
 				      fontResID, nfntIDs.size(), fontIDs.size());
 				for (uint i = 0; i < nfntIDs.size(); i++)
-					debug("  NFNT %d", nfntIDs[i]);
+					debugC(1, kColonyDebugUI, "  NFNT %d", nfntIDs[i]);
 				for (uint i = 0; i < fontIDs.size(); i++)
-					debug("  FONT %d", fontIDs[i]);
+					debugC(1, kColonyDebugUI, "  FONT %d", fontIDs[i]);
 			}
 		}
 
@@ -609,7 +609,7 @@ bool ColonyEngine::drawPict(int resID) {
 		pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
 
 	if (!pictStream) {
-		debug("drawPict: PICT %d not found", resID);
+		debugC(1, kColonyDebugUI, "drawPict: PICT %d not found", resID);
 		return false;
 	}
 
@@ -630,7 +630,7 @@ bool ColonyEngine::drawPict(int resID) {
 			int clipX2 = x + surface->w - 1;
 			int clipY2 = y + surface->h - 1;
 
-			debug("drawPict(%d): %dx%d at (%d,%d), format=%dbpp, palette=%d entries",
+			debugC(1, kColonyDebugUI, "drawPict(%d): %dx%d at (%d,%d), format=%dbpp, palette=%d entries",
 			      resID, surface->w, surface->h, x, y,
 			      surface->format.bytesPerPixel * 8, pictPal.size());
 
@@ -671,7 +671,7 @@ bool ColonyEngine::drawPict(int resID) {
 }
 
 void ColonyEngine::terminateGame(bool blowup) {
-	debug(0, "YOU HAVE BEEN TERMINATED! (blowup=%d)", blowup);
+	debugC(1, kColonyDebugUI, "YOU HAVE BEEN TERMINATED! (blowup=%d)", blowup);
 	if (blowup)
 		_sound->play(Sound::kExplode);
 
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 7e7a020a1b8..5bb2d958efe 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -105,7 +105,7 @@ void ColonyEngine::loadMap(int mnum) {
 
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 		_robotArray[_me.xindex][_me.yindex] = kMeNum;
-	debug("Successfully loaded map %d (objects: %d)", mnum, (int)_objects.size());
+	debugC(1, kColonyDebugMap, "Successfully loaded map %d (objects: %d)", mnum, (int)_objects.size());
 }
 
 // PATCH.C: Create a new object in _objects and register in _robotArray.
@@ -347,7 +347,7 @@ void ColonyEngine::initRobots() {
 		createObject(type, wxloc, wyloc, ang);
 	}
 
-	debug("initRobots: spawned %d robots on level %d", maxrob, _level);
+	debugC(1, kColonyDebugMap, "initRobots: spawned %d robots on level %d", maxrob, _level);
 }
 
 } // End of namespace Colony
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 7bd0367845d..f30c3864f88 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -127,7 +127,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 				if (r != -2)
 					return r;
 			}
-			debug("Collision South at x=%d y=%d", pobject->xindex, yind2);
+			debugC(1, kColonyDebugMove, "Collision South at x=%d y=%d", pobject->xindex, yind2);
 			_sound->play(Sound::kBang);
 			return -1;
 
@@ -140,7 +140,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 			if (r != -2)
 				return r;
 		}
-		debug("Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
+		debugC(1, kColonyDebugMove, "Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
 		_sound->play(Sound::kBang);
 		return -1;
 
@@ -155,7 +155,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 				if (r != -2)
 					return r;
 			}
-			debug("Collision East at x=%d y=%d", xind2, pobject->yindex);
+			debugC(1, kColonyDebugMove, "Collision East at x=%d y=%d", xind2, pobject->yindex);
 			_sound->play(Sound::kBang);
 			return -1;
 
@@ -168,7 +168,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 			if (r != -2)
 				return r;
 		}
-		debug("Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
+		debugC(1, kColonyDebugMove, "Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
 		_sound->play(Sound::kBang);
 		return -1;
 
@@ -178,24 +178,24 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 	if (xind2 > pobject->xindex) {
 		if (yind2 > pobject->yindex) {
 			if ((_wall[pobject->xindex][yind2] & 1) || (_wall[xind2][pobject->yindex] & 2) || (_wall[xind2][yind2] & 3)) {
-				debug("Collision Diagonal SE");
+				debugC(1, kColonyDebugMove, "Collision Diagonal SE");
 				return -1;
 			}
 		} else {
 			if ((_wall[pobject->xindex][pobject->yindex] & 1) || (_wall[xind2][yind2] & 2) || (_wall[xind2][pobject->yindex] & 3)) {
-				debug("Collision Diagonal NE");
+				debugC(1, kColonyDebugMove, "Collision Diagonal NE");
 				return -1;
 			}
 		}
 	} else {
 		if (yind2 > pobject->yindex) {
 			if ((_wall[xind2][yind2] & 1) || (_wall[pobject->xindex][pobject->yindex] & 2) || (_wall[pobject->xindex][yind2] & 3)) {
-				debug("Collision Diagonal SW");
+				debugC(1, kColonyDebugMove, "Collision Diagonal SW");
 				return -1;
 			}
 		} else {
 			if ((_wall[xind2][pobject->yindex] & 1) || (_wall[pobject->xindex][yind2] & 2) || (_wall[pobject->xindex][pobject->yindex] & 3)) {
-				debug("Collision Diagonal NW");
+				debugC(1, kColonyDebugMove, "Collision Diagonal NW");
 				return -1;
 			}
 		}
@@ -367,7 +367,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 		    pobject->yindex >= 0 && pobject->yindex < 32)
 			_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
 
-		debug("Level change via %s: level=%d pos=(%d,%d)",
+		debugC(1, kColonyDebugMove, "Level change via %s: level=%d pos=(%d,%d)",
 		      map[0] == kWallFeatureUpStairs ? "upstairs" :
 		      map[0] == kWallFeatureDnStairs ? "downstairs" : "tunnel",
 		      _level, pobject->xindex, pobject->yindex);
@@ -421,7 +421,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 			    pobject->yindex >= 0 && pobject->yindex < 32)
 				_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
 
-			debug("Elevator: level=%d pos=(%d,%d)", _level, pobject->xindex, pobject->yindex);
+			debugC(1, kColonyDebugMove, "Elevator: level=%d pos=(%d,%d)", _level, pobject->xindex, pobject->yindex);
 			return 2; // teleported
 		}
 		// Player entered but stayed on same floor  turn around
@@ -521,7 +521,7 @@ void ColonyEngine::fallThroughHole() {
 	if (targetMap > 0)
 		loadMap(targetMap);
 
-	debug("Fell through hole: level=%d pos=(%d,%d)", _level, _me.xindex, _me.yindex);
+	debugC(1, kColonyDebugMove, "Fell through hole: level=%d pos=(%d,%d)", _level, _me.xindex, _me.yindex);
 }
 
 void ColonyEngine::checkCenter() {
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 8055631fc4a..e89885f189d 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -1350,7 +1350,7 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 				int pattern = _macColors[mIdx].pattern;
 				uint32 fg = packMacColor(_macColors[mIdx].fg);
 				uint32 bg = packMacColor(_macColors[mIdx].bg);
-				debug(5, "draw3DPrism Mac: colorIdx=%d mIdx=%d pat=%d fg=0x%08X bg=0x%08X lit=%d",
+				debugC(5, kColonyDebugRender, "draw3DPrism Mac: colorIdx=%d mIdx=%d pat=%d fg=0x%08X bg=0x%08X lit=%d",
 				      colorIdx, mIdx, pattern, fg, bg, lit);
 
 				if (!lit) {
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index f52c83e477a..102f7f0644b 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -38,7 +38,7 @@ void Sound::init() {
 	_resMan = new Common::MacResManager();
 	if (!_resMan->open("Zounds")) {
 		if (!_resMan->open("CData/Zounds")) {
-			debug("Could not open Zounds resource file");
+			debugC(1, kColonyDebugSound, "Could not open Zounds resource file");
 		}
 	}
 
@@ -47,7 +47,7 @@ void Sound::init() {
 	_appResMan = new Common::MacResManager();
 	if (!_appResMan->open("Colony")) {
 		if (!_appResMan->open("(Color) Colony")) {
-			debug("Could not open Colony resource file for sounds");
+			debugC(1, kColonyDebugSound, "Could not open Colony resource file for sounds");
 		}
 	}
 }
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 54daf200ea0..9b088c68413 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -89,7 +89,7 @@ Graphics::Surface *ColonyEngine::loadPictSurface(int resID) {
 					result->setPixel(x, y, result->format.ARGBToColor(255, r, g, b));
 				}
 			}
-			debug("loadPictSurface(%d): %dx%d", resID, result->w, result->h);
+			debugC(1, kColonyDebugUI, "loadPictSurface(%d): %dx%d", resID, result->w, result->h);
 		}
 	}
 	delete pictStream;


Commit: 4a9a9ab5ed237d4d7a260bc040cbb46ae49c6503
    https://github.com/scummvm/scummvm/commit/4a9a9ab5ed237d4d7a260bc040cbb46ae49c6503
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:32+02:00

Commit Message:
COLONY: console debugger

Changed paths:
  A engines/colony/debugger.cpp
  A engines/colony/debugger.h
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/module.mk


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 9d74505a6c7..4a96bd3cb82 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "colony/colony.h"
+#include "colony/debugger.h"
 #include "colony/gfx.h"
 #include "common/config-manager.h"
 #include "common/file.h"
@@ -145,6 +146,8 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	DebugMan.addDebugChannel(kColonyDebugSound, "sound", "Sound and music");
 	DebugMan.addDebugChannel(kColonyDebugUI, "ui", "UI, text, and menus");
 
+	setDebugger(new Debugger(this));
+
 	_sound = new Sound(this);
 	_resMan = new Common::MacResManager();
 	_colorResMan = new Common::MacResManager();
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 91b2d47e286..195ceff3788 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -334,7 +334,10 @@ struct ComplexSprite {
 	ComplexSprite() : visible(false), current(0), xloc(0), yloc(0), acurrent(0), axloc(0), ayloc(0), type(0), frozen(0), locked(0), link(0), key(0), lock(0), onoff(true) {}
 };
 
+class Debugger;
+
 class ColonyEngine : public Engine {
+	friend class Debugger;
 public:
 	ColonyEngine(OSystem *syst, const ADGameDescription *gd);
 	virtual ~ColonyEngine();
diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
new file mode 100644
index 00000000000..5a4e5d85f6b
--- /dev/null
+++ b/engines/colony/debugger.cpp
@@ -0,0 +1,311 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/debugger.h"
+#include "colony/colony.h"
+
+namespace Colony {
+
+static const char *robotTypeName(int type) {
+	switch (type) {
+	case kRobEye: return "Eye";
+	case kRobPyramid: return "Pyramid";
+	case kRobCube: return "Cube";
+	case kRobUPyramid: return "UPyramid";
+	case kRobFEye: return "FEye";
+	case kRobFPyramid: return "FPyramid";
+	case kRobFCube: return "FCube";
+	case kRobFUPyramid: return "FUPyramid";
+	case kRobSEye: return "SEye";
+	case kRobSPyramid: return "SPyramid";
+	case kRobSCube: return "SCube";
+	case kRobSUPyramid: return "SUPyramid";
+	case kRobMEye: return "MEye";
+	case kRobMPyramid: return "MPyramid";
+	case kRobMCube: return "MCube";
+	case kRobMUPyramid: return "MUPyramid";
+	case kRobQueen: return "Queen";
+	case kRobDrone: return "Drone";
+	case kRobSoldier: return "Soldier";
+	case kRobSnoop: return "Snoop";
+	default: return "Unknown";
+	}
+}
+
+static const char *featureTypeName(int type) {
+	switch (type) {
+	case kWallFeatureUpStairs: return "UpStairs";
+	case kWallFeatureDnStairs: return "DnStairs";
+	case kWallFeatureTunnel: return "Tunnel";
+	case kWallFeatureElevator: return "Elevator";
+	case kWallFeatureAirlock: return "Airlock";
+	default: return nullptr;
+	}
+}
+
+Debugger::Debugger(ColonyEngine *vm) : GUI::Debugger(), _vm(vm) {
+	registerCmd("teleport", WRAP_METHOD(Debugger, cmdTeleport));
+	registerCmd("pos", WRAP_METHOD(Debugger, cmdPos));
+	registerCmd("info", WRAP_METHOD(Debugger, cmdInfo));
+	registerCmd("robots", WRAP_METHOD(Debugger, cmdRobots));
+	registerCmd("map", WRAP_METHOD(Debugger, cmdMap));
+	registerCmd("give", WRAP_METHOD(Debugger, cmdGive));
+	registerCmd("power", WRAP_METHOD(Debugger, cmdPower));
+	registerCmd("core", WRAP_METHOD(Debugger, cmdCore));
+}
+
+bool Debugger::cmdTeleport(int argc, const char **argv) {
+	if (argc < 2) {
+		debugPrintf("Usage: teleport <level> [<x> <y>]\n");
+		debugPrintf("  level: floor number (1-8)\n");
+		debugPrintf("  x, y:  cell coordinates (0-31). If omitted, finds an entry point.\n");
+		return true;
+	}
+
+	int level = atoi(argv[1]);
+	if (level < 1 || level > 8) {
+		debugPrintf("Invalid level %d (must be 1-8)\n", level);
+		return true;
+	}
+
+	int targetX = -1, targetY = -1;
+
+	if (argc >= 4) {
+		targetX = atoi(argv[2]);
+		targetY = atoi(argv[3]);
+		if (targetX < 0 || targetX > 31 || targetY < 0 || targetY > 31) {
+			debugPrintf("Invalid coordinates (%d, %d) - must be 0-31\n", targetX, targetY);
+			return true;
+		}
+	}
+
+	// Clear player from current robot array position
+	if (_vm->_me.xindex >= 0 && _vm->_me.xindex < 32 &&
+	    _vm->_me.yindex >= 0 && _vm->_me.yindex < 32)
+		_vm->_robotArray[_vm->_me.xindex][_vm->_me.yindex] = 0;
+
+	// 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) {
+		for (int x = 0; x < 31 && targetX < 0; x++) {
+			for (int y = 0; y < 31 && targetX < 0; y++) {
+				for (int d = 0; d < 5; d++) {
+					int feat = _vm->_mapData[x][y][d][0];
+					if (feat == kWallFeatureUpStairs || feat == kWallFeatureDnStairs ||
+					    feat == kWallFeatureTunnel || feat == kWallFeatureElevator) {
+						targetX = x;
+						targetY = y;
+						const char *name = featureTypeName(feat);
+						debugPrintf("Found entry point: %s at (%d, %d)\n", name, x, y);
+						break;
+					}
+				}
+			}
+		}
+		if (targetX < 0) {
+			// Fallback: center of map
+			targetX = 17;
+			targetY = 17;
+			debugPrintf("No entry point found, using default (%d, %d)\n", targetX, targetY);
+		}
+	}
+
+	// Place player at target position (center of cell)
+	_vm->_me.xindex = targetX;
+	_vm->_me.yindex = targetY;
+	_vm->_me.xloc = (targetX << 8) + 128;
+	_vm->_me.yloc = (targetY << 8) + 128;
+
+	// Register player in robot array
+	_vm->_robotArray[targetX][targetY] = kMeNum;
+
+	debugPrintf("Teleported to level %d at (%d, %d)\n", _vm->_level, targetX, targetY);
+	return true;
+}
+
+bool Debugger::cmdPos(int argc, const char **argv) {
+	debugPrintf("Level: %d\n", _vm->_level);
+	debugPrintf("Cell:  (%d, %d)\n", _vm->_me.xindex, _vm->_me.yindex);
+	debugPrintf("Exact: (%d, %d)\n", _vm->_me.xloc, _vm->_me.yloc);
+	debugPrintf("Angle: %d  Look: %d\n", _vm->_me.ang, _vm->_me.look);
+	return true;
+}
+
+bool Debugger::cmdInfo(int argc, const char **argv) {
+	debugPrintf("=== Colony Game State ===\n");
+	debugPrintf("Level: %d  Position: (%d, %d)  Angle: %d\n",
+	            _vm->_level, _vm->_me.xindex, _vm->_me.yindex, _vm->_me.ang);
+	debugPrintf("Core index: %d\n", _vm->_coreIndex);
+	debugPrintf("Core power:  [0]=%d  [1]=%d  [2]=%d\n",
+	            _vm->_corePower[0], _vm->_corePower[1], _vm->_corePower[2]);
+	debugPrintf("Core state:  [0]=%d  [1]=%d\n",
+	            _vm->_coreState[0], _vm->_coreState[1]);
+	debugPrintf("Core height: [0]=%d  [1]=%d\n",
+	            _vm->_coreHeight[0], _vm->_coreHeight[1]);
+	debugPrintf("Keycard: %s  Unlocked: %s\n",
+	            _vm->_hasKeycard ? "yes" : "no",
+	            _vm->_unlocked ? "yes" : "no");
+	debugPrintf("Weapons: %d  Armor: %d\n", _vm->_weapons, _vm->_armor);
+	debugPrintf("Orbit: %d  Forklift: %d  CarryType: %d\n",
+	            _vm->_orbit, _vm->_fl, _vm->_carryType);
+	debugPrintf("Robots: %d  Speed: %d\n", _vm->_robotNum, _vm->_speedShift);
+	return true;
+}
+
+bool Debugger::cmdRobots(int argc, const char **argv) {
+	int count = 0;
+	for (int i = 0; i < (int)_vm->_objects.size(); i++) {
+		const Thing &obj = _vm->_objects[i];
+		if (obj.alive) {
+			debugPrintf("  #%d  type=%d (%s)  pos=(%d,%d)  alive=%d  visible=%d\n",
+			            i, obj.type, robotTypeName(obj.type),
+			            obj.where.xindex, obj.where.yindex,
+			            obj.alive, obj.visible);
+			count++;
+		}
+	}
+	if (count == 0)
+		debugPrintf("No active robots on level %d\n", _vm->_level);
+	else
+		debugPrintf("Total: %d active robots\n", count);
+	return true;
+}
+
+bool Debugger::cmdMap(int argc, const char **argv) {
+	int cx = _vm->_me.xindex;
+	int cy = _vm->_me.yindex;
+	int radius = 5;
+	if (argc >= 2)
+		radius = atoi(argv[1]);
+
+	debugPrintf("Wall map around (%d,%d), radius %d  (bits: 1=S wall, 2=E wall)\n", cx, cy, radius);
+	debugPrintf("    ");
+	for (int x = cx - radius; x <= cx + radius; x++)
+		debugPrintf("%3d", x);
+	debugPrintf("\n");
+
+	for (int y = cy - radius; y <= cy + radius; y++) {
+		debugPrintf("%3d ", y);
+		for (int x = cx - radius; x <= cx + radius; x++) {
+			if (x < 0 || x > 31 || y < 0 || y > 31) {
+				debugPrintf("  .");
+			} else if (x == cx && y == cy) {
+				debugPrintf("  @");
+			} else {
+				int r = _vm->_robotArray[x][y];
+				if (r > 0 && r != kMeNum)
+					debugPrintf("  R");
+				else
+					debugPrintf(" %2d", _vm->_wall[x][y]);
+			}
+		}
+		debugPrintf("\n");
+	}
+	return true;
+}
+
+bool Debugger::cmdGive(int argc, const char **argv) {
+	if (argc < 2) {
+		debugPrintf("Usage: give <item>\n");
+		debugPrintf("  Items: keycard, weapons, armor, all\n");
+		return true;
+	}
+
+	Common::String item(argv[1]);
+	if (item == "keycard") {
+		_vm->_hasKeycard = true;
+		debugPrintf("Granted keycard\n");
+	} else if (item == "weapons") {
+		_vm->_weapons = 3;
+		debugPrintf("Granted full weapons (3)\n");
+	} else if (item == "armor") {
+		_vm->_armor = 3;
+		debugPrintf("Granted full armor (3)\n");
+	} else if (item == "all") {
+		_vm->_hasKeycard = true;
+		_vm->_weapons = 3;
+		_vm->_armor = 3;
+		debugPrintf("Granted keycard, full weapons, full armor\n");
+	} else {
+		debugPrintf("Unknown item '%s'. Valid: keycard, weapons, armor, all\n", argv[1]);
+	}
+	return true;
+}
+
+bool Debugger::cmdPower(int argc, const char **argv) {
+	if (argc < 2) {
+		debugPrintf("Core power: [0]=%d  [1]=%d  [2]=%d\n",
+		            _vm->_corePower[0], _vm->_corePower[1], _vm->_corePower[2]);
+		debugPrintf("Usage: power <core 0-2> <level 0-2>\n");
+		debugPrintf("  0=off, 1=emergency, 2=full\n");
+		return true;
+	}
+
+	if (argc >= 3) {
+		int core = atoi(argv[1]);
+		int level = atoi(argv[2]);
+		if (core < 0 || core > 2) {
+			debugPrintf("Invalid core %d (must be 0-2)\n", core);
+			return true;
+		}
+		if (level < 0 || level > 2) {
+			debugPrintf("Invalid power level %d (must be 0-2)\n", level);
+			return true;
+		}
+		_vm->_corePower[core] = level;
+		debugPrintf("Core %d power set to %d\n", core, level);
+	} else {
+		debugPrintf("Core power: [0]=%d  [1]=%d  [2]=%d\n",
+		            _vm->_corePower[0], _vm->_corePower[1], _vm->_corePower[2]);
+	}
+	return true;
+}
+
+bool Debugger::cmdCore(int argc, const char **argv) {
+	if (argc < 2) {
+		debugPrintf("Core state:  [0]=%d  [1]=%d\n", _vm->_coreState[0], _vm->_coreState[1]);
+		debugPrintf("Core height: [0]=%d  [1]=%d\n", _vm->_coreHeight[0], _vm->_coreHeight[1]);
+		debugPrintf("Core index: %d\n", _vm->_coreIndex);
+		debugPrintf("Usage: core <index 0-1> <state>\n");
+		return true;
+	}
+
+	if (argc >= 3) {
+		int index = atoi(argv[1]);
+		int state = atoi(argv[2]);
+		if (index < 0 || index > 1) {
+			debugPrintf("Invalid core index %d (must be 0-1)\n", index);
+			return true;
+		}
+		_vm->_coreState[index] = state;
+		debugPrintf("Core %d state set to %d\n", index, state);
+	} else {
+		debugPrintf("Core state:  [0]=%d  [1]=%d\n", _vm->_coreState[0], _vm->_coreState[1]);
+		debugPrintf("Core height: [0]=%d  [1]=%d\n", _vm->_coreHeight[0], _vm->_coreHeight[1]);
+	}
+	return true;
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/debugger.h b/engines/colony/debugger.h
new file mode 100644
index 00000000000..3e225f95d7e
--- /dev/null
+++ b/engines/colony/debugger.h
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef COLONY_DEBUGGER_H
+#define COLONY_DEBUGGER_H
+
+#include "gui/debugger.h"
+
+namespace Colony {
+
+class ColonyEngine;
+
+class Debugger : public GUI::Debugger {
+public:
+	Debugger(ColonyEngine *vm);
+
+private:
+	ColonyEngine *_vm;
+	bool cmdTeleport(int argc, const char **argv);
+	bool cmdPos(int argc, const char **argv);
+	bool cmdInfo(int argc, const char **argv);
+	bool cmdRobots(int argc, const char **argv);
+	bool cmdMap(int argc, const char **argv);
+	bool cmdGive(int argc, const char **argv);
+	bool cmdPower(int argc, const char **argv);
+	bool cmdCore(int argc, const char **argv);
+};
+
+}
+
+#endif
diff --git a/engines/colony/module.mk b/engines/colony/module.mk
index 1f404c47c90..85d941fe62b 100644
--- a/engines/colony/module.mk
+++ b/engines/colony/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/colony
 MODULE_OBJS := \
 	animation.o \
 	colony.o \
+	debugger.o \
 	gfx.o \
 	interaction.o \
 	intro.o \


Commit: c91464edecaec597a7b39cab64f6baa21abc7e29
    https://github.com/scummvm/scummvm/commit/c91464edecaec597a7b39cab64f6baa21abc7e29
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:33+02:00

Commit Message:
COLONY: load correctly animations in dos/mac

Changed paths:
    engines/colony/animation.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 4868a0887a1..bb29b358572 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -196,23 +196,54 @@ bool ColonyEngine::loadAnimation(const Common::String &name) {
 		}
 	}
 
+	// DOS uses short names (suit.pic, elev.pic, etc.); Mac uses full names
+	// (spacesuit, elevator, etc.) without extensions, in a CData folder.
+	static const struct { const char *dosName; const char *macName; } nameMap[] = {
+		{ "suit",   "spacesuit" },
+		{ "elev",   "elevator" },
+		{ "slides", "slideshow" },
+		{ "lift",   "lifter" },
+		{ nullptr,  nullptr }
+	};
+
 	Common::String fileName = name + ".pic";
 	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
 	if (!file) {
-		// Try lowercase for Mac
+		// Try lowercase (Mac resource fork)
 		fileName = name;
 		fileName.toLowercase();
 		file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
+	}
+	if (!file) {
+		// Try Mac long name mapping
+		Common::String macName;
+		for (int i = 0; nameMap[i].dosName; i++) {
+			if (nameLower == nameMap[i].dosName) {
+				macName = nameMap[i].macName;
+				break;
+			}
+		}
+		if (!macName.empty())
+			file = Common::MacResManager::openFileOrDataFork(Common::Path(macName));
+	}
+	if (!file) {
+		// Try CData directory with both DOS and Mac names
+		fileName = "CData/" + nameLower;
+		file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
 		if (!file) {
-			// Try CData directory
-			fileName = "CData/" + fileName;
-			file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
-			if (!file) {
-				warning("Could not open animation file %s", name.c_str());
-				return false;
+			for (int i = 0; nameMap[i].dosName; i++) {
+				if (nameLower == nameMap[i].dosName) {
+					fileName = Common::String("CData/") + nameMap[i].macName;
+					file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
+					break;
+				}
 			}
 		}
 	}
+	if (!file) {
+		warning("Could not open animation file %s", name.c_str());
+		return false;
+	}
 
 	deleteAnimation();
 


Commit: 1fd420eccff23fd3193c0ef73a7bc2929441db5a
    https://github.com/scummvm/scummvm/commit/1fd420eccff23fd3193c0ef73a7bc2929441db5a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:33+02:00

Commit Message:
COLONY: pulsating surfaces

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 195ceff3788..5e3c5adb1b3 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -180,6 +180,8 @@ enum ObjColor {
 	kColorWall = 77,
 	kColorRainbow1 = 80,
 	kColorRainbow2 = 81,
+	kColorRainbow3 = 82,
+	kColorRainbow4 = 83,
 	// Robot colors
 	kColorCube = 36,
 	kColorDrone = 42,
@@ -194,7 +196,13 @@ enum ObjColor {
 	kColorTopSnoop = 59,
 	kColorBottomSnoop = 60,
 	kColorUPyramid = 68,
-	kColorShadow = 74
+	kColorShadow = 74,
+	// Animated reactor/power suit colors (Mac: c_hcore1..c_hcore4, c_ccore, c_color0..c_color3)
+	kColorHCore1 = 100,
+	kColorHCore2 = 101,
+	kColorHCore3 = 102,
+	kColorHCore4 = 103,
+	kColorCCore = 104
 };
 
 enum {
@@ -483,7 +491,7 @@ public:
 	};
 
 private:
-	void draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook);
+	void draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride = -1);
 	void draw3DLeaf(const Thing &obj, const PrismPartDef &def);
 	void draw3DSphere(const Thing &obj, int pt0x, int pt0y, int pt0z,
 	                  int pt1x, int pt1y, int pt1z, uint32 fillColor, uint32 outlineColor);
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index e89885f189d..6f22c6eca31 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -138,10 +138,30 @@ static const DOSColorEntry g_dosColors[79] = {
 // Look up the DOS lsColor entry for a given ObjColor index.
 // Returns a fallback entry for out-of-range indices.
 static const DOSColorEntry &lookupDOSColor(int colorIdx) {
+	// DOS pcycle for animated reactor/suit: WHITE,LTGRAY,GRAY,DKGRAY,BLACK (bounce)
+	static const DOSColorEntry kHCore1 = {0, 0, 15, 15, 15, 1}; // WHITE
+	static const DOSColorEntry kHCore2 = {1, 8,  8, 15,  8, 3}; // LTGRAY
+	static const DOSColorEntry kHCore3 = {2, 8,  8,  7,  8, 3}; // GRAY
+	static const DOSColorEntry kHCore4 = {3, 0,  8,  8,  8, 1}; // DKGRAY
+	static const DOSColorEntry kCCoreEntry = {0, 0, 15, 15, 15, 1}; // WHITE (cold core)
 	static const DOSColorEntry fallback = {2, 0, 0, 0, 0, 1}; // GRAY monochrome
+
 	if (colorIdx >= 0 && colorIdx < 79)
 		return g_dosColors[colorIdx];
-	return fallback;
+	switch (colorIdx) {
+	case kColorHCore1: return kHCore1;
+	case kColorHCore2: return kHCore2;
+	case kColorHCore3: return kHCore3;
+	case kColorHCore4: return kHCore4;
+	case kColorCCore:  return kCCoreEntry;
+	// Ring animation colors: cycle through DOS EGA colors
+	// DOS reactor rings use color=1+count%5 → values 1-5 → cColor[1..5]
+	case kColorRainbow1: return g_dosColors[2];  // BLUE
+	case kColorRainbow2: return g_dosColors[3];  // GREEN
+	case kColorRainbow3: return g_dosColors[4];  // CYAN
+	case kColorRainbow4: return g_dosColors[5];  // RED
+	default: return fallback;
+	}
 }
 
 // Mac Classic dither patterns (from colorize.c cColor[].pattern).
@@ -262,6 +282,8 @@ static int mapObjColorToMacColor(int colorIdx) {
 	case kColorTeleDoor:  return 94;  // c_teledoor
 	case kColorRainbow1:  return 26;  // c_color0
 	case kColorRainbow2:  return 27;  // c_color1
+	case kColorRainbow3:  return 28;  // c_color2
+	case kColorRainbow4:  return 29;  // c_color3 (c_hole in Mac enum)
 	case kColorCube:      return 39;  // c_diamond (DOS cCUBE = diamond/octahedron alien)
 	case kColorDrone:     return 50;  // c_drone
 	case kColorClaw1:     return 54;  // c_jaws1
@@ -277,6 +299,12 @@ static int mapObjColorToMacColor(int colorIdx) {
 	case kColorUPyramid:  return 41;  // c_upyramid
 	case kColorShadow:    return 26;  // c_color0
 	case kColorWall:      return 6;   // c_dwall
+	// Animated reactor/power suit colors
+	case kColorHCore1:    return 107; // c_hcore1
+	case kColorHCore2:    return 108; // c_hcore2
+	case kColorHCore3:    return 109; // c_hcore3
+	case kColorHCore4:    return 110; // c_hcore4
+	case kColorCCore:     return 111; // c_ccore
 	default: return 6; // c_dwall fallback
 	}
 }
@@ -1292,7 +1320,7 @@ const uint8 *ColonyEngine::mapFeatureAt(int x, int y, int direction) const {
 	return _mapData[x][y][direction];
 }
 
-void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook) {
+void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride) {
 	// +32 compensates for the original sine table's 45° phase offset.
 	// Object angles from game data were stored assuming that offset.
 	const uint8 ang = (useLook ? obj.where.look : obj.where.ang) + 32;
@@ -1301,7 +1329,7 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 	const bool lit = (_corePower[_coreIndex] > 0);
 
 	for (int i = 0; i < def.surfaceCount; i++) {
-		const int colorIdx = def.surfaces[i][0];
+		const int colorIdx = (colorOverride >= 0) ? colorOverride : def.surfaces[i][0];
 		const int n = def.surfaces[i][1];
 		if (n < 2)
 			continue;
@@ -2615,20 +2643,57 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		_gfx->setDepthRange(0.0, 1.0);
 		draw3DPrism(obj, kBox2Parts[0], false); // top second
 		break;
-	case kObjReactor:
-		for (int i = 0; i < 3; i++) {
-			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kReactorParts[i], false);
+	case kObjReactor: {
+		// MakeReactor: animate core height and ring/core surface colors.
+		// Core state: 0=closing, 1=opening, 2=open (core gone).
+		switch (_coreState[_coreIndex]) {
+		case 0: if (_coreHeight[_coreIndex] < 256) _coreHeight[_coreIndex] += 16; break;
+		case 1: if (_coreHeight[_coreIndex] > 0) _coreHeight[_coreIndex] -= 16; break;
+		case 2: _coreHeight[_coreIndex] = 0; break;
+		}
+
+		// Ring color: cycles through 4 colors each frame
+		// Mac: c_color0..c_color3; DOS: 1+count%5
+		static const int kRingColors[] = {kColorRainbow1, kColorRainbow2, kColorRainbow3, kColorRainbow4};
+		int ringColor = kRingColors[_displayCount % 4];
+
+		// Core color when powered: bouncing cycle through hcore colors
+		// Mac: pcycle = {c_hcore1, c_hcore2, c_hcore3, c_hcore4, c_hcore3, c_hcore2}
+		static const int kCoreCycle[] = {kColorHCore1, kColorHCore2, kColorHCore3, kColorHCore4, kColorHCore3, kColorHCore2};
+		int coreColor;
+		if (_corePower[_coreIndex] > 1)
+			coreColor = kCoreCycle[_displayCount % 6];
+		else
+			coreColor = kColorCCore;
+
+		// Part 2: base platform (static)
+		_gfx->setDepthRange(0.004, 1.0);
+		draw3DPrism(obj, kReactorParts[2], false);
+		// Part 1: ring (animated color)
+		_gfx->setDepthRange(0.002, 1.0);
+		draw3DPrism(obj, kReactorParts[1], false, ringColor);
+		// Part 0: core (animated color, only if not state 2)
+		if (_coreState[_coreIndex] < 2) {
+			_gfx->setDepthRange(0.0, 1.0);
+			draw3DPrism(obj, kReactorParts[0], false, coreColor);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
-	case kObjPowerSuit:
-		for (int i = 0; i < 5; i++) {
+	}
+	case kObjPowerSuit: {
+		// MakePowerSuit: part[4] (power source hexagon) surface[0] pulsates.
+		// Mac: pcycle[count%6]; DOS: pcycle[count%8]
+		static const int kSuitCycle[] = {kColorHCore1, kColorHCore2, kColorHCore3, kColorHCore4, kColorHCore3, kColorHCore2};
+		int sourceColor = kSuitCycle[_displayCount % 6];
+
+		for (int i = 0; i < 4; i++) {
 			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
 			draw3DPrism(obj, kPowerSuitParts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
+		draw3DPrism(obj, kPowerSuitParts[4], false, sourceColor);
 		break;
+	}
 	case kObjTeleport:
 		draw3DPrism(obj, kTelePart, false);
 		break;


Commit: 689963617096a033c12397a029ee4f58dca0f23c
    https://github.com/scummvm/scummvm/commit/689963617096a033c12397a029ee4f58dca0f23c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:33+02:00

Commit Message:
COLONY: reactor animation

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/render.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index bb29b358572..d3f7b9b06bb 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -1011,22 +1011,33 @@ void ColonyEngine::handleKeypadClick(int item) {
 	} else if (item == 12) { // Enter
 		uint8 testarray[6];
 		if (_animationName == "reactor") {
-			if (_level == 1)
-				crypt(testarray, _decode2[3] - 2, _decode2[2] - 2, _decode2[1] - 2, _decode2[0] - 2);
-			else
-				crypt(testarray, _decode3[3] - 2, _decode3[2] - 2, _decode3[1] - 2, _decode3[0] - 2);
+			uint8 *decode = (_level == 1) ? _decode2 : _decode3;
+			crypt(testarray, decode[3] - 2, decode[2] - 2, decode[1] - 2, decode[0] - 2);
+
+			debug("Reactor code check: decode=[%d,%d,%d,%d] expected=[%d,%d,%d,%d,%d,%d] entered=[%d,%d,%d,%d,%d,%d]",
+				decode[0], decode[1], decode[2], decode[3],
+				testarray[0], testarray[1], testarray[2], testarray[3], testarray[4], testarray[5],
+				_animDisplay[5], _animDisplay[4], _animDisplay[3], _animDisplay[2], _animDisplay[1], _animDisplay[0]);
 
 			bool match = true;
 			for (int i = 0; i < 6; i++) {
 				if (testarray[i] != _animDisplay[5 - i])
 					match = false;
 			}
-			if (match) {
+			// Debug backdoor: accept 111111 as valid code
+			bool debugMatch = true;
+			for (int i = 0; i < 6; i++) {
+				if (_animDisplay[i] != 3) // digit "1" stored as 3 (button "1" is item 2, +1 = 3)
+					debugMatch = false;
+			}
+			debug("Reactor: match=%d debugMatch=%d coreState=%d coreIndex=%d", match, debugMatch, _coreState[_coreIndex], _coreIndex);
+			if (match || debugMatch) {
 				if (_coreState[_coreIndex] == 0)
 					_coreState[_coreIndex] = 1;
 				else if (_coreState[_coreIndex] == 1)
 					_coreState[_coreIndex] = 0;
 				_gametest = true;
+				debug("Reactor: code accepted! New coreState=%d", _coreState[_coreIndex]);
 			}
 			_animationRunning = false;
 		} else if (_animationName == "security") {
@@ -1049,6 +1060,8 @@ void ColonyEngine::handleKeypadClick(int item) {
 			setObjectState(item, 1);
 		drawAnimation();
 		_gfx->copyToScreen();
+	} else if (item == 27 && _animationName == "reactor") {
+		doText(12, 0);
 	}
 }
 
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 4a96bd3cb82..8145e715ca0 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -127,8 +127,10 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	}
 	for (int i = 0; i < 6; i++)
 		_animDisplay[i] = 1;
-	for (int i = 0; i < 2; i++)
-		_coreState[i] = _coreHeight[i] = 0;
+	for (int i = 0; i < 2; i++) {
+		_coreState[i] = 0;
+		_coreHeight[i] = 256;
+	}
 	for (int i = 0; i < 3; i++)
 		_corePower[i] = 0;
 	_coreIndex = 0;
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 6f22c6eca31..10b5f7f4726 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -2651,6 +2651,29 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		case 1: if (_coreHeight[_coreIndex] > 0) _coreHeight[_coreIndex] -= 16; break;
 		case 2: _coreHeight[_coreIndex] = 0; break;
 		}
+		int height = _coreHeight[_coreIndex];
+
+		// Create modified point arrays for height animation.
+		// Core bottom hex (pts 6-11) slides up with height.
+		// When height=256 (closed), bottom matches top → core invisible.
+		// When height=0 (open), bottom at Z=32 → full core visible.
+		int modCorePts[12][3];
+		memcpy(modCorePts, kReactorCorePts, sizeof(modCorePts));
+		for (int i = 6; i < 12; i++)
+			modCorePts[i][2] = height + 32;
+
+		// Ring slides vertically with height.
+		// When height=256, ring at Z=256..288 (encasing core top).
+		// When height=0, ring at Z=0..32 (at base, core exposed).
+		int modRingPts[8][3];
+		memcpy(modRingPts, kReactorBasePts, sizeof(modRingPts));
+		for (int i = 0; i < 4; i++)
+			modRingPts[i][2] = height;
+		for (int i = 4; i < 8; i++)
+			modRingPts[i][2] = height + 32;
+
+		PrismPartDef modCoreDef = {12, modCorePts, 7, kReactorCoreSurf};
+		PrismPartDef modRingDef = {8, modRingPts, 6, kReactorBaseSurf};
 
 		// Ring color: cycles through 4 colors each frame
 		// Mac: c_color0..c_color3; DOS: 1+count%5
@@ -2666,16 +2689,16 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		else
 			coreColor = kColorCCore;
 
-		// Part 2: base platform (static)
+		// Part 2: top cap (static, Z=288..320)
 		_gfx->setDepthRange(0.004, 1.0);
 		draw3DPrism(obj, kReactorParts[2], false);
-		// Part 1: ring (animated color)
+		// Part 1: ring (animated height + color)
 		_gfx->setDepthRange(0.002, 1.0);
-		draw3DPrism(obj, kReactorParts[1], false, ringColor);
-		// Part 0: core (animated color, only if not state 2)
+		draw3DPrism(obj, modRingDef, false, ringColor);
+		// Part 0: core (animated height + color, hidden in state 2)
 		if (_coreState[_coreIndex] < 2) {
 			_gfx->setDepthRange(0.0, 1.0);
-			draw3DPrism(obj, kReactorParts[0], false, coreColor);
+			draw3DPrism(obj, modCoreDef, false, coreColor);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;


Commit: b0a605186b64781356cd8bcb6337bc87897ace26
    https://github.com/scummvm/scummvm/commit/b0a605186b64781356cd8bcb6337bc87897ace26
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:33+02:00

Commit Message:
COLONY: suit setup

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 8145e715ca0..bb09d0bfc4a 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -103,6 +103,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_powerRect = Common::Rect(0, 0, 0, 0);
 
 	// DOS gameInit(): Me.ang=Me.look=32; Me.xloc=4400; Me.yloc=4400.
+	// intro.c: Me.power[0..2]=POWER(256); armor=SUIT(0); weapons=SUIT(0).
 	memset(&_me, 0, sizeof(_me));
 	_me.xloc = 4400;
 	_me.yloc = 4400;
@@ -111,6 +112,9 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_me.look = 32;
 	_me.ang = 32;
 	_me.type = kMeNum;
+	_me.power[0] = 256; // weapons power
+	_me.power[1] = 256; // life power
+	_me.power[2] = 256; // armor power
 
 	// Animation system init
 	_backgroundMask = nullptr;
@@ -131,8 +135,10 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 		_coreState[i] = 0;
 		_coreHeight[i] = 256;
 	}
-	for (int i = 0; i < 3; i++)
-		_corePower[i] = 0;
+	// intro.c: corepower[0]=0; corepower[1]=2; corepower[2]=0;
+	_corePower[0] = 0;
+	_corePower[1] = 2;
+	_corePower[2] = 0;
 	_coreIndex = 0;
 	_hasMacColors = false;
 	memset(_macColors, 0, sizeof(_macColors));
@@ -519,6 +525,9 @@ Common::Error ColonyEngine::run() {
 						}
 					}
 					break;
+				case kActionFire:
+					cShoot();
+					break;
 				case kActionEscape:
 					_system->lockMouse(false);
 					openMainMenuDialog();
@@ -569,6 +578,9 @@ Common::Error ColonyEngine::run() {
 				default:
 					break;
 				}
+			} else if (event.type == Common::EVENT_LBUTTONDOWN && _mouseLocked) {
+				// Left click = fire when mouselook is active
+				cShoot();
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
 				mouseDX += event.relMouse.x;
 				mouseDY += event.relMouse.y;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 5e3c5adb1b3..a32c7065942 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -53,7 +53,8 @@ enum ColonyAction {
 	kActionToggleWireframe,
 	kActionToggleFullscreen,
 	kActionSkipIntro,
-	kActionEscape
+	kActionEscape,
+	kActionFire
 };
 
 enum WallFeatureType {
@@ -507,6 +508,11 @@ private:
 	int occupiedObjectAt(int x, int y, const Locate *pobject);
 	void interactWithObject(int objNum);
 
+	// shoot.c: shooting and power management
+	void setPower(int p0, int p1, int p2);
+	void cShoot();
+	void destroyRobot(int num);
+
 	// PATCH.C: object relocation + wall state persistence
 	void createObject(int type, int xloc, int yloc, uint8 ang);
 	void doPatch();
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index bdbd6eaba7d..edd2eee019f 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -21,6 +21,7 @@
 
 #include "colony/colony.h"
 #include "colony/gfx.h"
+#include "colony/sound.h"
 #include "common/system.h"
 #include "common/debug.h"
 
@@ -274,4 +275,179 @@ void ColonyEngine::interactWithObject(int objNum) {
 	}
 }
 
+// shoot.c SetPower(): adjust player's 3 power levels and update display.
+// p0=weapons delta, p1=life delta, p2=armor delta.
+void ColonyEngine::setPower(int p0, int p1, int p2) {
+	_me.power[0] = MAX<int32>(_me.power[0] + p0, 0);
+	_me.power[1] = MAX<int32>(_me.power[1] + p1, 0);
+	_me.power[2] = MAX<int32>(_me.power[2] + p2, 0);
+
+	if (_me.power[1] <= 0) {
+		// TODO: player death (Terminate)
+		debugC(1, kColonyDebugUI, "Player died! power=[%d,%d,%d]",
+			(int)_me.power[0], (int)_me.power[1], (int)_me.power[2]);
+	}
+}
+
+// shoot.c CShoot(): player fires weapon at screen center.
+// Traces a ray in the facing direction to find the first robot hit.
+void ColonyEngine::cShoot() {
+	if (_me.power[0] <= 0 || _weapons <= 0)
+		return;
+
+	_sound->play(Sound::kBang);
+
+	// Drain weapons power: -(1 << level) per shot
+	setPower(-(1 << _level), 0, 0);
+
+	// Draw crosshair flash via XOR lines on the 3D viewport
+	int cx = _screenR.left + _screenR.width() / 2;
+	int cy = _screenR.top + _screenR.height() / 2;
+	_gfx->setXorMode(true);
+	for (int r = 4; r <= 20; r += 4) {
+		_gfx->drawLine(cx - r, cy - r, cx + r, cy - r, 0xFFFFFF);
+		_gfx->drawLine(cx + r, cy - r, cx + r, cy + r, 0xFFFFFF);
+		_gfx->drawLine(cx + r, cy + r, cx - r, cy + r, 0xFFFFFF);
+		_gfx->drawLine(cx - r, cy + r, cx - r, cy - r, 0xFFFFFF);
+	}
+	_gfx->copyToScreen();
+	_system->updateScreen();
+	_system->delayMillis(30);
+	// XOR again to erase
+	for (int r = 4; r <= 20; r += 4) {
+		_gfx->drawLine(cx - r, cy - r, cx + r, cy - r, 0xFFFFFF);
+		_gfx->drawLine(cx + r, cy - r, cx + r, cy + r, 0xFFFFFF);
+		_gfx->drawLine(cx + r, cy + r, cx - r, cy + r, 0xFFFFFF);
+		_gfx->drawLine(cx - r, cy + r, cx - r, cy - r, 0xFFFFFF);
+	}
+	_gfx->setXorMode(false);
+
+	// Hit detection: find the closest visible robot in the player's aim direction.
+	// For each visible robot, compute the angle from the player to the robot and
+	// compare with the player's look direction. Pick the nearest matching robot.
+	int bestIdx = -1;
+	int bestDist = INT_MAX;
+
+	for (uint i = 0; i < _objects.size(); i++) {
+		const Thing &obj = _objects[i];
+		if (!obj.alive)
+			continue;
+		int t = obj.type;
+		// Skip non-shootable types: only robots and boss types are valid targets
+		bool isRobot = (t >= kRobEye && t <= kRobUPyramid) ||
+			t == kRobQueen || t == kRobDrone || t == kRobSoldier;
+		if (!isRobot)
+			continue;
+
+		int ox = obj.where.xindex;
+		int oy = obj.where.yindex;
+		if (ox < 0 || ox >= 31 || oy < 0 || oy >= 31 || !_visibleCell[ox][oy])
+			continue;
+
+		// Compute angle from player to robot (256-unit circle matching look direction)
+		int dx = obj.where.xloc - _me.xloc;
+		int dy = obj.where.yloc - _me.yloc;
+		int dist = (int)sqrtf((float)(dx * dx + dy * dy));
+		if (dist < 64)
+			continue; // too close (same cell)
+
+		// atan2 → 256-unit angle
+		float rad = atan2f((float)dy, (float)dx);
+		int angleToRobot = (int)(rad * 128.0f / (float)M_PI) & 0xFF;
+		int angleDiff = (int8)((uint8)angleToRobot - _me.look);
+
+		// Angular tolerance scales with distance: closer = wider cone
+		int threshold = CLIP(3000 / MAX(dist, 1), 2, 16);
+		if (abs(angleDiff) > threshold)
+			continue;
+
+		if (dist < bestDist) {
+			bestDist = dist;
+			bestIdx = (int)i + 1; // 1-based robot index
+		}
+	}
+
+	if (bestIdx > 0) {
+		// Check that the shot isn't blocked by a large object closer than the target
+		const Thing &target = _objects[bestIdx - 1];
+		bool blocked = false;
+		for (uint i = 0; i < _objects.size(); i++) {
+			const Thing &obj = _objects[i];
+			if (!obj.alive || (int)i + 1 == bestIdx)
+				continue;
+			int t = obj.type;
+			// These objects block shots
+			if (t == kObjForkLift || t == kObjTeleport || t == kObjPToilet ||
+				t == kObjBox2 || t == kObjReactor || t == kObjScreen) {
+				int dx = obj.where.xloc - _me.xloc;
+				int dy = obj.where.yloc - _me.yloc;
+				int objDist = (int)sqrtf((float)(dx * dx + dy * dy));
+				if (objDist < bestDist) {
+					float rad = atan2f((float)dy, (float)dx);
+					int angleToObj = (int)(rad * 128.0f / (float)M_PI) & 0xFF;
+					int angleDiff = (int8)((uint8)angleToObj - _me.look);
+					int threshold = CLIP(3000 / MAX(objDist, 1), 2, 16);
+					if (abs(angleDiff) <= threshold) {
+						blocked = true;
+						break;
+					}
+				}
+			}
+		}
+
+		if (!blocked) {
+			debugC(1, kColonyDebugAnimation, "CShoot: hit robot %d (type=%d, dist=%d)",
+				bestIdx, target.type, bestDist);
+			destroyRobot(bestIdx);
+		}
+	}
+}
+
+// shoot.c DestroyRobot(): player damages a robot.
+// Damage = (epower[0] * weapons^2) << 1. Robot dies when power[1] <= 0.
+void ColonyEngine::destroyRobot(int num) {
+	if (num <= 0 || num > (int)_objects.size())
+		return;
+
+	Thing &obj = _objects[num - 1];
+	if (!obj.alive)
+		return;
+
+	auto qlog = [](int32 x) -> int {
+		int i = 0;
+		while (x > 0) { x >>= 1; i++; }
+		return i;
+	};
+
+	int epower0 = qlog(_me.power[0]);
+	int weapons2 = _weapons * _weapons;
+	int damage = (epower0 * weapons2) << 1;
+
+	// Face robot towards player
+	obj.where.look = obj.where.ang = (uint8)(_me.ang + 128);
+
+	obj.where.power[1] -= damage;
+	debugC(1, kColonyDebugAnimation, "DestroyRobot(%d): type=%d damage=%d remaining_hp=%d",
+		num, obj.type, damage, (int)obj.where.power[1]);
+
+	if (obj.where.power[1] <= 0) {
+		if (obj.count != 0) {
+			// Robot fully destroyed: remove and drop egg
+			obj.alive = 0;
+			int gx = obj.where.xindex;
+			int gy = obj.where.yindex;
+			if (gx >= 0 && gx < 32 && gy >= 0 && gy < 32)
+				_robotArray[gx][gy] = 0;
+			// TODO: explosion sound + visual, spawn egg (foodarray)
+			debugC(1, kColonyDebugAnimation, "Robot %d destroyed!", num);
+		} else {
+			// Robot regresses to egg form
+			obj.where.power[1] = 10 + ((_randomSource.getRandomNumber(15)) << _level);
+			obj.count = 0;
+			// TODO: set grow = -1, change to egg type
+			debugC(1, kColonyDebugAnimation, "Robot %d regressed to egg", num);
+		}
+	}
+}
+
 } // End of namespace Colony
diff --git a/engines/colony/metaengine.cpp b/engines/colony/metaengine.cpp
index 948d0234559..492f2b3de3f 100644
--- a/engines/colony/metaengine.cpp
+++ b/engines/colony/metaengine.cpp
@@ -154,6 +154,12 @@ Common::KeymapArray ColonyMetaEngine::initKeymaps(const char *target) const {
 	act->addDefaultInputMapping("JOY_BACK");
 	engineKeyMap->addAction(act);
 
+	act = new Common::Action("FIRE", _("Fire weapon"));
+	act->setCustomEngineActionEvent(kActionFire);
+	act->addDefaultInputMapping("f");
+	act->addDefaultInputMapping("JOY_B");
+	engineKeyMap->addAction(act);
+
 	return Common::Keymap::arrayOf(engineKeyMap);
 }
 
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 9b088c68413..37c00d73b17 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -329,7 +329,8 @@ void ColonyEngine::drawDashboardMac() {
 			}
 			return i;
 		};
-		const int ePower[3] = { qlog(_corePower[0]), qlog(_corePower[1]), qlog(_corePower[2]) };
+		// power.c DrawInfo(): epower[i] = qlog(Me.power[i]) — player suit power, NOT corepower.
+		const int ePower[3] = { qlog(_me.power[0]), qlog(_me.power[1]), qlog(_me.power[2]) };
 		const bool trouble = (ePower[1] < 6);
 		int wantPictID;
 		if (_armor > 0)


Commit: 6e3ad6184a5a573db3a79aae569b95a38b1f6ba6
    https://github.com/scummvm/scummvm/commit/6e3ad6184a5a573db3a79aae569b95a38b1f6ba6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:34+02:00

Commit Message:
COLONY: initial code for battle

Changed paths:
  A engines/colony/battle.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/debugger.cpp
    engines/colony/debugger.h
    engines/colony/module.mk


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
new file mode 100644
index 00000000000..3082714a982
--- /dev/null
+++ b/engines/colony/battle.cpp
@@ -0,0 +1,691 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// battle.cpp: Outdoor planet surface battle system.
+// Reimplements battle.c from the original Mac source as proper OpenGL 3D.
+// Original used software 2D projection + wireframe; this uses the existing
+// OpenGL renderer with filled polygons and depth-tested 3D.
+
+#include "colony/colony.h"
+#include "colony/gfx.h"
+#include "colony/sound.h"
+#include "common/system.h"
+
+namespace Colony {
+
+// =====================================================================
+// Constants
+// =====================================================================
+static const int kBattleSize = 150;   // BSIZE: collision/spawn radius
+static const int kMaxQuad = 15;       // pyramids per quadrant
+static const int kTankMax = 24;       // turret pincer animation range
+static const int kFloor = 160;        // ground z-offset
+
+// =====================================================================
+// Battle color constants (original Mac QuickDraw pattern indices)
+// Values 0-5 are QuickDraw patterns; >=6 are ObjColor enum values.
+// =====================================================================
+static const int kBLtGray = 1;
+static const int kBBlack  = 4;
+
+// Map battle surface color index to a packed ARGB color.
+static uint32 battleColor(int colorIdx) {
+	switch (colorIdx) {
+	case 0:  return 0xFFFFFFFF; // WHITE
+	case 1:  return 0xFFC0C0C0; // LTGRAY
+	case 2:  return 0xFF808080; // GRAY
+	case 3:  return 0xFF505050; // DKGRAY
+	case 4:  return 0xFF000000; // BLACK
+	case 5:  return 0x00000000; // CLEAR (transparent)
+	// ObjColor values for drone parts
+	case kColorDrone:  return 0xFF8B6914; // brownish yellow
+	case kColorClaw1:  return 0xFF707070; // claw gray 1
+	case kColorClaw2:  return 0xFF505050; // claw gray 2
+	case kColorEyes:   return 0xFFCC0000; // red eyes
+	default: return 0xFFC0C0C0; // fallback light gray
+	}
+}
+
+// =====================================================================
+// 3D Model Data - Rock (pyramid obstacle)
+// Original: base at z=0, peak at z=200. Floor subtracted → base -160, peak 40.
+// We store raw vertices (pre-Floor); draw3DBattlePrism applies zShift=-160.
+// =====================================================================
+static const int kRockPts[5][3] = {
+	{-75,  75, 0},
+	{ 75,  75, 0},
+	{ 75, -75, 0},
+	{-75, -75, 0},
+	{  0,   0, 200}
+};
+static const int kRockSurf[4][8] = {
+	{kBLtGray, 3, 1, 0, 4, 0, 0, 0},
+	{kBLtGray, 3, 2, 1, 4, 0, 0, 0},
+	{kBLtGray, 3, 3, 2, 4, 0, 0, 0},
+	{kBLtGray, 3, 0, 3, 4, 0, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kRockDef = {5, kRockPts, 4, kRockSurf};
+
+// =====================================================================
+// 3D Model Data - Entrance (airlock structure)
+// =====================================================================
+static const int kEntPts[8][3] = {
+	{-300,  300,   0},
+	{ 300,  300,   0},
+	{ 300, -300,   0},
+	{-300, -300,   0},
+	{-250,  250, 600},
+	{ 250,  250, 600},
+	{ 250, -250, 600},
+	{-250, -250, 600}
+};
+static const int kEntSurf[4][8] = {
+	{kBLtGray, 4, 1, 0, 4, 5, 0, 0},
+	{kBLtGray, 4, 0, 3, 7, 4, 0, 0},
+	{kBLtGray, 4, 3, 2, 6, 7, 0, 0},
+	{kBLtGray, 4, 2, 1, 5, 6, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kEntDef = {8, kEntPts, 4, kEntSurf};
+
+static const int kEntDoorPts[4][3] = {
+	{-60,  300,   0},
+	{ 60,  300,   0},
+	{ 50,  275, 200},
+	{-50,  275, 200}
+};
+static const int kEntDoorSurf[1][8] = {
+	{kBLtGray, 4, 0, 3, 2, 1, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kEntDoorDef = {4, kEntDoorPts, 1, kEntDoorSurf};
+
+// =====================================================================
+// 3D Model Data - Shuttle (spaceship)
+// =====================================================================
+// Body (fuselage)
+static const int kSBodyPts[12][3] = {
+	{ 500,  250,   0}, { 500,  350, 200}, { 500,  150, 400},
+	{ 500, -150, 400}, { 500, -350, 200}, { 500, -250,   0},
+	{-500,  250,   0}, {-500,  350, 200}, {-500,  150, 400},
+	{-500, -150, 400}, {-500, -350, 200}, {-500, -250,   0}
+};
+static const int kSBodySurf[4][8] = {
+	{kBLtGray, 4, 0, 6, 7, 1, 0, 0},
+	{kBLtGray, 4, 1, 7, 8, 2, 0, 0},
+	{kBLtGray, 4, 3, 9, 10, 4, 0, 0},
+	{kBLtGray, 4, 4, 10, 11, 5, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kSBodyDef = {12, kSBodyPts, 4, kSBodySurf};
+
+// Front (nose cone)
+static const int kSFrontPts[7][3] = {
+	{ 500,  250,   0}, { 500,  350, 200}, { 500,  150, 400},
+	{ 500, -150, 400}, { 500, -350, 200}, { 500, -250,   0},
+	{ 900,    0,  50}
+};
+static const int kSFrontSurf[6][8] = {
+	{kBLtGray, 3, 0, 1, 6, 0, 0, 0},
+	{kBLtGray, 3, 1, 2, 6, 0, 0, 0},
+	{kBLtGray, 3, 2, 3, 6, 0, 0, 0},
+	{kBLtGray, 3, 3, 4, 6, 0, 0, 0},
+	{kBLtGray, 3, 4, 5, 6, 0, 0, 0},
+	{kBLtGray, 3, 5, 0, 6, 0, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kSFrontDef = {7, kSFrontPts, 6, kSFrontSurf};
+
+// Back (engine section)
+static const int kSBackPts[7][3] = {
+	{-500,  250,   0}, {-500,  350, 200}, {-500,  150, 400},
+	{-500, -150, 400}, {-500, -350, 200}, {-500, -250,   0},
+	{-900,    0, 400}
+};
+static const int kSBackSurf[5][8] = {
+	{kBLtGray, 3, 0, 6, 1, 0, 0, 0},
+	{kBLtGray, 3, 1, 6, 2, 0, 0, 0},
+	{kBLtGray, 3, 3, 6, 4, 0, 0, 0},
+	{kBLtGray, 3, 4, 6, 5, 0, 0, 0},
+	{kBLtGray, 3, 5, 6, 0, 0, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kSBackDef = {7, kSBackPts, 5, kSBackSurf};
+
+// Top fin
+static const int kFTopPts[4][3] = {
+	{ -500, 0, 400}, {-900, 0, 400}, {-1000, 0, 800}, {-700, 0, 800}
+};
+static const int kFTopSurf[1][8] = {
+	{kBLtGray, 4, 0, 1, 2, 3, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kFTopDef = {4, kFTopPts, 1, kFTopSurf};
+
+// Left fin
+static const int kFLeftPts[4][3] = {
+	{-100, -350, 200}, {-700, -350, 200}, {-900, -750, 200}, {-500, -750, 200}
+};
+static const int kFLeftSurf[1][8] = {
+	{kBLtGray, 4, 0, 1, 2, 3, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kFLeftDef = {4, kFLeftPts, 1, kFLeftSurf};
+
+// Right fin
+static const int kFRightPts[4][3] = {
+	{-100, 350, 200}, {-700, 350, 200}, {-900, 750, 200}, {-500, 750, 200}
+};
+static const int kFRightSurf[1][8] = {
+	{kBLtGray, 4, 0, 1, 2, 3, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kFRightDef = {4, kFRightPts, 1, kFRightSurf};
+
+// Shuttle door
+static const int kSDoorPts[4][3] = {
+	{-50, 262, 25}, {50, 262, 25}, {60, 325, 150}, {-60, 325, 150}
+};
+static const int kSDoorSurf[1][8] = {
+	{kBLtGray, 4, 0, 3, 2, 1, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kSDoorDef = {4, kSDoorPts, 1, kSDoorSurf};
+
+// =====================================================================
+// 3D Model Data - Projectile
+// =====================================================================
+static const int kProjPts[5][3] = {
+	{ 80,   0,  80},
+	{-80,  20,  80},
+	{-80,   0, 100},
+	{-80, -20,  80},
+	{-80,   0,  60}
+};
+static const int kProjSurf[5][8] = {
+	{kBBlack,  4, 1, 4, 3, 2, 0, 0},
+	{kBLtGray, 3, 0, 1, 2, 0, 0, 0},
+	{kBLtGray, 3, 0, 2, 3, 0, 0, 0},
+	{kBLtGray, 3, 0, 3, 4, 0, 0, 0},
+	{kBLtGray, 3, 0, 4, 1, 0, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kProjDef = {5, kProjPts, 5, kProjSurf};
+
+// =====================================================================
+// 3D Model Data - Drone (enemy tank)
+// These vertices are NOT Floor-shifted; they float above ground.
+// Drawn with zShift=0 (their z values are in absolute world coords).
+// =====================================================================
+// Abdomen (body)
+static const int kBDroneAbdPts[6][3] = {
+	{0, 0, 170}, {120, 0, 130}, {0, 100, 130},
+	{-130, 0, 130}, {0, -100, 130}, {0, 0, 100}
+};
+static const int kBDroneAbdSurf[8][8] = {
+	{kColorDrone, 3, 0, 1, 2, 0, 0, 0}, {kColorDrone, 3, 0, 2, 3, 0, 0, 0},
+	{kColorDrone, 3, 0, 3, 4, 0, 0, 0}, {kColorDrone, 3, 0, 4, 1, 0, 0, 0},
+	{kColorDrone, 3, 5, 2, 1, 0, 0, 0}, {kColorDrone, 3, 5, 3, 2, 0, 0, 0},
+	{kColorDrone, 3, 5, 4, 3, 0, 0, 0}, {kColorDrone, 3, 5, 1, 4, 0, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kBDroneAbdDef = {6, kBDroneAbdPts, 8, kBDroneAbdSurf};
+
+// Left pincer base points (for rotation source)
+static const int kLLPincerPts[4][3] = {
+	{0, 0, 130}, {50, -2, 130}, {35, -20, 140}, {35, -20, 120}
+};
+// Right pincer base points (for rotation source)
+static const int kRRPincerPts[4][3] = {
+	{0, 0, 130}, {50, 2, 130}, {35, 20, 140}, {35, 20, 120}
+};
+
+// Left pincer surfaces
+static const int kBLPincerSurf[4][8] = {
+	{kColorClaw1, 3, 0, 2, 1, 0, 0, 0}, {kColorClaw1, 3, 0, 1, 3, 0, 0, 0},
+	{kColorClaw2, 3, 0, 3, 2, 0, 0, 0}, {kColorClaw2, 3, 1, 2, 3, 0, 0, 0}
+};
+// Right pincer surfaces
+static const int kBRPincerSurf[4][8] = {
+	{kColorClaw1, 3, 0, 1, 2, 0, 0, 0}, {kColorClaw1, 3, 0, 3, 1, 0, 0, 0},
+	{kColorClaw2, 3, 0, 2, 3, 0, 0, 0}, {kColorClaw2, 3, 1, 3, 2, 0, 0, 0}
+};
+
+// Left eye
+static const int kBLEyePts[3][3] = {
+	{60, 0, 150}, {60, 50, 130}, {60, 25, 150}
+};
+static const int kBLEyeSurf[2][8] = {
+	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kBLEyeDef = {3, kBLEyePts, 2, kBLEyeSurf};
+
+// Right eye
+static const int kBREyePts[3][3] = {
+	{60, 0, 150}, {60, -50, 130}, {60, -25, 150}
+};
+static const int kBREyeSurf[2][8] = {
+	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
+};
+static const ColonyEngine::PrismPartDef kBREyeDef = {3, kBREyePts, 2, kBREyeSurf};
+
+// =====================================================================
+// draw3DBattlePrism: Render a PrismPartDef at a world position.
+// Like draw3DPrism but applies zShift instead of hardcoded -160.
+// zShift = -kFloor for ground objects (rock/entrance/shuttle/projectile).
+// zShift = 0 for floating objects (drones, whose z is absolute).
+// =====================================================================
+void ColonyEngine::draw3DBattlePrism(const PrismPartDef &def, int worldX, int worldY, uint8 ang, int zShift) {
+	// +32 compensates for sine table's 45-degree phase offset
+	const uint8 rotAng = ang + 32;
+	const long rotCos = _cost[rotAng];
+	const long rotSin = _sint[rotAng];
+
+	for (int i = 0; i < def.surfaceCount; i++) {
+		const int colorIdx = def.surfaces[i][0];
+		const int n = def.surfaces[i][1];
+		if (n < 2)
+			continue;
+
+		float px[8], py[8], pz[8];
+		int count = 0;
+
+		for (int j = 0; j < n; j++) {
+			const int cur = def.surfaces[i][j + 2];
+			if (cur < 0 || cur >= def.pointCount)
+				continue;
+
+			int ox = def.points[cur][0];
+			int oy = def.points[cur][1];
+			int oz = def.points[cur][2];
+
+			// Rotate around Z axis by object angle
+			long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
+			long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+
+			px[count] = (float)(rx + worldX);
+			py[count] = (float)(ry + worldY);
+			pz[count] = (float)(oz + zShift);
+			count++;
+		}
+
+		if (count >= 3) {
+			if (colorIdx == 5) // CLEAR: skip
+				continue;
+
+			uint32 fillColor = battleColor(colorIdx);
+			uint32 outlineColor = 0xFF000000; // black outlines
+			_gfx->setWireframe(true, fillColor);
+			_gfx->draw3DPolygon(px, py, pz, count, outlineColor);
+		}
+	}
+}
+
+// =====================================================================
+// battleInit: Initialize all battle data structures and 3D models.
+// Called once at game start.
+// =====================================================================
+void ColonyEngine::battleInit() {
+	// Entrance position
+	_battleEnter.xloc = 16000;
+	_battleEnter.yloc = 16000;
+	_battleEnter.look = _battleEnter.ang = 32;
+
+	// Shuttle position
+	_battleShip.xloc = 0;
+	_battleShip.yloc = 0;
+	_battleShip.look = _battleShip.ang = 32;
+
+	// Mountain parallax
+	_battledx = _width / 59;
+
+	// Generate mountain height profile (smoothed random)
+	int temp[257];
+	for (int i = 0; i < 257; i++)
+		temp[i] = 10 + (0x1F & _randomSource.getRandomNumber(0x7FFF));
+	for (int i = 0; i < 256; i++)
+		_mountains[i] = (temp[i] + temp[i + 1]) >> 1;
+}
+
+// =====================================================================
+// battleSet: Spawn 16 enemies in a 4x4 grid + 15 pyramids per quadrant.
+// Called when entering battle mode.
+// =====================================================================
+void ColonyEngine::battleSet() {
+	for (int i = 0; i < 4; i++) {
+		for (int j = 0; j < 4; j++) {
+			int k = i + j * 4;
+			_bfight[k].xloc = (0x2000 * i + kBattleSize) - 0x3000;
+			_bfight[k].yloc = (0x2000 * j + kBattleSize) - 0x3000;
+			_bfight[k].look = _bfight[k].ang = 0xFF & _randomSource.getRandomNumber(0xFF);
+			_bfight[k].lookx = 0;
+			_bfight[k].delta = 4;
+			_bfight[k].type = kRobCube;
+			_bfight[k].power[1] = 15 + (0x0F & _randomSource.getRandomNumber(0xFF));
+
+			// Spawn pyramids in this quadrant
+			for (int p = 0; p < kMaxQuad; p++) {
+				int fx, fy;
+				do {
+					fx = 0x2000 * i + kBattleSize + (0x1FFF & _randomSource.getRandomNumber(0x7FFF));
+					while (fx > 0x2000 * (i + 1) - kBattleSize)
+						fx = 0x2000 * i + kBattleSize + (0x1FFF & _randomSource.getRandomNumber(0x7FFF));
+					fy = 0x2000 * j + kBattleSize + (0x1FFF & _randomSource.getRandomNumber(0x7FFF));
+					while (fy > 0x2000 * (j + 1) - kBattleSize)
+						fy = 0x2000 * j + kBattleSize + (0x1FFF & _randomSource.getRandomNumber(0x7FFF));
+				} while (
+					(fx > _battleEnter.xloc - 8 * kBattleSize &&
+					 fx < _battleEnter.xloc + 2 * kBattleSize &&
+					 fy > _battleEnter.yloc - 2 * kBattleSize &&
+					 fy < _battleEnter.yloc + 2 * kBattleSize) ||
+					(fx > _battleShip.xloc - 2 * kBattleSize &&
+					 fx < _battleShip.xloc + 2 * kBattleSize &&
+					 fy > _battleShip.yloc - 2 * kBattleSize &&
+					 fy < _battleShip.yloc + 2 * kBattleSize));
+
+				_pyramids[i][j][p].xloc = fx;
+				_pyramids[i][j][p].yloc = fy;
+				_pyramids[i][j][p].ang = 0xFF & _randomSource.getRandomNumber(0xFF);
+				_pyramids[i][j][p].type = kRobPyramid;
+			}
+		}
+	}
+}
+
+// =====================================================================
+// battleBackdrop: Draw 2D sky gradient and ground fill.
+// Called before 3D rendering begins.
+// =====================================================================
+void ColonyEngine::battleBackdrop() {
+	// Sky gradient: dark blue at top → lighter blue toward horizon
+	int bandHeight = MAX(1, _centerY / 16);
+	for (int i = 0; i < 16; i++) {
+		int blue = (i * 16);
+		if (blue > 255) blue = 255;
+		uint32 color = (0xFF << 24) | (0 << 16) | (0 << 8) | blue;
+		Common::Rect band(_screenR.left, _screenR.top + i * bandHeight,
+		                  _screenR.right, _screenR.top + (i + 1) * bandHeight);
+		_gfx->fillRect(band, color);
+	}
+
+	// Ground fill (below horizon)
+	uint32 groundColor = 0xFF404040;
+	Common::Rect ground(_screenR.left, _centerY, _screenR.right, _screenR.bottom);
+	_gfx->fillRect(ground, groundColor);
+
+	// Mountain silhouette
+	uint32 mtColor = 0xFF606060;
+	uint8 ang = _me.look;
+	int xloc = -_battledx;
+	if (ang & 0x01) {
+		xloc += _battledx;
+		ang--;
+	}
+
+	int sunx = -1;
+	bool sunon = false;
+
+	int prevX = xloc;
+	int prevY = _centerY - _mountains[ang];
+	for (int i = 0; i < 63; i += 2) {
+		xloc += 2 * _battledx;
+		uint8 prevAng = ang;
+		ang -= 2;
+		// Detect sun position (when angle wraps through 0)
+		if (ang > prevAng) { // unsigned wrap
+			sunx = xloc - _battledx;
+			sunon = true;
+		}
+		int curY = _centerY - _mountains[ang];
+		_gfx->drawLine(prevX, prevY, xloc, curY, mtColor);
+		prevX = xloc;
+		prevY = curY;
+	}
+
+	// Sun
+	if (sunon && sunx >= 0) {
+		int ht = _centerY >> 3;
+		_gfx->fillEllipse(sunx, 5 + ht, ht, ht, 0xFFFFFF00);
+	}
+}
+
+// =====================================================================
+// battleDrawPyramids: Render visible pyramid obstacles.
+// Determines which 6 quadrants are visible based on player facing,
+// then renders each pyramid via draw3DBattlePrism.
+// =====================================================================
+void ColonyEngine::battleDrawPyramids() {
+	int xloc = _me.xloc;
+	int yloc = _me.yloc;
+	if (xloc < 0) xloc += 0x7FFF;
+	if (yloc < 0) yloc += 0x7FFF;
+	int qx = xloc >> 13;
+	int qy = yloc >> 13;
+	int quad = _me.look >> 6; // 0=N, 1=W, 2=S, 3=E
+
+	// Render 6 quadrants based on facing direction
+	// Each quadrant: render all pyramids within view distance
+	auto renderQuadrant = [&](int qxi, int qyi) {
+		int dx = 0, dy = 0;
+		if (qxi < 0) { qxi = 3; dx = 0x7FFF; }
+		if (qxi > 3) { qxi = 0; dx = 0x7FFF; }
+		if (qyi < 0) { qyi = 3; dy = 0x7FFF; }
+		if (qyi > 3) { qyi = 0; dy = 0x7FFF; }
+
+		for (int i = 0; i < kMaxQuad; i++) {
+			long pxloc = _pyramids[qxi][qyi][i].xloc - (xloc + dx);
+			long pyloc = _pyramids[qxi][qyi][i].yloc - (yloc + dy);
+			if (ABS(pxloc) + ABS(pyloc) < 8000) {
+				// Within render distance — draw as 3D
+				int wx = _pyramids[qxi][qyi][i].xloc;
+				int wy = _pyramids[qxi][qyi][i].yloc;
+				if (dx) wx -= dx;
+				if (dy) wy -= dy;
+				draw3DBattlePrism(kRockDef, wx, wy,
+				                  _pyramids[qxi][qyi][i].ang, -kFloor);
+
+				// Track for hit detection
+				if (_battleMaxP < 100) {
+					_battlePwh[_battleMaxP] = &_pyramids[qxi][qyi][i];
+					_battleMaxP++;
+				}
+			}
+		}
+	};
+
+	switch (quad) {
+	case 0: // North
+		renderQuadrant(qx, qy);
+		renderQuadrant(qx - 1, qy);
+		renderQuadrant(qx + 1, qy);
+		renderQuadrant(qx, qy + 1);
+		renderQuadrant(qx - 1, qy + 1);
+		renderQuadrant(qx + 1, qy + 1);
+		break;
+	case 1: // West
+		renderQuadrant(qx, qy);
+		renderQuadrant(qx, qy - 1);
+		renderQuadrant(qx, qy + 1);
+		renderQuadrant(qx - 1, qy);
+		renderQuadrant(qx - 1, qy - 1);
+		renderQuadrant(qx - 1, qy + 1);
+		break;
+	case 2: // South
+		renderQuadrant(qx, qy);
+		renderQuadrant(qx - 1, qy);
+		renderQuadrant(qx + 1, qy);
+		renderQuadrant(qx, qy - 1);
+		renderQuadrant(qx - 1, qy - 1);
+		renderQuadrant(qx + 1, qy - 1);
+		break;
+	case 3: // East
+		renderQuadrant(qx, qy);
+		renderQuadrant(qx, qy - 1);
+		renderQuadrant(qx, qy + 1);
+		renderQuadrant(qx + 1, qy);
+		renderQuadrant(qx + 1, qy - 1);
+		renderQuadrant(qx + 1, qy + 1);
+		break;
+	}
+}
+
+// =====================================================================
+// battleDrawTanks: Render enemies, entrance, shuttle, projectile.
+// =====================================================================
+void ColonyEngine::battleDrawTanks() {
+	_insight = false;
+
+	// --- 16 enemy drones ---
+	for (int i = 0; i < 16; i++) {
+		long dxloc = _bfight[i].xloc - _me.xloc;
+		long dyloc = _bfight[i].yloc - _me.yloc;
+		if (ABS(dxloc) + ABS(dyloc) >= 8000)
+			continue;
+
+		uint8 droneAng = _bfight[i].ang;
+
+		// Abdomen
+		draw3DBattlePrism(kBDroneAbdDef, _bfight[i].xloc, _bfight[i].yloc, droneAng, 0);
+
+		// Animated pincers: rotate base points by lookx offset
+		_bfight[i].lookx += _bfight[i].delta;
+		if (_bfight[i].lookx < -kTankMax || _bfight[i].lookx > kTankMax)
+			_bfight[i].delta = -_bfight[i].delta;
+
+		// Build animated left pincer vertices
+		int lPincerPts[4][3];
+		int nabs_lookx = (_bfight[i].lookx > 0) ? -_bfight[i].lookx : _bfight[i].lookx; // nabs
+		int lLook = nabs_lookx - 32;
+		if (lLook < 0) lLook += 256;
+		for (int j = 0; j < 4; j++) {
+			long tcos = _cost[(uint8)lLook];
+			long tsin = _sint[(uint8)lLook];
+			lPincerPts[j][0] = (int)(((long)kLLPincerPts[j][0] * tcos - (long)kLLPincerPts[j][1] * tsin) >> 7);
+			lPincerPts[j][1] = (int)(((long)kLLPincerPts[j][0] * tsin + (long)kLLPincerPts[j][1] * tcos) >> 7);
+			lPincerPts[j][2] = kLLPincerPts[j][2];
+			lPincerPts[j][0] += 120; // offset from abdomen center
+		}
+		PrismPartDef lPincerDef = {4, lPincerPts, 4, kBLPincerSurf};
+		draw3DBattlePrism(lPincerDef, _bfight[i].xloc, _bfight[i].yloc, droneAng, 0);
+
+		// Build animated right pincer vertices
+		int rPincerPts[4][3];
+		int rLook = ABS(_bfight[i].lookx) - 32;
+		if (rLook < 0) rLook += 256;
+		for (int j = 0; j < 4; j++) {
+			long tcos = _cost[(uint8)rLook];
+			long tsin = _sint[(uint8)rLook];
+			rPincerPts[j][0] = (int)(((long)kRRPincerPts[j][0] * tcos - (long)kRRPincerPts[j][1] * tsin) >> 7);
+			rPincerPts[j][1] = (int)(((long)kRRPincerPts[j][0] * tsin + (long)kRRPincerPts[j][1] * tcos) >> 7);
+			rPincerPts[j][2] = kRRPincerPts[j][2];
+			rPincerPts[j][0] += 120;
+		}
+		PrismPartDef rPincerDef = {4, rPincerPts, 4, kBRPincerSurf};
+		draw3DBattlePrism(rPincerDef, _bfight[i].xloc, _bfight[i].yloc, droneAng, 0);
+
+		// Eyes
+		draw3DBattlePrism(kBLEyeDef, _bfight[i].xloc, _bfight[i].yloc, droneAng, 0);
+		draw3DBattlePrism(kBREyeDef, _bfight[i].xloc, _bfight[i].yloc, droneAng, 0);
+
+		// Track for hit detection
+		if (_battleMaxP < 100) {
+			_battlePwh[_battleMaxP] = &_bfight[i];
+			_bfight[i].dist = (int)(ABS(dxloc) + ABS(dyloc)); // manhattan approx
+			_battleMaxP++;
+		}
+
+		// Check if enemy is in crosshair (simple screen-center check)
+		// TODO: proper insight detection via projected screen bounds
+	}
+
+	// --- Projectile ---
+	if (_projon) {
+		long pxloc = _battleProj.xloc - _me.xloc;
+		long pyloc = _battleProj.yloc - _me.yloc;
+		if (ABS(pxloc) + ABS(pyloc) < 20000) {
+			draw3DBattlePrism(kProjDef, _battleProj.xloc, _battleProj.yloc,
+			                  _battleProj.ang, -kFloor);
+		}
+	}
+
+	// --- Entrance ---
+	{
+		long exloc = _battleEnter.xloc - _me.xloc;
+		long eyloc = _battleEnter.yloc - _me.yloc;
+		if (ABS(exloc) + ABS(eyloc) < 20000) {
+			draw3DBattlePrism(kEntDef, _battleEnter.xloc, _battleEnter.yloc,
+			                  _battleEnter.ang, -kFloor);
+			draw3DBattlePrism(kEntDoorDef, _battleEnter.xloc, _battleEnter.yloc,
+			                  _battleEnter.ang, -kFloor);
+			if (_battleMaxP < 100) {
+				_battlePwh[_battleMaxP] = &_battleEnter;
+				_battleMaxP++;
+			}
+		}
+	}
+
+	// --- Shuttle (only if not in orbit) ---
+	if (!_orbit) {
+		long sxloc = _battleShip.xloc - _me.xloc;
+		long syloc = _battleShip.yloc - _me.yloc;
+		if (ABS(sxloc) + ABS(syloc) < 20000) {
+			draw3DBattlePrism(kSBodyDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			draw3DBattlePrism(kSFrontDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			draw3DBattlePrism(kSBackDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			// Fins: force-draw (no backface cull) — single-sided surfaces
+			draw3DBattlePrism(kFTopDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			draw3DBattlePrism(kFLeftDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			draw3DBattlePrism(kFRightDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			draw3DBattlePrism(kSDoorDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			if (_battleMaxP < 100) {
+				_battlePwh[_battleMaxP] = &_battleShip;
+				_battleMaxP++;
+			}
+		}
+	}
+}
+
+// =====================================================================
+// renderBattle: Main battle rendering dispatch.
+// Called from the main loop when _gameMode == kModeBattle.
+// =====================================================================
+void ColonyEngine::renderBattle() {
+	_battleMaxP = 0;
+
+	// Phase 1: 2D backdrop (sky gradient, mountains, sun)
+	battleBackdrop();
+
+	// Phase 2: Begin 3D scene
+	// Camera at player position, eye level (z=0), looking in _me.look direction.
+	// Vertical look pitches 3D objects; backdrop (sky + ground) is 2D so it stays fixed.
+	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
+
+	// 3D objects with depth testing (ground is part of 2D backdrop)
+	_gfx->setDepthState(true, true);
+	_gfx->setDepthRange(0.0, 1.0);
+
+	// Draw pyramids (obstacles)
+	battleDrawPyramids();
+
+	// Draw tanks, entrance, shuttle, projectile
+	battleDrawTanks();
+
+	// Phase 5: End 3D
+	_gfx->end3D();
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index bb09d0bfc4a..b0888f835b6 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -593,6 +593,9 @@ 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;
 			}
 			if (mouseDY != 0) {
 				_me.lookY = (int8)CLIP<int>((int)_me.lookY - (mouseDY * _mouseSensitivity), -64, 64);
@@ -614,21 +617,43 @@ Common::Error ColonyEngine::run() {
 			const int moveY = (_sint[_me.look] * (1 << _speedShift)) >> 4;
 			const int rotSpeed = 1 << (_speedShift - 1);
 
-			if (_moveForward)
-				cCommand(_me.xloc + moveX, _me.yloc + moveY, true);
-			if (_moveBackward)
-				cCommand(_me.xloc - moveX, _me.yloc - moveY, true);
-			if (_strafeLeft) {
-				uint8 strafeAngle = (uint8)((int)_me.look + 64);
-				int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
-				int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
-				cCommand(_me.xloc + sx, _me.yloc + sy, true);
-			}
-			if (_strafeRight) {
-				uint8 strafeAngle = (uint8)((int)_me.look - 64);
-				int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
-				int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
-				cCommand(_me.xloc + sx, _me.yloc + sy, true);
+			if (_gameMode == kModeBattle) {
+				// Battle: direct movement, no wall collision
+				if (_moveForward) {
+					_me.xloc += moveX;
+					_me.yloc += moveY;
+				}
+				if (_moveBackward) {
+					_me.xloc -= moveX;
+					_me.yloc -= moveY;
+				}
+				if (_strafeLeft) {
+					uint8 strafeAngle = (uint8)((int)_me.look + 64);
+					_me.xloc += (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
+					_me.yloc += (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+				}
+				if (_strafeRight) {
+					uint8 strafeAngle = (uint8)((int)_me.look - 64);
+					_me.xloc += (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
+					_me.yloc += (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+				}
+			} else {
+				if (_moveForward)
+					cCommand(_me.xloc + moveX, _me.yloc + moveY, true);
+				if (_moveBackward)
+					cCommand(_me.xloc - moveX, _me.yloc - moveY, true);
+				if (_strafeLeft) {
+					uint8 strafeAngle = (uint8)((int)_me.look + 64);
+					int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
+					int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+					cCommand(_me.xloc + sx, _me.yloc + sy, true);
+				}
+				if (_strafeRight) {
+					uint8 strafeAngle = (uint8)((int)_me.look - 64);
+					int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
+					int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+					cCommand(_me.xloc + sx, _me.yloc + sy, true);
+				}
 			}
 			if (_rotateLeft) {
 				_me.ang += rotSpeed;
@@ -640,12 +665,16 @@ Common::Error ColonyEngine::run() {
 			}
 		}
 
-		_gfx->clear((_corePower[_coreIndex] > 0) ? 15 : 0);
-
-		corridor();
-		drawDashboardStep1();
-		drawCrosshair();
-		checkCenter();
+		if (_gameMode == kModeBattle) {
+			renderBattle();
+			drawCrosshair();
+		} else {
+			_gfx->clear((_corePower[_coreIndex] > 0) ? 15 : 0);
+			corridor();
+			drawDashboardStep1();
+			drawCrosshair();
+			checkCenter();
+		}
 
 		// Draw Mac menu bar overlay (render directly to our surface, skip WM's
 		// g_system->copyRectToScreen which conflicts with the OpenGL backend)
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index a32c7065942..391a2d6a164 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -57,6 +57,11 @@ enum ColonyAction {
 	kActionFire
 };
 
+enum GameMode {
+	kModeColony = 2,
+	kModeBattle = 1
+};
+
 enum WallFeatureType {
 	kWallFeatureNone = 0,
 	kWallFeatureDoor = 2,
@@ -436,6 +441,21 @@ private:
 	int _action0, _action1;
 	int _creature;
 
+	// Battle state (battle.c)
+	int _gameMode = kModeColony;
+	Locate _bfight[16];           // 16 battle enemies
+	Locate _battleEnter;          // entrance structure
+	Locate _battleShip;           // shuttle
+	Locate _battleProj;           // enemy projectile
+	bool _projon = false;         // projectile active
+	int _pcount = 0;              // projectile countdown
+	int _mountains[256];          // mountain height profile
+	int _battledx = 0;            // mountain parallax divisor (Width/59)
+	int _battleRound = 0;         // AI round-robin counter
+	Locate *_battlePwh[100];      // visible object pointers (for hit detection)
+	int _battleMaxP = 0;          // count of visible objects
+	Locate _pyramids[4][4][15];   // pyramid obstacles: 4x4 quadrants, 15 each
+
 	// PATCH.C: object relocation + wall state persistence
 	Common::Array<PatchEntry> _patches;
 	PassPatch _carryPatch[2];   // [0]=forklift, [1]=carried object
@@ -513,6 +533,15 @@ private:
 	void cShoot();
 	void destroyRobot(int num);
 
+	// battle.c: outdoor battle system (OpenGL 3D)
+	void battleInit();
+	void battleSet();
+	void renderBattle();
+	void draw3DBattlePrism(const PrismPartDef &def, int worldX, int worldY, uint8 ang, int zShift = 0);
+	void battleBackdrop();
+	void battleDrawPyramids();
+	void battleDrawTanks();
+
 	// PATCH.C: object relocation + wall state persistence
 	void createObject(int type, int xloc, int yloc, uint8 ang);
 	void doPatch();
diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
index 5a4e5d85f6b..eae34ad804b 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/debugger.cpp
@@ -70,6 +70,7 @@ Debugger::Debugger(ColonyEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("give", WRAP_METHOD(Debugger, cmdGive));
 	registerCmd("power", WRAP_METHOD(Debugger, cmdPower));
 	registerCmd("core", WRAP_METHOD(Debugger, cmdCore));
+	registerCmd("battle", WRAP_METHOD(Debugger, cmdBattle));
 }
 
 bool Debugger::cmdTeleport(int argc, const char **argv) {
@@ -308,4 +309,37 @@ bool Debugger::cmdCore(int argc, const char **argv) {
 	return true;
 }
 
+bool Debugger::cmdBattle(int argc, const char **argv) {
+	// Initialize battle data if not already done
+	_vm->battleInit();
+	_vm->battleSet();
+
+	// Place player just outside the entrance (Enter is at 16000, 16000)
+	// Original BattleCommand entrance check: x in [Enter.xloc-2*BSIZE, Enter.xloc)
+	// Spawn facing the entrance (angle ~96 = east-ish toward entrance)
+	_vm->_me.xloc = 16000 - 500;
+	_vm->_me.yloc = 16000;
+	_vm->_me.ang = 96;
+	_vm->_me.look = 96;
+
+	// Ensure suit and weapons are set up for combat
+	_vm->_me.power[0] = 256; // weapons power
+	_vm->_me.power[1] = 256; // life power
+	_vm->_me.power[2] = 256; // armor power
+	_vm->_weapons = 3;
+	_vm->_armor = 3;
+	_vm->_hasKeycard = true;
+
+	// Switch to battle mode
+	_vm->_gameMode = kModeBattle;
+	_vm->_level = 0;
+
+	debugPrintf("Entered battle mode at (%d, %d) ang=%d\n",
+	            _vm->_me.xloc, _vm->_me.yloc, _vm->_me.ang);
+	debugPrintf("Suit: power=[%d,%d,%d] weapons=%d armor=%d\n",
+	            (int)_vm->_me.power[0], (int)_vm->_me.power[1],
+	            (int)_vm->_me.power[2], _vm->_weapons, _vm->_armor);
+	return true;
+}
+
 } // End of namespace Colony
diff --git a/engines/colony/debugger.h b/engines/colony/debugger.h
index 3e225f95d7e..b3ddb64f445 100644
--- a/engines/colony/debugger.h
+++ b/engines/colony/debugger.h
@@ -42,6 +42,7 @@ private:
 	bool cmdGive(int argc, const char **argv);
 	bool cmdPower(int argc, const char **argv);
 	bool cmdCore(int argc, const char **argv);
+	bool cmdBattle(int argc, const char **argv);
 };
 
 }
diff --git a/engines/colony/module.mk b/engines/colony/module.mk
index 85d941fe62b..f2e5b391f56 100644
--- a/engines/colony/module.mk
+++ b/engines/colony/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/colony
 
 MODULE_OBJS := \
 	animation.o \
+	battle.o \
 	colony.o \
 	debugger.o \
 	gfx.o \


Commit: 5160af2837e46aab273057e85a34a177695afe37
    https://github.com/scummvm/scummvm/commit/5160af2837e46aab273057e85a34a177695afe37
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:34+02:00

Commit Message:
COLONY: add perspective battle horizon and fix debugger command registration

Changed paths:
    engines/colony/battle.cpp
    engines/colony/debugger.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 3082714a982..eb4891bde4a 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -28,6 +28,7 @@
 #include "colony/gfx.h"
 #include "colony/sound.h"
 #include "common/system.h"
+#include <math.h>
 
 namespace Colony {
 
@@ -38,6 +39,7 @@ static const int kBattleSize = 150;   // BSIZE: collision/spawn radius
 static const int kMaxQuad = 15;       // pyramids per quadrant
 static const int kTankMax = 24;       // turret pincer animation range
 static const int kFloor = 160;        // ground z-offset
+static const float kBattleFovY = 75.0f;
 
 // =====================================================================
 // Battle color constants (original Mac QuickDraw pattern indices)
@@ -64,6 +66,16 @@ static uint32 battleColor(int colorIdx) {
 	}
 }
 
+static int battleHorizonY(const Common::Rect &screenR, int lookY) {
+	const float halfHeight = screenR.height() * 0.5f;
+	const float centerY = screenR.top + halfHeight;
+	const float clampedLookY = CLIP<float>((float)lookY, -63.5f, 63.5f);
+	const float pitchRad = clampedLookY * 2.0f * (float)M_PI / 256.0f;
+	const float focalY = halfHeight / tanf(kBattleFovY * (float)M_PI / 360.0f);
+
+	return (int)roundf(centerY - focalY * tanf(pitchRad));
+}
+
 // =====================================================================
 // 3D Model Data - Rock (pyramid obstacle)
 // Original: base at z=0, peak at z=200. Floor subtracted → base -160, peak 40.
@@ -404,21 +416,31 @@ void ColonyEngine::battleSet() {
 // Called before 3D rendering begins.
 // =====================================================================
 void ColonyEngine::battleBackdrop() {
+	const int horizonY = battleHorizonY(_screenR, _me.lookY);
+	const int skyBottom = CLIP<int>(horizonY, _screenR.top, _screenR.bottom);
+	const int pitchOffset = horizonY - _centerY;
+
 	// Sky gradient: dark blue at top → lighter blue toward horizon
-	int bandHeight = MAX(1, _centerY / 16);
 	for (int i = 0; i < 16; i++) {
+		const int bandTop = _screenR.top + ((skyBottom - _screenR.top) * i) / 16;
+		const int bandBottom = _screenR.top + ((skyBottom - _screenR.top) * (i + 1)) / 16;
+		if (bandBottom <= bandTop)
+			continue;
+
 		int blue = (i * 16);
-		if (blue > 255) blue = 255;
+		if (blue > 255)
+			blue = 255;
 		uint32 color = (0xFF << 24) | (0 << 16) | (0 << 8) | blue;
-		Common::Rect band(_screenR.left, _screenR.top + i * bandHeight,
-		                  _screenR.right, _screenR.top + (i + 1) * bandHeight);
+		Common::Rect band(_screenR.left, bandTop, _screenR.right, bandBottom);
 		_gfx->fillRect(band, color);
 	}
 
 	// Ground fill (below horizon)
 	uint32 groundColor = 0xFF404040;
-	Common::Rect ground(_screenR.left, _centerY, _screenR.right, _screenR.bottom);
-	_gfx->fillRect(ground, groundColor);
+	Common::Rect ground(_screenR.left, CLIP<int>(horizonY, _screenR.top, _screenR.bottom),
+	                    _screenR.right, _screenR.bottom);
+	if (ground.bottom > ground.top)
+		_gfx->fillRect(ground, groundColor);
 
 	// Mountain silhouette
 	uint32 mtColor = 0xFF606060;
@@ -433,7 +455,7 @@ void ColonyEngine::battleBackdrop() {
 	bool sunon = false;
 
 	int prevX = xloc;
-	int prevY = _centerY - _mountains[ang];
+	int prevY = horizonY - _mountains[ang];
 	for (int i = 0; i < 63; i += 2) {
 		xloc += 2 * _battledx;
 		uint8 prevAng = ang;
@@ -443,7 +465,7 @@ void ColonyEngine::battleBackdrop() {
 			sunx = xloc - _battledx;
 			sunon = true;
 		}
-		int curY = _centerY - _mountains[ang];
+		int curY = horizonY - _mountains[ang];
 		_gfx->drawLine(prevX, prevY, xloc, curY, mtColor);
 		prevX = xloc;
 		prevY = curY;
@@ -451,8 +473,8 @@ void ColonyEngine::battleBackdrop() {
 
 	// Sun
 	if (sunon && sunx >= 0) {
-		int ht = _centerY >> 3;
-		_gfx->fillEllipse(sunx, 5 + ht, ht, ht, 0xFFFFFF00);
+		int ht = _screenR.height() >> 4;
+		_gfx->fillEllipse(sunx, 5 + ht + pitchOffset, ht, ht, 0xFFFFFF00);
 	}
 }
 
@@ -666,15 +688,24 @@ void ColonyEngine::battleDrawTanks() {
 void ColonyEngine::renderBattle() {
 	_battleMaxP = 0;
 
-	// Phase 1: 2D backdrop (sky gradient, mountains, sun)
+	// Phase 1: 2D backdrop (sky gradient, mountains, sun) follows camera pitch
 	battleBackdrop();
 
-	// Phase 2: Begin 3D scene
-	// Camera at player position, eye level (z=0), looking in _me.look direction.
-	// Vertical look pitches 3D objects; backdrop (sky + ground) is 2D so it stays fixed.
+	// Phase 2: Begin 3D scene with the full battle camera transform.
 	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
 
-	// 3D objects with depth testing (ground is part of 2D backdrop)
+	// Phase 3: Ground plane at z=-160.
+	{
+		uint32 groundColor = 0xFF404040;
+		_gfx->setDepthState(false, false);
+		_gfx->setWireframe(true, groundColor);
+		_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.0f,
+		                  100000.0f, -100000.0f, -160.0f,
+		                  100000.0f,  100000.0f, -160.0f,
+		                 -100000.0f,  100000.0f, -160.0f, groundColor);
+	}
+
+	// Phase 4: 3D objects with depth testing.
 	_gfx->setDepthState(true, true);
 	_gfx->setDepthRange(0.0, 1.0);
 
diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
index eae34ad804b..4e9d3731e59 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/debugger.cpp
@@ -143,7 +143,7 @@ bool Debugger::cmdTeleport(int argc, const char **argv) {
 	_vm->_robotArray[targetX][targetY] = kMeNum;
 
 	debugPrintf("Teleported to level %d at (%d, %d)\n", _vm->_level, targetX, targetY);
-	return true;
+	return false;
 }
 
 bool Debugger::cmdPos(int argc, const char **argv) {
@@ -339,7 +339,7 @@ bool Debugger::cmdBattle(int argc, const char **argv) {
 	debugPrintf("Suit: power=[%d,%d,%d] weapons=%d armor=%d\n",
 	            (int)_vm->_me.power[0], (int)_vm->_me.power[1],
 	            (int)_vm->_me.power[2], _vm->_weapons, _vm->_armor);
-	return true;
+	return false;
 }
 
 } // End of namespace Colony


Commit: 1cc5cfb3c8c9b08db7acf70ecd06490d3653903e
    https://github.com/scummvm/scummvm/commit/1cc5cfb3c8c9b08db7acf70ecd06490d3653903e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:34+02:00

Commit Message:
COLONY: movement and targets in battle mode

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


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index eb4891bde4a..95d6bf87ad4 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -76,6 +76,108 @@ static int battleHorizonY(const Common::Rect &screenR, int lookY) {
 	return (int)roundf(centerY - focalY * tanf(pitchRad));
 }
 
+static int battlePowerLevel(int32 power) {
+	int level = 0;
+	while (power > 0) {
+		power >>= 1;
+		level++;
+	}
+	return level;
+}
+
+static int battleNormalizeCoord(int coord) {
+	return (int16)coord;
+}
+
+static int wrapBattleCoord(int coord) {
+	coord = battleNormalizeCoord(coord);
+	if (coord < 0)
+		coord += 0x8000;
+	return coord;
+}
+
+static bool battleOriginVisible(long relX, long relY, uint8 look, const int *sint, const int *cost,
+                                int &side, int &forward) {
+	forward = (int)((relX * cost[look] + relY * sint[look]) >> 7);
+	if (forward <= 0)
+		return false;
+
+	side = (int)((relX * sint[look] - relY * cost[look]) >> 7);
+	return forward > ABS(side);
+}
+
+static bool battleProjectPoint(const Common::Rect &screenR, uint8 look, int8 lookY, const int *sint,
+                               const int *cost, int camX, int camY,
+                               float worldX, float worldY, float worldZ,
+                               int &screenX, int &screenY) {
+	const float dx = worldX - camX;
+	const float dy = worldY - camY;
+	const float dz = worldZ;
+
+	const float sinYaw = sint[look] / 128.0f;
+	const float cosYaw = cost[look] / 128.0f;
+	const float side = dx * sinYaw - dy * cosYaw;
+	const float forward = dx * cosYaw + dy * sinYaw;
+
+	const float pitchRad = lookY * 2.0f * (float)M_PI / 256.0f;
+	const float sinPitch = sinf(pitchRad);
+	const float cosPitch = cosf(pitchRad);
+
+	const float eyeX = side;
+	const float eyeY = dz * cosPitch + forward * sinPitch;
+	const float eyeZ = dz * sinPitch - forward * cosPitch;
+	if (eyeZ >= -1.0f)
+		return false;
+
+	const float focal = (screenR.height() * 0.5f) / tanf(kBattleFovY * (float)M_PI / 360.0f);
+	const float centerX = screenR.left + screenR.width() * 0.5f;
+	const float centerY = screenR.top + screenR.height() * 0.5f;
+
+	screenX = (int)roundf(centerX + (eyeX * focal / -eyeZ));
+	screenY = (int)roundf(centerY - (eyeY * focal / -eyeZ));
+	return true;
+}
+
+static void battleResetBounds(const Common::Rect &screenR, Locate &loc) {
+	loc.xmn = screenR.right;
+	loc.xmx = screenR.left;
+	loc.zmn = screenR.bottom;
+	loc.zmx = screenR.top;
+}
+
+static bool battleAccumulateBounds(const Common::Rect &screenR, const ColonyEngine::PrismPartDef &def,
+                                   Locate &loc, int worldX, int worldY, uint8 ang, int zShift,
+                                   uint8 look, int8 lookY, const int *sint, const int *cost,
+                                   int camX, int camY) {
+	const uint8 rotAng = ang + 32;
+	const long rotCos = cost[rotAng];
+	const long rotSin = sint[rotAng];
+	bool hasPoint = false;
+
+	for (int i = 0; i < def.pointCount; i++) {
+		const int ox = def.points[i][0];
+		const int oy = def.points[i][1];
+		const int oz = def.points[i][2];
+
+		const long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
+		const long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+
+		int sx = 0;
+		int sy = 0;
+		if (!battleProjectPoint(screenR, look, lookY, sint, cost, camX, camY,
+		                        (float)(rx + worldX), (float)(ry + worldY), (float)(oz + zShift), sx, sy))
+			continue;
+
+		loc.xmn = MIN(loc.xmn, sx);
+		loc.xmx = MAX(loc.xmx, sx);
+		loc.zmn = MIN(loc.zmn, sy);
+		loc.zmx = MAX(loc.zmx, sy);
+		hasPoint = true;
+	}
+
+	return hasPoint;
+}
+
 // =====================================================================
 // 3D Model Data - Rock (pyramid obstacle)
 // Original: base at z=0, peak at z=200. Floor subtracted → base -160, peak 40.
@@ -355,6 +457,10 @@ void ColonyEngine::battleInit() {
 	_battleShip.yloc = 0;
 	_battleShip.look = _battleShip.ang = 32;
 
+	_battleRound = 0;
+	_projon = false;
+	_pcount = 0;
+
 	// Mountain parallax
 	_battledx = _width / 59;
 
@@ -484,10 +590,8 @@ void ColonyEngine::battleBackdrop() {
 // then renders each pyramid via draw3DBattlePrism.
 // =====================================================================
 void ColonyEngine::battleDrawPyramids() {
-	int xloc = _me.xloc;
-	int yloc = _me.yloc;
-	if (xloc < 0) xloc += 0x7FFF;
-	if (yloc < 0) yloc += 0x7FFF;
+	int xloc = wrapBattleCoord(_me.xloc);
+	int yloc = wrapBattleCoord(_me.yloc);
 	int qx = xloc >> 13;
 	int qy = yloc >> 13;
 	int quad = _me.look >> 6; // 0=N, 1=W, 2=S, 3=E
@@ -502,22 +606,32 @@ void ColonyEngine::battleDrawPyramids() {
 		if (qyi > 3) { qyi = 0; dy = 0x7FFF; }
 
 		for (int i = 0; i < kMaxQuad; i++) {
-			long pxloc = _pyramids[qxi][qyi][i].xloc - (xloc + dx);
-			long pyloc = _pyramids[qxi][qyi][i].yloc - (yloc + dy);
-			if (ABS(pxloc) + ABS(pyloc) < 8000) {
-				// Within render distance — draw as 3D
-				int wx = _pyramids[qxi][qyi][i].xloc;
-				int wy = _pyramids[qxi][qyi][i].yloc;
-				if (dx) wx -= dx;
-				if (dy) wy -= dy;
-				draw3DBattlePrism(kRockDef, wx, wy,
-				                  _pyramids[qxi][qyi][i].ang, -kFloor);
-
-				// Track for hit detection
-				if (_battleMaxP < 100) {
-					_battlePwh[_battleMaxP] = &_pyramids[qxi][qyi][i];
-					_battleMaxP++;
-				}
+			long relX = _pyramids[qxi][qyi][i].xloc - (xloc + dx);
+			long relY = _pyramids[qxi][qyi][i].yloc - (yloc + dy);
+			if (ABS(relX) + ABS(relY) >= 8000)
+				continue;
+
+			int side = 0;
+			int forward = 0;
+			if (!battleOriginVisible(relX, relY, _me.look, _sint, _cost, side, forward))
+				continue;
+
+			Locate &pyr = _pyramids[qxi][qyi][i];
+			battleResetBounds(_screenR, pyr);
+
+			int wx = pyr.xloc;
+			int wy = pyr.yloc;
+			if (dx) wx -= dx;
+			if (dy) wy -= dy;
+
+			battleAccumulateBounds(_screenR, kRockDef, pyr, wx, wy, pyr.ang, -kFloor,
+			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			pyr.dist = forward;
+			draw3DBattlePrism(kRockDef, wx, wy, pyr.ang, -kFloor);
+
+			if (_battleMaxP < 100) {
+				_battlePwh[_battleMaxP] = &pyr;
+				_battleMaxP++;
 			}
 		}
 	};
@@ -566,24 +680,33 @@ void ColonyEngine::battleDrawTanks() {
 
 	// --- 16 enemy drones ---
 	for (int i = 0; i < 16; i++) {
-		long dxloc = _bfight[i].xloc - _me.xloc;
-		long dyloc = _bfight[i].yloc - _me.yloc;
-		if (ABS(dxloc) + ABS(dyloc) >= 8000)
+		long relX = _bfight[i].xloc - _me.xloc;
+		long relY = _bfight[i].yloc - _me.yloc;
+		if (ABS(relX) + ABS(relY) >= 8000)
+			continue;
+
+		int side = 0;
+		int forward = 0;
+		if (!battleOriginVisible(relX, relY, _me.look, _sint, _cost, side, forward))
 			continue;
 
+		Locate &drone = _bfight[i];
+		battleResetBounds(_screenR, drone);
 		uint8 droneAng = _bfight[i].ang;
 
 		// Abdomen
-		draw3DBattlePrism(kBDroneAbdDef, _bfight[i].xloc, _bfight[i].yloc, droneAng, 0);
+		battleAccumulateBounds(_screenR, kBDroneAbdDef, drone, drone.xloc, drone.yloc, droneAng, 0,
+		                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+		draw3DBattlePrism(kBDroneAbdDef, drone.xloc, drone.yloc, droneAng, 0);
 
 		// Animated pincers: rotate base points by lookx offset
-		_bfight[i].lookx += _bfight[i].delta;
-		if (_bfight[i].lookx < -kTankMax || _bfight[i].lookx > kTankMax)
-			_bfight[i].delta = -_bfight[i].delta;
+		drone.lookx += drone.delta;
+		if (drone.lookx < -kTankMax || drone.lookx > kTankMax)
+			drone.delta = -drone.delta;
 
 		// Build animated left pincer vertices
 		int lPincerPts[4][3];
-		int nabs_lookx = (_bfight[i].lookx > 0) ? -_bfight[i].lookx : _bfight[i].lookx; // nabs
+		int nabs_lookx = (drone.lookx > 0) ? -drone.lookx : drone.lookx; // nabs
 		int lLook = nabs_lookx - 32;
 		if (lLook < 0) lLook += 256;
 		for (int j = 0; j < 4; j++) {
@@ -595,11 +718,13 @@ void ColonyEngine::battleDrawTanks() {
 			lPincerPts[j][0] += 120; // offset from abdomen center
 		}
 		PrismPartDef lPincerDef = {4, lPincerPts, 4, kBLPincerSurf};
-		draw3DBattlePrism(lPincerDef, _bfight[i].xloc, _bfight[i].yloc, droneAng, 0);
+		battleAccumulateBounds(_screenR, lPincerDef, drone, drone.xloc, drone.yloc, droneAng, 0,
+		                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+		draw3DBattlePrism(lPincerDef, drone.xloc, drone.yloc, droneAng, 0);
 
 		// Build animated right pincer vertices
 		int rPincerPts[4][3];
-		int rLook = ABS(_bfight[i].lookx) - 32;
+		int rLook = ABS(drone.lookx) - 32;
 		if (rLook < 0) rLook += 256;
 		for (int j = 0; j < 4; j++) {
 			long tcos = _cost[(uint8)rLook];
@@ -610,73 +735,452 @@ void ColonyEngine::battleDrawTanks() {
 			rPincerPts[j][0] += 120;
 		}
 		PrismPartDef rPincerDef = {4, rPincerPts, 4, kBRPincerSurf};
-		draw3DBattlePrism(rPincerDef, _bfight[i].xloc, _bfight[i].yloc, droneAng, 0);
+		battleAccumulateBounds(_screenR, rPincerDef, drone, drone.xloc, drone.yloc, droneAng, 0,
+		                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+		draw3DBattlePrism(rPincerDef, drone.xloc, drone.yloc, droneAng, 0);
 
 		// Eyes
-		draw3DBattlePrism(kBLEyeDef, _bfight[i].xloc, _bfight[i].yloc, droneAng, 0);
-		draw3DBattlePrism(kBREyeDef, _bfight[i].xloc, _bfight[i].yloc, droneAng, 0);
+		battleAccumulateBounds(_screenR, kBLEyeDef, drone, drone.xloc, drone.yloc, droneAng, 0,
+		                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+		battleAccumulateBounds(_screenR, kBREyeDef, drone, drone.xloc, drone.yloc, droneAng, 0,
+		                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+		draw3DBattlePrism(kBLEyeDef, drone.xloc, drone.yloc, droneAng, 0);
+		draw3DBattlePrism(kBREyeDef, drone.xloc, drone.yloc, droneAng, 0);
+		drone.dist = forward;
 
 		// Track for hit detection
 		if (_battleMaxP < 100) {
-			_battlePwh[_battleMaxP] = &_bfight[i];
-			_bfight[i].dist = (int)(ABS(dxloc) + ABS(dyloc)); // manhattan approx
+			_battlePwh[_battleMaxP] = &drone;
 			_battleMaxP++;
 		}
 
-		// Check if enemy is in crosshair (simple screen-center check)
-		// TODO: proper insight detection via projected screen bounds
+		_insight = _insight || (drone.xmn < _centerX && drone.xmx > _centerX);
 	}
 
 	// --- Projectile ---
 	if (_projon) {
-		long pxloc = _battleProj.xloc - _me.xloc;
-		long pyloc = _battleProj.yloc - _me.yloc;
-		if (ABS(pxloc) + ABS(pyloc) < 20000) {
-			draw3DBattlePrism(kProjDef, _battleProj.xloc, _battleProj.yloc,
-			                  _battleProj.ang, -kFloor);
+		long relX = _battleProj.xloc - _me.xloc;
+		long relY = _battleProj.yloc - _me.yloc;
+		if (ABS(relX) + ABS(relY) < 20000) {
+			int side = 0;
+			int forward = 0;
+			if (battleOriginVisible(relX, relY, _me.look, _sint, _cost, side, forward)) {
+				battleResetBounds(_screenR, _battleProj);
+				battleAccumulateBounds(_screenR, kProjDef, _battleProj,
+				                       _battleProj.xloc, _battleProj.yloc, _battleProj.ang, -kFloor,
+				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleProj.dist = forward;
+				draw3DBattlePrism(kProjDef, _battleProj.xloc, _battleProj.yloc,
+				                  _battleProj.ang, -kFloor);
+				if (_battleMaxP < 100) {
+					_battlePwh[_battleMaxP] = &_battleProj;
+					_battleMaxP++;
+				}
+			}
 		}
 	}
 
 	// --- Entrance ---
 	{
-		long exloc = _battleEnter.xloc - _me.xloc;
-		long eyloc = _battleEnter.yloc - _me.yloc;
-		if (ABS(exloc) + ABS(eyloc) < 20000) {
-			draw3DBattlePrism(kEntDef, _battleEnter.xloc, _battleEnter.yloc,
-			                  _battleEnter.ang, -kFloor);
-			draw3DBattlePrism(kEntDoorDef, _battleEnter.xloc, _battleEnter.yloc,
-			                  _battleEnter.ang, -kFloor);
-			if (_battleMaxP < 100) {
-				_battlePwh[_battleMaxP] = &_battleEnter;
-				_battleMaxP++;
+		long relX = _battleEnter.xloc - _me.xloc;
+		long relY = _battleEnter.yloc - _me.yloc;
+		if (ABS(relX) + ABS(relY) < 20000) {
+			int side = 0;
+			int forward = 0;
+			if (battleOriginVisible(relX, relY, _me.look, _sint, _cost, side, forward)) {
+				battleResetBounds(_screenR, _battleEnter);
+				battleAccumulateBounds(_screenR, kEntDef, _battleEnter,
+				                       _battleEnter.xloc, _battleEnter.yloc, _battleEnter.ang, -kFloor,
+				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				battleAccumulateBounds(_screenR, kEntDoorDef, _battleEnter,
+				                       _battleEnter.xloc, _battleEnter.yloc, _battleEnter.ang, -kFloor,
+				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleEnter.dist = forward;
+				draw3DBattlePrism(kEntDef, _battleEnter.xloc, _battleEnter.yloc,
+				                  _battleEnter.ang, -kFloor);
+				draw3DBattlePrism(kEntDoorDef, _battleEnter.xloc, _battleEnter.yloc,
+				                  _battleEnter.ang, -kFloor);
+				if (_battleMaxP < 100) {
+					_battlePwh[_battleMaxP] = &_battleEnter;
+					_battleMaxP++;
+				}
 			}
 		}
 	}
 
 	// --- Shuttle (only if not in orbit) ---
 	if (!_orbit) {
-		long sxloc = _battleShip.xloc - _me.xloc;
-		long syloc = _battleShip.yloc - _me.yloc;
-		if (ABS(sxloc) + ABS(syloc) < 20000) {
-			draw3DBattlePrism(kSBodyDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
-			draw3DBattlePrism(kSFrontDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
-			draw3DBattlePrism(kSBackDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
-			// Fins: force-draw (no backface cull) — single-sided surfaces
-			draw3DBattlePrism(kFTopDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
-			draw3DBattlePrism(kFLeftDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
-			draw3DBattlePrism(kFRightDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
-			draw3DBattlePrism(kSDoorDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
-			if (_battleMaxP < 100) {
-				_battlePwh[_battleMaxP] = &_battleShip;
-				_battleMaxP++;
+		long relX = _battleShip.xloc - _me.xloc;
+		long relY = _battleShip.yloc - _me.yloc;
+		if (ABS(relX) + ABS(relY) < 20000) {
+			int side = 0;
+			int forward = 0;
+			if (battleOriginVisible(relX, relY, _me.look, _sint, _cost, side, forward)) {
+				battleResetBounds(_screenR, _battleShip);
+				battleAccumulateBounds(_screenR, kSBodyDef, _battleShip,
+				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				battleAccumulateBounds(_screenR, kSFrontDef, _battleShip,
+				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				battleAccumulateBounds(_screenR, kSBackDef, _battleShip,
+				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				battleAccumulateBounds(_screenR, kFTopDef, _battleShip,
+				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				battleAccumulateBounds(_screenR, kFLeftDef, _battleShip,
+				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				battleAccumulateBounds(_screenR, kFRightDef, _battleShip,
+				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				battleAccumulateBounds(_screenR, kSDoorDef, _battleShip,
+				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleShip.dist = forward;
+				draw3DBattlePrism(kSBodyDef, _battleShip.xloc, _battleShip.yloc,
+				                  _battleShip.ang, -kFloor);
+				draw3DBattlePrism(kSFrontDef, _battleShip.xloc, _battleShip.yloc,
+				                  _battleShip.ang, -kFloor);
+				draw3DBattlePrism(kSBackDef, _battleShip.xloc, _battleShip.yloc,
+				                  _battleShip.ang, -kFloor);
+				// Fins: force-draw (no backface cull) — single-sided surfaces
+				draw3DBattlePrism(kFTopDef, _battleShip.xloc, _battleShip.yloc,
+				                  _battleShip.ang, -kFloor);
+				draw3DBattlePrism(kFLeftDef, _battleShip.xloc, _battleShip.yloc,
+				                  _battleShip.ang, -kFloor);
+				draw3DBattlePrism(kFRightDef, _battleShip.xloc, _battleShip.yloc,
+				                  _battleShip.ang, -kFloor);
+				draw3DBattlePrism(kSDoorDef, _battleShip.xloc, _battleShip.yloc,
+				                  _battleShip.ang, -kFloor);
+				if (_battleMaxP < 100) {
+					_battlePwh[_battleMaxP] = &_battleShip;
+					_battleMaxP++;
+				}
+			}
+		}
+	}
+}
+
+void ColonyEngine::battleThink() {
+	if (_projon) {
+		const int fx = battleNormalizeCoord(_battleProj.xloc + (_cost[_battleProj.ang] << 2));
+		const int fy = battleNormalizeCoord(_battleProj.yloc + (_sint[_battleProj.ang] << 2));
+		if (0 == (_pcount--))
+			_projon = false;
+		battleProjCommand(fx, fy);
+	}
+
+	const int xcheck = _bfight[_battleRound].xloc;
+	const int ycheck = _bfight[_battleRound].yloc;
+	int bestShootDist = 4000;
+	bool shouldShoot = false;
+	int shooter = 0;
+
+	for (int i = 0; i < 16; i++) {
+		if (i != _battleRound &&
+		    xcheck > _bfight[i].xloc - kBattleSize &&
+		    xcheck < _bfight[i].xloc + kBattleSize &&
+		    ycheck > _bfight[i].yloc - kBattleSize &&
+		    ycheck < _bfight[i].yloc + kBattleSize) {
+			while (_bfight[i].xloc - _me.xloc < 2000 &&
+			       _bfight[i].yloc - _me.yloc < 2000) {
+				_bfight[i].xloc = _randomSource.getRandomNumber(0x7FFF);
+				_bfight[i].yloc = _randomSource.getRandomNumber(0x7FFF);
+			}
+		}
+
+		uint8 &ang = _bfight[i].ang;
+		long dx = _bfight[i].xloc - _me.xloc;
+		long dy = _bfight[i].yloc - _me.yloc;
+		long adx = ABS(dx);
+		long ady = ABS(dy);
+		bool tooFar = false;
+		long distance = 0;
+
+		if (adx > 4000 || ady > 4000) {
+			dx >>= 8;
+			dy >>= 8;
+			tooFar = true;
+		}
+
+		long dir = dx * _sint[ang] - dy * _cost[ang];
+		if (!tooFar) {
+			distance = (long)sqrt((double)(dx * dx + dy * dy));
+			if (distance > 0) {
+				dir /= distance;
+				if (ABS(dir) < 10) {
+					if (dir < 0)
+						ang--;
+					if (dir > 0)
+						ang++;
+				} else {
+					if (dir < 0)
+						ang -= 4;
+					if (dir > 0)
+						ang += 4;
+				}
+
+				if (ABS(dir) == 0 && !_projon && distance < bestShootDist) {
+					bestShootDist = (int)distance;
+					shooter = i;
+					shouldShoot = true;
+				}
+			}
+		} else {
+			if (dir < 0)
+				ang -= 4;
+			if (dir > 0)
+				ang += 4;
+		}
+
+		const int fx = _bfight[i].xloc + (_cost[ang] >> 2);
+		const int fy = _bfight[i].yloc + (_sint[ang] >> 2);
+		if (distance > 250 || tooFar) {
+			if ((!_orbit) &&
+			    fx > _battleShip.xloc - 2 * kBattleSize &&
+			    fx < _battleShip.xloc + 2 * kBattleSize &&
+			    fy > _battleShip.yloc - 4 * kBattleSize &&
+			    fy < _battleShip.yloc + 4 * kBattleSize) {
+				ang += 8;
+			} else if (fx > _battleEnter.xloc - 2 * kBattleSize &&
+			           fx < _battleEnter.xloc + 2 * kBattleSize &&
+			           fy > _battleEnter.yloc - 2 * kBattleSize &&
+			           fy < _battleEnter.yloc + 2 * kBattleSize) {
+				ang += 8;
+			} else {
+				_bfight[i].xloc = battleNormalizeCoord(fx);
+				_bfight[i].yloc = battleNormalizeCoord(fy);
+			}
+		} else {
+			_sound->play(Sound::kBonk);
+		}
+
+		_bfight[i].look = ang;
+	}
+
+	_battleRound = (_battleRound + 1) & 0x0F;
+	if (shouldShoot) {
+		_sound->play(Sound::kShoot);
+		_battleProj.ang = _bfight[shooter].ang;
+		_battleProj.look = _bfight[shooter].look;
+		_battleProj.xloc = battleNormalizeCoord(_bfight[shooter].xloc + (_cost[_battleProj.ang] << 1));
+		_battleProj.yloc = battleNormalizeCoord(_bfight[shooter].yloc + (_sint[_battleProj.ang] << 1));
+		_pcount = 10;
+		_projon = true;
+	}
+}
+
+void ColonyEngine::battleCommand(int xnew, int ynew) {
+	xnew = battleNormalizeCoord(xnew);
+	ynew = battleNormalizeCoord(ynew);
+
+	auto bonk = [&]() {
+		_sound->play(Sound::kBonk);
+	};
+
+	for (int i = 0; i < 16; i++) {
+		if (xnew > _bfight[i].xloc - kBattleSize &&
+		    xnew < _bfight[i].xloc + kBattleSize &&
+		    ynew > _bfight[i].yloc - kBattleSize &&
+		    ynew < _bfight[i].yloc + kBattleSize) {
+			bonk();
+			return;
+		}
+	}
+
+	auto enterColony = [&](int mapNum, int xloc, int yloc) {
+		_gameMode = kModeColony;
+		_projon = false;
+		_pcount = 0;
+		_me.xloc = xloc;
+		_me.yloc = yloc;
+		_me.xindex = _me.xloc >> 8;
+		_me.yindex = _me.yloc >> 8;
+		loadMap(mapNum);
+		_coreIndex = (mapNum == 1) ? 0 : 1;
+	};
+
+	if (!_orbit &&
+	    xnew > _battleShip.xloc - 2 * kBattleSize &&
+	    xnew < _battleShip.xloc &&
+	    ynew > _battleShip.yloc - kBattleSize / 2 &&
+	    ynew < _battleShip.yloc + kBattleSize / 2) {
+		enterColony(1, 900, 3000);
+		return;
+	}
+
+	if (xnew > _battleEnter.xloc - 2 * kBattleSize &&
+	    xnew < _battleEnter.xloc &&
+	    ynew > _battleEnter.yloc - kBattleSize / 2 &&
+	    ynew < _battleEnter.yloc + kBattleSize / 2) {
+		enterColony(2, 384, 640);
+		return;
+	}
+
+	if ((!_orbit &&
+	     xnew > _battleShip.xloc - 2 * kBattleSize &&
+	     xnew < _battleShip.xloc + 2 * kBattleSize &&
+	     ynew > _battleShip.yloc - 4 * kBattleSize &&
+	     ynew < _battleShip.yloc + 4 * kBattleSize) ||
+	    (xnew > _battleEnter.xloc - 2 * kBattleSize &&
+	     xnew < _battleEnter.xloc + 2 * kBattleSize &&
+	     ynew > _battleEnter.yloc - 2 * kBattleSize &&
+	     ynew < _battleEnter.yloc + 2 * kBattleSize)) {
+		bonk();
+		return;
+	}
+
+	const int wrappedX = wrapBattleCoord(xnew);
+	const int wrappedY = wrapBattleCoord(ynew);
+	const int qx = wrappedX >> 13;
+	const int qy = wrappedY >> 13;
+	Locate *pw = _pyramids[qx][qy];
+	for (int i = 0; i < kMaxQuad; i++) {
+		if (wrappedX > pw[i].xloc - kBattleSize && wrappedX < pw[i].xloc + kBattleSize &&
+		    wrappedY > pw[i].yloc - kBattleSize && wrappedY < pw[i].yloc + kBattleSize) {
+			bonk();
+			return;
+		}
+	}
+
+	_me.xloc = battleNormalizeCoord(xnew);
+	_me.yloc = battleNormalizeCoord(ynew);
+	_me.xindex = wrapBattleCoord(_me.xloc) >> 8;
+	_me.yindex = wrapBattleCoord(_me.yloc) >> 8;
+}
+
+void ColonyEngine::battleShoot() {
+	static bool s_sendFarX = false;
+
+	if (_me.power[0] <= 0 || _weapons <= 0 || _fl)
+		return;
+
+	_sound->play(Sound::kBang);
+	setPower(-2, 0, 0);
+
+	const int cx = _centerX;
+	const int cy = _centerY;
+	_gfx->setXorMode(true);
+	for (int i = 100; i < 900; i += 200) {
+		const int outer = CLIP<int>(kFloor * 128 / i, 0, 1000);
+		const int inner = CLIP<int>(kFloor * 128 / (i + 100), 0, 1000);
+		_gfx->drawLine(cx - outer, cy - outer, cx - inner, cy - inner, 0xFFFFFF);
+		_gfx->drawLine(cx - outer, cy + outer, cx - inner, cy + inner, 0xFFFFFF);
+		_gfx->drawLine(cx + outer, cy + outer, cx + inner, cy + inner, 0xFFFFFF);
+		_gfx->drawLine(cx + outer, cy - outer, cx + inner, cy - inner, 0xFFFFFF);
+		_gfx->drawLine(cx - outer, cy - outer, cx - inner, cy - inner, 0xFFFFFF);
+		_gfx->drawLine(cx - outer, cy + outer, cx - inner, cy + inner, 0xFFFFFF);
+		_gfx->drawLine(cx + outer, cy + outer, cx + inner, cy + inner, 0xFFFFFF);
+		_gfx->drawLine(cx + outer, cy - outer, cx + inner, cy - inner, 0xFFFFFF);
+	}
+	_gfx->copyToScreen();
+	_system->updateScreen();
+	_system->delayMillis(30);
+	for (int i = 100; i < 900; i += 200) {
+		const int outer = CLIP<int>(kFloor * 128 / i, 0, 1000);
+		const int inner = CLIP<int>(kFloor * 128 / (i + 100), 0, 1000);
+		_gfx->drawLine(cx - outer, cy - outer, cx - inner, cy - inner, 0xFFFFFF);
+		_gfx->drawLine(cx - outer, cy + outer, cx - inner, cy + inner, 0xFFFFFF);
+		_gfx->drawLine(cx + outer, cy + outer, cx + inner, cy + inner, 0xFFFFFF);
+		_gfx->drawLine(cx + outer, cy - outer, cx + inner, cy - inner, 0xFFFFFF);
+		_gfx->drawLine(cx - outer, cy - outer, cx - inner, cy - inner, 0xFFFFFF);
+		_gfx->drawLine(cx - outer, cy + outer, cx - inner, cy + inner, 0xFFFFFF);
+		_gfx->drawLine(cx + outer, cy + outer, cx + inner, cy + inner, 0xFFFFFF);
+		_gfx->drawLine(cx + outer, cy - outer, cx + inner, cy - inner, 0xFFFFFF);
+	}
+	_gfx->setXorMode(false);
+
+	int bestDist = 11584;
+	int bestIndex = -1;
+	for (int i = 0; i < _battleMaxP; i++) {
+		Locate *target = _battlePwh[i];
+		if (target->xmn < cx && target->xmx > cx &&
+		    target->zmn < cy && target->zmx > cy &&
+		    target->dist < bestDist) {
+			bestDist = target->dist;
+			bestIndex = i;
+		}
+	}
+
+	if (bestIndex < 0 || bestDist >= 4000)
+		return;
+
+	Locate *target = _battlePwh[bestIndex];
+	if (target->type != kRobCube)
+		return;
+
+	target->power[1] -= battlePowerLevel(_me.power[0]);
+	if (target->power[1] < 0) {
+		target->power[1] = 15 + (_randomSource.getRandomNumber(0x0F) & 0x0F);
+		if (s_sendFarX) {
+			target->xloc = battleNormalizeCoord(_me.xloc + 16000);
+			target->yloc = battleNormalizeCoord(_me.yloc + _randomSource.getRandomNumber(0x7FFF));
+		} else {
+			target->xloc = battleNormalizeCoord(_me.xloc + _randomSource.getRandomNumber(0x7FFF));
+			target->yloc = battleNormalizeCoord(_me.yloc + 16000);
+		}
+		s_sendFarX = !s_sendFarX;
+		_sound->play(Sound::kExplode);
+	}
+}
+
+void ColonyEngine::battleProjCommand(int xcheck, int ycheck) {
+	xcheck = battleNormalizeCoord(xcheck);
+	ycheck = battleNormalizeCoord(ycheck);
+	_battleProj.xloc = xcheck;
+	_battleProj.yloc = ycheck;
+
+	if (xcheck > _me.xloc - 200 && xcheck < _me.xloc + 200 &&
+	    ycheck > _me.yloc - 200 && ycheck < _me.yloc + 200) {
+		setPower(-4, -4, -4);
+		_sound->play(Sound::kExplode);
+		_projon = false;
+		return;
+	}
+
+	for (int i = 0; i < 16; i++) {
+		if (xcheck > _bfight[i].xloc - kBattleSize &&
+		    xcheck < _bfight[i].xloc + kBattleSize &&
+		    ycheck > _bfight[i].yloc - kBattleSize &&
+		    ycheck < _bfight[i].yloc + kBattleSize) {
+			while (_bfight[i].xloc - _me.xloc < 2000 &&
+			       _bfight[i].yloc - _me.yloc < 2000) {
+				_bfight[i].xloc = _randomSource.getRandomNumber(0x7FFF);
+				_bfight[i].yloc = _randomSource.getRandomNumber(0x7FFF);
 			}
+			_sound->play(Sound::kBonk);
+			_projon = false;
+			return;
+		}
+	}
+
+	if ((!_orbit &&
+	     xcheck > _battleShip.xloc - 2 * kBattleSize &&
+	     xcheck < _battleShip.xloc + 2 * kBattleSize &&
+	     ycheck > _battleShip.yloc - 4 * kBattleSize &&
+	     ycheck < _battleShip.yloc + 4 * kBattleSize) ||
+	    (xcheck > _battleEnter.xloc - 2 * kBattleSize &&
+	     xcheck < _battleEnter.xloc + 2 * kBattleSize &&
+	     ycheck > _battleEnter.yloc - 2 * kBattleSize &&
+	     ycheck < _battleEnter.yloc + 2 * kBattleSize)) {
+		_sound->play(Sound::kBonk);
+		_projon = false;
+		return;
+	}
+
+	const int wrappedX = wrapBattleCoord(xcheck);
+	const int wrappedY = wrapBattleCoord(ycheck);
+	const int qx = wrappedX >> 13;
+	const int qy = wrappedY >> 13;
+	Locate *pw = _pyramids[qx][qy];
+	for (int i = 0; i < kMaxQuad; i++) {
+		if (wrappedX > pw[i].xloc - kBattleSize && wrappedX < pw[i].xloc + kBattleSize &&
+		    wrappedY > pw[i].yloc - kBattleSize && wrappedY < pw[i].yloc + kBattleSize) {
+			_sound->play(Sound::kBonk);
+			_projon = false;
+			return;
 		}
 	}
 }
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index b0888f835b6..0e7e45d6083 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -443,6 +443,9 @@ Common::Error ColonyEngine::run() {
 	uint32 lastMoveTick = _system->getMillis();
 	while (!shouldQuit()) {
 		_frameLimiter->startFrame();
+		if (_gameMode == kModeBattle)
+			battleThink();
+
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
 			// Let MacWindowManager handle menu events first
@@ -618,24 +621,21 @@ Common::Error ColonyEngine::run() {
 			const int rotSpeed = 1 << (_speedShift - 1);
 
 			if (_gameMode == kModeBattle) {
-				// Battle: direct movement, no wall collision
-				if (_moveForward) {
-					_me.xloc += moveX;
-					_me.yloc += moveY;
-				}
-				if (_moveBackward) {
-					_me.xloc -= moveX;
-					_me.yloc -= moveY;
-				}
+				if (_moveForward)
+					battleCommand(_me.xloc + moveX, _me.yloc + moveY);
+				if (_moveBackward)
+					battleCommand(_me.xloc - moveX, _me.yloc - moveY);
 				if (_strafeLeft) {
 					uint8 strafeAngle = (uint8)((int)_me.look + 64);
-					_me.xloc += (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
-					_me.yloc += (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+					int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
+					int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+					battleCommand(_me.xloc + sx, _me.yloc + sy);
 				}
 				if (_strafeRight) {
 					uint8 strafeAngle = (uint8)((int)_me.look - 64);
-					_me.xloc += (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
-					_me.yloc += (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+					int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
+					int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+					battleCommand(_me.xloc + sx, _me.yloc + sy);
 				}
 			} else {
 				if (_moveForward)
@@ -667,6 +667,7 @@ Common::Error ColonyEngine::run() {
 
 		if (_gameMode == kModeBattle) {
 			renderBattle();
+			drawDashboardStep1();
 			drawCrosshair();
 		} else {
 			_gfx->clear((_corePower[_coreIndex] > 0) ? 15 : 0);
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 391a2d6a164..497b9e2ba1d 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -536,6 +536,10 @@ private:
 	// battle.c: outdoor battle system (OpenGL 3D)
 	void battleInit();
 	void battleSet();
+	void battleThink();
+	void battleCommand(int xnew, int ynew);
+	void battleShoot();
+	void battleProjCommand(int xcheck, int ycheck);
 	void renderBattle();
 	void draw3DBattlePrism(const PrismPartDef &def, int worldX, int worldY, uint8 ang, int zShift = 0);
 	void battleBackdrop();
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index edd2eee019f..18421fa83b2 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -292,6 +292,11 @@ void ColonyEngine::setPower(int p0, int p1, int p2) {
 // shoot.c CShoot(): player fires weapon at screen center.
 // Traces a ray in the facing direction to find the first robot hit.
 void ColonyEngine::cShoot() {
+	if (_gameMode == kModeBattle) {
+		battleShoot();
+		return;
+	}
+
 	if (_me.power[0] <= 0 || _weapons <= 0)
 		return;
 


Commit: 15a8b373b4686f8b8d21486484b9e6d572bd0a31
    https://github.com/scummvm/scummvm/commit/15a8b373b4686f8b8d21486484b9e6d572bd0a31
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:35+02:00

Commit Message:
COLONY: fix battle projectile shift-to-multiply and minimap bounds guard

Changed paths:
    engines/colony/battle.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 95d6bf87ad4..57be9ba1b73 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -865,8 +865,8 @@ void ColonyEngine::battleDrawTanks() {
 
 void ColonyEngine::battleThink() {
 	if (_projon) {
-		const int fx = battleNormalizeCoord(_battleProj.xloc + (_cost[_battleProj.ang] << 2));
-		const int fy = battleNormalizeCoord(_battleProj.yloc + (_sint[_battleProj.ang] << 2));
+		const int fx = battleNormalizeCoord(_battleProj.xloc + (_cost[_battleProj.ang] * 4));
+		const int fy = battleNormalizeCoord(_battleProj.yloc + (_sint[_battleProj.ang] * 4));
 		if (0 == (_pcount--))
 			_projon = false;
 		battleProjCommand(fx, fy);
@@ -965,8 +965,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] << 1));
-		_battleProj.yloc = battleNormalizeCoord(_bfight[shooter].yloc + (_sint[_battleProj.ang] << 1));
+		_battleProj.xloc = battleNormalizeCoord(_bfight[shooter].xloc + (_cost[_battleProj.ang] * 2));
+		_battleProj.yloc = battleNormalizeCoord(_bfight[shooter].yloc + (_sint[_battleProj.ang] * 2));
 		_pcount = 10;
 		_projon = true;
 	}
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 37c00d73b17..8947fd2fc9e 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -420,6 +420,11 @@ void ColonyEngine::drawDashboardMac() {
 
 // Draws the mini floor map into _headsUpRect (shared by Mac and DOS paths)
 void ColonyEngine::drawMiniMap(uint32 lineColor) {
+	if (_gameMode != kModeColony)
+		return;
+	if (_me.xindex < 0 || _me.xindex >= 32 || _me.yindex < 0 || _me.yindex >= 32)
+		return;
+
 	const int kFWALLType = 48;
 	const Common::Rect miniMapClip(_headsUpRect.left + 1, _headsUpRect.top + 1, _headsUpRect.right - 1, _headsUpRect.bottom - 1);
 	auto drawMiniMapLine = [&](int x1, int y1, int x2, int y2, uint32 color) {


Commit: 1a807c097ce018f235677290dc5c26d13c5cb48a
    https://github.com/scummvm/scummvm/commit/1a807c097ce018f235677290dc5c26d13c5cb48a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:35+02:00

Commit Message:
COLONY: add reactor core and cryo chamber Mac color geometry

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 10b5f7f4726..ce0d98966f4 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -741,17 +741,17 @@ static const int kReactorCorePts[12][3] = {
 	{-40, 20, 32}, {0, 40, 32}, {40, 20, 32}, {40, -20, 32}, {0, -40, 32}, {-40, -20, 32}
 };
 static const int kReactorCoreSurf[7][8] = {
-	{kColorReactor, 4, 0, 1, 7, 6, 0, 0}, {kColorReactor, 4, 1, 2, 8, 7, 0, 0}, {kColorReactor, 4, 2, 3, 9, 8, 0, 0},
-	{kColorReactor, 4, 3, 4, 10, 9, 0, 0}, {kColorReactor, 4, 4, 5, 11, 10, 0, 0}, {kColorReactor, 4, 5, 0, 6, 11, 0, 0},
-	{kColorReactor, 6, 5, 4, 3, 2, 1, 0}
+	{kColorCCore, 4, 0, 1, 7, 6, 0, 0}, {kColorCCore, 4, 1, 2, 8, 7, 0, 0}, {kColorCCore, 4, 2, 3, 9, 8, 0, 0},
+	{kColorCCore, 4, 3, 4, 10, 9, 0, 0}, {kColorCCore, 4, 4, 5, 11, 10, 0, 0}, {kColorCCore, 4, 5, 0, 6, 11, 0, 0},
+	{kColorCCore, 6, 5, 4, 3, 2, 1, 0}
 };
 static const int kReactorBasePts[8][3] = {
 	{-128, 128, 0}, {128, 128, 0}, {128, -128, 0}, {-128, -128, 0},
 	{-128, 128, 32}, {128, 128, 32}, {128, -128, 32}, {-128, -128, 32}
 };
 static const int kReactorBaseSurf[6][8] = {
-	{kColorRainbow1, 4, 0, 3, 7, 4, 0, 0}, {kColorRainbow1, 4, 3, 2, 6, 7, 0, 0}, {kColorRainbow1, 4, 1, 0, 4, 5, 0, 0},
-	{kColorRainbow1, 4, 2, 1, 5, 6, 0, 0}, {kColorRainbow1, 4, 7, 6, 5, 4, 0, 0}, {kColorRainbow1, 4, 0, 1, 2, 3, 0, 0}
+	{kColorReactor, 4, 0, 3, 7, 4, 0, 0}, {kColorReactor, 4, 3, 2, 6, 7, 0, 0}, {kColorReactor, 4, 1, 0, 4, 5, 0, 0},
+	{kColorReactor, 4, 2, 1, 5, 6, 0, 0}, {kColorReactor, 4, 7, 6, 5, 4, 0, 0}, {kColorReactor, 4, 0, 1, 2, 3, 0, 0}
 };
 static const int kReactorTopPts[8][3] = {
 	{-128, 128, 288}, {128, 128, 288}, {128, -128, 288}, {-128, -128, 288},
@@ -763,12 +763,6 @@ static const Colony::ColonyEngine::PrismPartDef kBox2Parts[2] = {
 	{8, kBox2Pts, 4, kBox1Surf}, // Body (stacked on box1)
 	{8, kBox1Pts, 5, kBox1Surf}  // Lid (same geometry as box1 base)
 };
-static const Colony::ColonyEngine::PrismPartDef kReactorParts[3] = {
-	{12, kReactorCorePts, 7, kReactorCoreSurf},
-	{8, kReactorBasePts, 6, kReactorBaseSurf},
-	{8, kReactorTopPts, 6, kReactorBaseSurf}
-};
-
 // Bench: simple box (1 part). DOS INITOBJ.C InitBench.
 static const int kBenchPts[8][3] = {
 	{-60, 128, 0}, {60, 128, 0}, {60, -128, 0}, {-60, -128, 0},
@@ -1748,9 +1742,9 @@ void ColonyEngine::wallLine(const float corners[4][3], float u1, float v1, float
 
 // Draw a filled polygon on a wall face using normalized (u,v) coordinates
 void ColonyEngine::wallPolygon(const float corners[4][3], const float *u, const float *v, int count, uint32 color) {
-	float px[8], py[8], pz[8];
-	if (count > 8)
-		count = 8;
+	float px[64], py[64], pz[64];
+	if (count > 64)
+		count = 64;
 	for (int i = 0; i < count; i++) {
 		float p[3];
 		wallPoint(corners, u[i], v[i], p);
@@ -1759,70 +1753,208 @@ void ColonyEngine::wallPolygon(const float corners[4][3], const float *u, const
 	_gfx->draw3DPolygon(px, py, pz, count, color);
 }
 
+static float signedArea2D(const float *x, const float *y, int count) {
+	float area = 0.0f;
+	for (int i = 0; i < count; ++i) {
+		const int next = (i + 1) % count;
+		area += x[i] * y[next] - x[next] * y[i];
+	}
+	return area * 0.5f;
+}
+
+static bool pointInTriangle2D(float px, float py, float ax, float ay, float bx, float by, float cx, float cy) {
+	const float d1 = (px - bx) * (ay - by) - (ax - bx) * (py - by);
+	const float d2 = (px - cx) * (by - cy) - (bx - cx) * (py - cy);
+	const float d3 = (px - ax) * (cy - ay) - (cx - ax) * (py - ay);
+	const bool hasNeg = (d1 < 0.0f) || (d2 < 0.0f) || (d3 < 0.0f);
+	const bool hasPos = (d1 > 0.0f) || (d2 > 0.0f) || (d3 > 0.0f);
+	return !(hasNeg && hasPos);
+}
+
+static const char *const kWallCharData[] = {
+	"\00",
+	"\02\10\02\00\03\00\03\01\02\01\10\02\02\03\02\03\06\02\06",
+	"\02\10\01\04\02\04\02\05\01\05\10\03\04\04\04\04\05\03\05",
+	"\04\10\01\00\02\00\02\05\01\05\10\03\00\04\00\04\05\03\05\10\00\01\05\01\05\02\00\02\10\00\03\05\03\05\04\00\04",
+	"\02\10\02\00\03\00\03\06\02\06\050\00\02\00\01\01\00\04\00\05\01\05\02\01\04\01\05\04\05\04\04\05\04\05\05\04\06\01\06\00\05\00\04\04\02\04\01\01\01\01\02",
+	"\03\10\01\00\06\05\05\06\00\01\032\01\03\02\03\03\04\03\05\02\06\01\06\00\05\00\04\01\03\01\05\02\05\02\04\01\04\032\04\00\05\00\06\01\06\02\05\03\04\03\03\02\03\01\04\00\04\02\05\02\05\01\04\01",
+	"\03\10\05\01\05\02\03\04\02\04\014\02\04\01\05\02\06\01\06\00\05\01\04\032\05\04\03\01\01\01\01\03\03\05\02\06\01\06\02\05\00\03\00\01\01\00\03\00\05\03",
+	"\01\10\02\04\03\05\03\06\02\06",
+	"\01\014\04\00\03\02\03\04\04\06\02\04\02\02",
+	"\01\014\02\00\04\02\04\04\02\06\03\04\03\02",
+	"\06\06\01\00\03\03\00\02\06\02\00\04\00\03\03\06\05\00\06\02\03\03\06\06\04\05\06\03\03\06\02\06\04\06\03\03\06\00\04\01\06\03\03",
+	"\02\10\02\00\03\00\03\05\02\05\10\00\02\05\02\05\03\00\03",
+	"\01\10\02\00\03\01\03\02\02\02",
+	"\01\10\00\02\05\02\05\03\00\03",
+	"\01\10\02\00\03\00\03\01\02\01",
+	"\01\10\01\00\06\05\05\06\00\01",
+	"\02\032\01\00\05\00\06\01\06\05\05\06\01\06\00\05\00\01\01\00\01\05\05\05\05\01\01\01\10\01\01\02\01\05\05\04\05",
+	"\01\026\01\00\04\00\04\01\03\01\03\06\02\06\01\05\01\04\02\04\02\01\01\01",
+	"\01\042\06\00\06\01\02\01\05\02\06\03\06\05\05\06\01\06\00\05\00\04\01\04\01\05\05\05\05\04\01\02\00\01\00\00",
+	"\01\054\00\02\00\01\01\00\05\00\06\01\06\02\05\03\06\04\06\05\05\06\01\06\00\05\00\04\01\04\01\05\05\05\05\04\04\03\05\02\05\01\01\01\01\02",
+	"\01\036\04\00\05\00\05\02\06\02\06\03\05\03\05\06\04\06\04\03\01\03\02\06\01\06\00\03\00\02\04\02",
+	"\01\044\00\02\00\01\01\00\05\00\06\01\06\03\05\04\01\04\02\05\06\05\06\06\01\06\00\04\00\03\05\03\05\01\01\01\01\02",
+	"\01\046\01\02\05\02\05\01\01\01\01\05\05\05\05\04\06\04\06\05\05\06\01\06\00\05\00\01\01\00\05\00\06\01\06\02\05\03\01\03",
+	"\01\020\02\00\03\00\03\03\06\06\00\06\00\05\04\05\02\03",
+	"\02\040\03\00\03\01\01\01\01\02\04\03\01\04\01\05\03\05\03\06\01\06\00\05\00\04\01\03\00\02\00\01\01\00\040\03\00\05\00\06\01\06\02\05\03\06\04\06\05\05\06\03\06\03\05\05\05\05\04\02\03\05\02\05\01\03\01",
+	"\01\046\00\02\00\01\01\00\05\00\06\01\06\05\05\06\01\06\00\05\00\04\01\03\05\03\05\04\01\04\01\05\05\05\05\01\01\01\01\02",
+	"\02\10\02\01\03\01\03\02\02\02\10\02\03\03\03\03\04\02\04",
+	"\02\10\02\00\03\01\03\02\02\02\10\02\03\03\03\03\04\02\04",
+	"\01\014\06\00\06\01\02\03\06\05\06\06\00\03",
+	"\02\10\00\01\05\01\05\02\00\02\10\00\03\05\03\05\04\00\04",
+	"\01\014\00\00\06\03\00\06\00\05\04\03\00\01",
+	"\02\10\02\00\03\00\03\01\02\01\030\02\02\03\02\05\04\05\05\04\06\01\06\00\05\00\04\01\04\01\05\04\05\04\04",
+	"\05\012\04\00\01\01\00\04\00\01\01\00\012\02\00\05\00\06\01\06\04\05\01\012\06\02\06\05\05\06\02\06\05\05\012\04\06\01\06\00\05\00\02\01\05\034\05\01\05\02\03\05\02\05\01\04\01\02\02\01\03\01\05\04\05\05\03\02\02\02\02\04\03\04",
+	"\01\034\03\06\04\06\06\00\05\00\04\02\02\02\01\00\00\00\02\06\03\06\03\05\02\03\04\03\03\05",
+	"\01\050\00\00\00\06\05\06\06\05\06\04\05\03\06\02\06\01\05\00\01\00\01\01\05\01\05\02\04\03\01\03\01\04\05\04\05\05\01\05\01\00",
+	"\01\040\06\02\06\01\05\00\01\00\00\01\00\05\01\06\05\06\06\05\06\04\05\04\05\05\01\05\01\01\05\01\05\02",
+	"\01\034\00\00\00\06\04\06\06\04\06\02\04\00\01\00\01\01\04\01\05\02\05\04\04\05\01\05\01\00",
+	"\01\030\00\00\00\06\06\06\06\05\01\05\01\04\04\04\04\03\01\03\01\01\06\01\06\00",
+	"\01\024\00\00\00\06\06\06\06\05\01\05\01\03\04\03\04\02\01\02\01\00",
+	"\01\044\03\03\06\03\06\01\05\00\01\00\00\01\00\05\01\06\05\06\06\05\06\04\05\04\05\05\01\05\01\01\05\01\05\02\03\02",
+	"\01\030\00\00\00\06\01\06\01\04\05\04\05\06\06\06\06\00\05\00\05\03\01\03\01\00",
+	"\01\030\01\00\01\01\02\01\02\05\01\05\01\06\04\06\04\05\03\05\03\01\04\01\04\00",
+	"\01\034\00\02\00\01\01\00\04\00\05\01\05\05\06\05\06\06\03\06\03\05\04\05\04\01\01\01\01\02",
+	"\01\026\00\00\00\06\01\06\01\04\04\06\06\06\02\03\06\00\04\00\01\02\01\00",
+	"\01\014\00\06\00\00\06\00\06\01\01\01\01\06",
+	"\01\030\00\00\00\06\01\06\03\04\05\06\06\06\06\00\05\00\05\04\03\03\01\04\01\00",
+	"\01\024\00\00\00\06\02\06\05\01\05\06\06\06\06\00\04\00\01\05\01\00",
+	"\01\032\00\01\00\05\01\06\05\06\06\05\06\01\05\00\01\00\00\01\05\01\05\05\01\05\01\01",
+	"\01\030\00\00\00\06\05\06\06\05\06\03\05\02\01\02\01\03\05\03\05\05\01\05\01\00",
+	"\02\036\04\00\01\00\00\01\00\05\01\06\05\06\06\05\06\02\04\00\04\02\05\02\05\05\01\05\01\01\04\01\014\06\00\06\01\05\02\04\02\04\01\05\00",
+	"\01\036\00\00\00\06\05\06\06\05\06\03\05\02\06\00\05\00\04\02\01\02\01\03\05\03\05\05\01\05\01\00",
+	"\01\054\00\02\00\01\01\00\05\00\06\01\06\02\05\03\01\04\01\05\05\05\05\04\06\04\06\05\05\06\01\06\00\05\00\04\01\03\05\02\05\01\01\01\01\02",
+	"\01\020\02\00\02\05\00\05\00\06\05\06\05\05\03\05\03\00",
+	"\01\024\00\06\00\01\01\00\05\00\06\01\06\06\05\06\05\01\01\01\01\06",
+	"\01\016\00\06\02\00\04\00\06\06\05\06\03\01\01\06",
+	"\01\030\00\06\01\00\02\00\03\02\04\00\05\00\06\06\05\06\04\02\03\04\02\02\01\06",
+	"\01\030\00\00\02\03\00\06\01\06\03\04\05\06\06\06\04\03\06\00\05\00\03\02\01\00",
+	"\02\014\00\06\02\03\02\00\03\00\03\03\01\06\10\02\03\03\03\05\06\04\06",
+	"\01\024\00\05\04\05\00\01\00\00\06\00\06\01\02\01\06\05\06\06\00\06",
+	"\01\020\04\00\02\00\02\06\04\06\04\05\03\05\03\01\04\01",
+	"\01\10\00\05\05\00\06\01\01\06",
+	"\01\020\02\00\02\01\03\01\03\05\02\05\02\06\04\06\04\00",
+	"\01\014\00\02\03\06\06\02\05\02\03\05\01\02",
+	"\01\10\00\01\06\01\06\02\00\02",
+	"\01\10\02\04\03\05\03\06\02\06",
+	"\04\06\03\04\03\05\04\06\024\00\02\00\04\01\05\02\05\03\04\04\05\05\05\06\04\05\04\04\02\012\00\02\04\02\04\01\02\00\01\00\014\02\01\04\04\05\02\06\02\05\00\04\00",
+	"\01\016\00\03\03\00\03\02\06\02\06\04\03\04\03\06",
+	"\01\016\00\02\00\04\03\04\03\06\06\03\03\00\03\02",
+	"\01\06\00\00\03\06\06\00",
+	"\03\06\01\00\05\00\03\03\06\03\03\00\03\01\06\06\03\03\06\03\05\06",
+};
+
 void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
-	// Character 'b' (right arrow) and 'c' (left arrow) coordinates on 0-6 grid
-	static const uint8 wallcharB[] = {7, 0,3, 3,0, 3,2, 6,2, 6,4, 3,4, 3,6};
-	static const uint8 wallcharC[] = {7, 0,2, 0,4, 3,4, 3,6, 6,3, 3,0, 3,2};
-	
-	const uint8 *data = nullptr;
-	if (cnum == 'b')
-		data = wallcharB;
-	else if (cnum == 'c')
-		data = wallcharC;
-	
-	if (data) {
-		int count = data[0];
-		float u[8], v[8];
-		for (int i = 0; i < count; i++) {
-			u[i] = 0.2f + (data[1 + i*2] / 6.0f) * 0.6f;
-			v[i] = 0.2f + (data[2 + i*2] / 6.0f) * 0.6f;
-		}
-		// Mac drawchar(): sets cc=c_char0+level-1, fg=black, pattern=WHITE.
-		// SuperPoly fills with BACKGROUND color = cColor[cc].b.
-		// B&W: bg=black. Color: bg=c_char0.bg (e.g., yellow for level 1).
-		// Arrow shapes are concave  GL_POLYGON only handles convex polys.
-		// Decompose into convex parts: triangle head + rectangle shaft.
-		if (_renderMode == Common::kRenderMacintosh) {
-			uint32 fillColor;
-			if (_hasMacColors) {
-				int cc = 8 + _level - 1; // c_char0 + level - 1
-				fillColor = packMacColor(_macColors[cc].bg);
-			} else {
-				fillColor = 0; // B&W: black
+	if (cnum < 0x20 || cnum > 0x65)
+		cnum = 0x20;
+
+	const uint8 *data = reinterpret_cast<const uint8 *>(kWallCharData[cnum - 0x20]);
+	if (!data || data[0] == 0)
+		return;
+
+	const bool macMode = (_renderMode == Common::kRenderMacintosh);
+	const bool macColors = (macMode && _hasMacColors);
+	const uint32 fillColor = macColors ? packMacColor(_macColors[8 + _level - 1].bg) : 0;
+	const uint32 lineColor = macColors ? (uint32)0xFF000000 : 0;
+
+	auto drawFilledCharPolygon = [&](const float *u, const float *v, int count) {
+		if (!macMode || count < 3)
+			return;
+
+		const float area = signedArea2D(u, v, count);
+		if (fabsf(area) < 0.0001f)
+			return;
+
+		int indices[32];
+		for (int i = 0; i < count; ++i)
+			indices[i] = i;
+
+		const bool ccw = area > 0.0f;
+		int remaining = count;
+		int guard = 0;
+
+		while (remaining > 3 && guard++ < 64) {
+			bool earFound = false;
+			for (int i = 0; i < remaining; ++i) {
+				const int prev = indices[(i + remaining - 1) % remaining];
+				const int curr = indices[i];
+				const int next = indices[(i + 1) % remaining];
+				const float cross = (u[curr] - u[prev]) * (v[next] - v[prev]) -
+					(v[curr] - v[prev]) * (u[next] - u[prev]);
+				if (ccw ? (cross <= 0.0001f) : (cross >= -0.0001f))
+					continue;
+
+				bool containsPoint = false;
+				for (int j = 0; j < remaining; ++j) {
+					const int test = indices[j];
+					if (test == prev || test == curr || test == next)
+						continue;
+					if (pointInTriangle2D(u[test], v[test], u[prev], v[prev], u[curr], v[curr], u[next], v[next])) {
+						containsPoint = true;
+						break;
+					}
+				}
+				if (containsPoint)
+					continue;
+
+				const float triU[3] = {u[prev], u[curr], u[next]};
+				const float triV[3] = {v[prev], v[curr], v[next]};
+				wallPolygon(corners, triU, triV, 3, fillColor);
+
+				for (int j = i; j < remaining - 1; ++j)
+					indices[j] = indices[j + 1];
+				--remaining;
+				earFound = true;
+				break;
 			}
-			_gfx->setWireframe(true, fillColor);
-			if (cnum == 'b') {
-				// Right arrow head: (0,3)-(3,0)-(3,6)
-				float hu[3] = {u[0], u[1], u[6]};
-				float hv[3] = {v[0], v[1], v[6]};
-				wallPolygon(corners, hu, hv, 3, fillColor);
-				// Right arrow shaft: (3,2)-(6,2)-(6,4)-(3,4)
-				float su[4] = {u[2], u[3], u[4], u[5]};
-				float sv[4] = {v[2], v[3], v[4], v[5]};
-				wallPolygon(corners, su, sv, 4, fillColor);
-			} else if (cnum == 'c') {
-				// Left arrow shaft: (0,2)-(0,4)-(3,4)-(3,2)
-				float su[4] = {u[0], u[1], u[2], u[6]};
-				float sv[4] = {v[0], v[1], v[2], v[6]};
-				wallPolygon(corners, su, sv, 4, fillColor);
-				// Left arrow head: (3,6)-(6,3)-(3,0)
-				float hu[3] = {u[3], u[4], u[5]};
-				float hv[3] = {v[3], v[4], v[5]};
-				wallPolygon(corners, hu, hv, 3, fillColor);
+
+			if (!earFound) {
+				for (int i = 1; i < remaining - 1; ++i) {
+					const float triU[3] = {u[indices[0]], u[indices[i]], u[indices[i + 1]]};
+					const float triV[3] = {v[indices[0]], v[indices[i]], v[indices[i + 1]]};
+					wallPolygon(corners, triU, triV, 3, fillColor);
+				}
+				return;
 			}
-			// Restore wall fill
-			uint32 wallFill = _hasMacColors
-				? packMacColor(_macColors[8 + _level - 1].fg)
-				: (uint32)255;
-			_gfx->setWireframe(true, wallFill);
 		}
-		// Outline in black (or vBLACK for EGA)
-		uint32 lineColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh)
-			? (uint32)0xFF000000 : 0;
-		for (int i = 0; i < count; i++) {
-			int n = (i + 1) % count;
-			wallLine(corners, u[i], v[i], u[n], v[n], lineColor);
+
+		if (remaining == 3) {
+			const float triU[3] = {u[indices[0]], u[indices[1]], u[indices[2]]};
+			const float triV[3] = {v[indices[0]], v[indices[1]], v[indices[2]]};
+			wallPolygon(corners, triU, triV, 3, fillColor);
 		}
+	};
+
+	if (macMode)
+		_gfx->setWireframe(false);
+
+	int offset = 1;
+	for (int poly = 0; poly < data[0]; ++poly) {
+		const int coordCount = data[offset++];
+		int count = coordCount / 2;
+		if (count > 32)
+			count = 32;
+
+		float u[32], v[32];
+		for (int i = 0; i < count; ++i) {
+			u[i] = (float)data[offset + i * 2] / 6.0f;
+			v[i] = (float)data[offset + i * 2 + 1] / 6.0f;
+		}
+
+		drawFilledCharPolygon(u, v, count);
+		for (int i = 0; i < count; ++i) {
+			const int next = (i + 1) % count;
+			wallLine(corners, u[i], v[i], u[next], v[next], lineColor);
+		}
+
+		offset += coordCount;
+	}
+
+	if (macMode) {
+		const uint32 wallFill = _hasMacColors
+			? packMacColor(_macColors[8 + _level - 1].fg)
+			: (uint32)255;
+		_gfx->setWireframe(true, wallFill);
 	}
 }
 
@@ -2644,8 +2776,8 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		draw3DPrism(obj, kBox2Parts[0], false); // top second
 		break;
 	case kObjReactor: {
-		// MakeReactor: animate core height and ring/core surface colors.
-		// Core state: 0=closing, 1=opening, 2=open (core gone).
+		// MakeReactor: animate core height and recolor only the original
+		// side faces. The reactor body stays c_reactor and the core cap stays c_ccore.
 		switch (_coreState[_coreIndex]) {
 		case 0: if (_coreHeight[_coreIndex] < 256) _coreHeight[_coreIndex] += 16; break;
 		case 1: if (_coreHeight[_coreIndex] > 0) _coreHeight[_coreIndex] -= 16; break;
@@ -2672,33 +2804,44 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		for (int i = 4; i < 8; i++)
 			modRingPts[i][2] = height + 32;
 
-		PrismPartDef modCoreDef = {12, modCorePts, 7, kReactorCoreSurf};
-		PrismPartDef modRingDef = {8, modRingPts, 6, kReactorBaseSurf};
+		int modCoreSurf[7][8];
+		int modRingSurf[6][8];
+		int modTopSurf[6][8];
+		memcpy(modCoreSurf, kReactorCoreSurf, sizeof(modCoreSurf));
+		memcpy(modRingSurf, kReactorBaseSurf, sizeof(modRingSurf));
+		memcpy(modTopSurf, kReactorBaseSurf, sizeof(modTopSurf));
 
-		// Ring color: cycles through 4 colors each frame
-		// Mac: c_color0..c_color3; DOS: 1+count%5
+		// Mac MakeReactor(): first 4 ring/top faces cycle through c_color0..c_color3.
 		static const int kRingColors[] = {kColorRainbow1, kColorRainbow2, kColorRainbow3, kColorRainbow4};
-		int ringColor = kRingColors[_displayCount % 4];
+		const int ringColor = kRingColors[_displayCount % 4];
+		for (int i = 0; i < 4; ++i) {
+			modRingSurf[i][0] = ringColor;
+			modTopSurf[i][0] = ringColor;
+		}
 
-		// Core color when powered: bouncing cycle through hcore colors
-		// Mac: pcycle = {c_hcore1, c_hcore2, c_hcore3, c_hcore4, c_hcore3, c_hcore2}
+		// Only the 6 core side faces animate. The top face remains c_ccore.
 		static const int kCoreCycle[] = {kColorHCore1, kColorHCore2, kColorHCore3, kColorHCore4, kColorHCore3, kColorHCore2};
 		int coreColor;
 		if (_corePower[_coreIndex] > 1)
 			coreColor = kCoreCycle[_displayCount % 6];
 		else
 			coreColor = kColorCCore;
+		for (int i = 0; i < 6; ++i)
+			modCoreSurf[i][0] = coreColor;
+
+		PrismPartDef modCoreDef = {12, modCorePts, 7, modCoreSurf};
+		PrismPartDef modRingDef = {8, modRingPts, 6, modRingSurf};
+		PrismPartDef modTopDef = {8, kReactorTopPts, 6, modTopSurf};
 
-		// Part 2: top cap (static, Z=288..320)
+		// Depth separation matches the original draw order closely, but the
+		// per-face colors now follow MakeReactor() exactly.
 		_gfx->setDepthRange(0.004, 1.0);
-		draw3DPrism(obj, kReactorParts[2], false);
-		// Part 1: ring (animated height + color)
+		draw3DPrism(obj, modTopDef, false);
 		_gfx->setDepthRange(0.002, 1.0);
-		draw3DPrism(obj, modRingDef, false, ringColor);
-		// Part 0: core (animated height + color, hidden in state 2)
+		draw3DPrism(obj, modRingDef, false);
 		if (_coreState[_coreIndex] < 2) {
 			_gfx->setDepthRange(0.0, 1.0);
-			draw3DPrism(obj, modCoreDef, false, coreColor);
+			draw3DPrism(obj, modCoreDef, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;


Commit: 76b5db18439147f3670a5950e8360dbf13ae7773
    https://github.com/scummvm/scummvm/commit/76b5db18439147f3670a5950e8360dbf13ae7773
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:35+02:00

Commit Message:
COLONY: correctly play mac animations

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


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index d3f7b9b06bb..ac4efc0d509 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -178,6 +178,16 @@ static uint32 packMacColorBG(const uint16 rgb[3]) {
 	       ((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
 }
 
+static int getAnimationStateCount(const Common::Array<ComplexSprite *> &sprites, int num) {
+	num--;
+	if (num >= 0 && num < (int)sprites.size()) {
+		int count = (int)sprites[num]->objects.size();
+		return (count > 0) ? count : 1;
+	}
+
+	return 1;
+}
+
 bool ColonyEngine::loadAnimation(const Common::String &name) {
 	_animationName = name;
 	for (int i = 0; i < 6; i++)
@@ -890,7 +900,7 @@ void ColonyEngine::handleAnimationClick(int item) {
 		handleDoorClick(item);
 	} else if (_animationName == "airlock") {
 		handleAirlockClick(item);
-	} else if (_animationName == "elev") {
+	} else if (_animationName == "elev" || _animationName == "elevator" || _animationName == "elevator2") {
 		handleElevatorClick(item);
 	} else if (_animationName == "controls") {
 		handleControlsClick(item);
@@ -1116,51 +1126,74 @@ void ColonyEngine::handleSuitClick(int item) {
 }
 
 void ColonyEngine::handleDoorClick(int item) {
-	// DOS DoDoor: item==3 toggles door open/close, item==1 or (item==101 && door open) exits
+	const bool isMac = (getPlatform() == Common::kPlatformMacintosh);
+	const int doorFrames = getAnimationStateCount(_lSprites, 2);
+	int openStart = isMac ? 2 : 1;
+	int closeStart = isMac ? doorFrames - 1 : doorFrames;
+
+	if (openStart > doorFrames)
+		openStart = doorFrames;
+	if (closeStart < 1)
+		closeStart = 1;
+
+	// Original DoDoor() differs by platform:
+	// DOS uses a 3-state door, Mac uses a 5-state door.
 	if (item == 3) {
 		_sound->play(Sound::kDoor);
 		if (_doorOpen) {
-			for (int i = 3; i >= 1; i--) {
-				_doorOpen = !_doorOpen;
+			for (int i = closeStart; i >= 1; i--) {
 				setObjectState(2, i);
 				drawAnimation();
 				_gfx->copyToScreen();
 				_system->delayMillis(80);
 			}
 		} else {
-			for (int i = 1; i < 4; i++) {
-				_doorOpen = !_doorOpen;
+			for (int i = openStart; i <= doorFrames; i++) {
 				setObjectState(2, i);
 				drawAnimation();
 				_gfx->copyToScreen();
 				_system->delayMillis(80);
 			}
 		}
+		_doorOpen = !_doorOpen;
 	}
-	if (item == 1 || (item == 101 && objectState(2) == 3)) {
+	if ((item == 1 || item == 101) && _doorOpen) {
 		_animationResult = 1;
 		_animationRunning = false;
 	}
 }
 
 void ColonyEngine::handleAirlockClick(int item) {
-	// DOS DoAirLock: item==1 toggles airlock if power on && unlocked
-	// item==2 or (item==101 && airlock open) exits with pass-through
-	if ((item == 2 || item == 101) && _doorOpen) {
+	const bool isMac = (getPlatform() == Common::kPlatformMacintosh);
+	const int doorItem = isMac ? 3 : 2;
+	const int toggleItem = isMac ? 2 : 1;
+	const int exitItem = isMac ? 3 : 2;
+	const int doorFrames = getAnimationStateCount(_lSprites, doorItem);
+	int openStart = isMac ? 2 : 1;
+	int closeStart = isMac ? doorFrames - 1 : doorFrames;
+
+	if (openStart > doorFrames)
+		openStart = doorFrames;
+	if (closeStart < 1)
+		closeStart = 1;
+
+	// Original DoAirLock() also differs by platform:
+	// DOS uses sprite 2 as the 3-state door, Mac uses sprite 3 as the 5-state door.
+	if ((item == exitItem || item == 101) && _doorOpen) {
 		_animationResult = 1;
 		_animationRunning = false;
-	} else if (item == 1 && _corePower[_coreIndex] && _unlocked) {
+	} else if (item == toggleItem && _corePower[_coreIndex] && _unlocked) {
 		_sound->play(Sound::kAirlock);
 		if (_doorOpen) {
-			for (int i = 3; i >= 1; i--) {
-				setObjectState(2, i);
+			for (int i = closeStart; i >= 1; i--) {
+				setObjectState(doorItem, i);
 				drawAnimation();
 				_gfx->copyToScreen();
 				_system->delayMillis(80);
 			}
 		} else {
-			for (int i = 1; i < 4; i++) {
-				setObjectState(2, i);
+			for (int i = openStart; i <= doorFrames; i++) {
+				setObjectState(doorItem, i);
 				drawAnimation();
 				_gfx->copyToScreen();
 				_system->delayMillis(80);
@@ -1174,6 +1207,119 @@ void ColonyEngine::handleAirlockClick(int item) {
 }
 
 void ColonyEngine::handleElevatorClick(int item) {
+	const bool isMac = (getPlatform() == Common::kPlatformMacintosh);
+
+	if (isMac) {
+		if (_animationName == "elevator2") {
+			const int doorFrames3 = getAnimationStateCount(_lSprites, 3);
+			const int doorFrames4 = getAnimationStateCount(_lSprites, 4);
+			const int doorFrames = (doorFrames3 < doorFrames4) ? doorFrames3 : doorFrames4;
+			const int savedLevel = _level;
+
+			if (item >= 5 && item <= 9) {
+				int fl = item - 4;
+				if (fl == _elevatorFloor) {
+					setObjectState(item, 1);
+					drawAnimation();
+					_gfx->copyToScreen();
+				} else {
+					_sound->play(Sound::kElevator);
+					for (int i = doorFrames - 1; i >= 1; i--) {
+						setObjectState(3, i);
+						setObjectState(4, i);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(80);
+					}
+
+					if (fl > _elevatorFloor) {
+						for (int i = _elevatorFloor; i <= fl; i++) {
+							_level = i + 1;
+							if (i >= 1)
+								setObjectState(2, i);
+							drawAnimation();
+							_gfx->copyToScreen();
+							_system->delayMillis(80);
+						}
+					} else {
+						for (int i = _elevatorFloor; i >= fl; i--) {
+							_level = i + 1;
+							if (i >= 1)
+								setObjectState(2, i);
+							drawAnimation();
+							_gfx->copyToScreen();
+							_system->delayMillis(80);
+						}
+					}
+
+					_elevatorFloor = fl;
+					_level = savedLevel;
+					_sound->play(Sound::kElevator);
+					for (int i = 2; i <= doorFrames; i++) {
+						setObjectState(3, i);
+						setObjectState(4, i);
+						drawAnimation();
+						_gfx->copyToScreen();
+						_system->delayMillis(80);
+					}
+					setObjectState(item, 1);
+					drawAnimation();
+					_gfx->copyToScreen();
+				}
+			} else if (item == 1 || item == 101) {
+				_level = savedLevel;
+				_animationRunning = false;
+			}
+			return;
+		}
+
+		const int doorFrames2 = getAnimationStateCount(_lSprites, 2);
+		const int doorFrames3 = getAnimationStateCount(_lSprites, 3);
+		const int doorFrames = (doorFrames2 < doorFrames3) ? doorFrames2 : doorFrames3;
+
+		if (item == 4) {
+			_sound->play(Sound::kElevator);
+			if (_doorOpen) {
+				for (int i = doorFrames - 1; i >= 1; i--) {
+					setObjectState(2, i);
+					setObjectState(3, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(80);
+				}
+			} else {
+				for (int i = 2; i <= doorFrames; i++) {
+					setObjectState(2, i);
+					setObjectState(3, i);
+					drawAnimation();
+					_gfx->copyToScreen();
+					_system->delayMillis(80);
+				}
+			}
+			_doorOpen = !_doorOpen;
+		} else if (item == 1 || item == 101) {
+			if (_doorOpen) {
+				if (!loadAnimation("elevator2")) {
+					_animationRunning = false;
+					return;
+				}
+
+				_animationResult = 2;
+				_doorOpen = true;
+				if (_elevatorFloor >= 1)
+					setObjectState(2, _elevatorFloor);
+				setObjectState(3, 5);
+				setObjectState(4, 5);
+				drawAnimation();
+				_gfx->copyToScreen();
+			} else {
+				_animationResult = 0;
+				_animationRunning = false;
+			}
+		}
+		return;
+	}
+
 	// DOS DoElevator: two phases
 	// _doorOpen=false: Phase 1 (outside) - item==5 toggles doors
 	// _doorOpen=true: Phase 2 (inside) - items 6-10 select floor
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index f30c3864f88..03709de16cc 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -291,6 +291,8 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 				return 0;
 			_animationResult = 0;
 			_doorOpen = false;
+			if (getPlatform() == Common::kPlatformMacintosh)
+				setObjectState(3, 2); // original Mac DoDoor: closed handle state
 			setObjectState(2, 1); // door starts closed
 			playAnimation();
 			if (_animationResult) {
@@ -310,8 +312,13 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 				return 0;
 			_animationResult = 0;
 			_doorOpen = false;
-			setObjectState(2, 1); // airlock starts closed
-			setObjectState(1, 1);
+			if (getPlatform() == Common::kPlatformMacintosh) {
+				setObjectState(3, 1); // original Mac DoAirLock: closed airlock frame
+				setObjectState(2, 2); // control sprite starts in locked/closed state
+			} else {
+				setObjectState(2, 1); // original DOS DoAirLock: closed door frame
+				setObjectState(1, 1); // control sprite starts in locked/closed state
+			}
 			playAnimation();
 			if (_animationResult) {
 				setDoorState(fromX, fromY, direction, 0);


Commit: 7393a8b5a23bb2128606aae2f71a770729def504
    https://github.com/scummvm/scummvm/commit/7393a8b5a23bb2128606aae2f71a770729def504
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:36+02:00

Commit Message:
COLONY: fix Mac color palette mapping for animations

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index ce0d98966f4..720ab2797e0 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -177,6 +177,11 @@ enum MacPattern {
 	kPatternClear  = 5  // Outline only (no fill)
 };
 
+// Internal-only surface color for Mac wall helper objects (FWALL/CWALL).
+// In the original Mac renderer these use c_lwall/c_dwall, which are
+// special corridor-wall colors rather than normal material palette entries.
+static const int kColorCorridorWall = 1000;
+
 // GL_POLYGON_STIPPLE patterns: 32x32 bit arrays (128 bytes), tiled from 8x8 Mac patterns.
 // Bit=1 → fragment drawn (black foreground), bit=0 → fragment discarded (white background shows).
 // Mac QuickDraw convention: bit=1 = foreground (BLACK), bit=0 = background (WHITE).
@@ -1368,18 +1373,38 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 
 			if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
 				// Mac color rendering: follows SuperPoly() from calcrobo.c:429-505.
-				int mIdx = mapObjColorToMacColor(colorIdx);
-				int pattern = _macColors[mIdx].pattern;
-				uint32 fg = packMacColor(_macColors[mIdx].fg);
-				uint32 bg = packMacColor(_macColors[mIdx].bg);
-				debugC(5, kColonyDebugRender, "draw3DPrism Mac: colorIdx=%d mIdx=%d pat=%d fg=0x%08X bg=0x%08X lit=%d",
-				      colorIdx, mIdx, pattern, fg, bg, lit);
-
-				if (!lit) {
-					// Mac unlit: all non-wall surfaces fill solid black
-					fg = 0xFF000000;
-					bg = 0xFF000000;
-					pattern = 4; // force BLACK (solid fill)
+				int pattern;
+				uint32 fg;
+				uint32 bg;
+
+				if (colorIdx == kColorCorridorWall) {
+					// Original Mac calcrobo.c special-cases c_lwall/c_dwall:
+					// lit c_lwall uses the current corridor wall tint + black outline,
+					// unlit c_dwall collapses to a solid dark wall color.
+					pattern = 0; // WHITE pattern = solid bg fill + fg outline
+					if (lit) {
+						fg = 0xFF000000;
+						bg = packMacColor(_macColors[8 + _level - 1].fg);
+					} else {
+						bg = packMacColor(_macColors[6].bg);
+						fg = bg;
+					}
+					debugC(5, kColonyDebugRender, "draw3DPrism Mac corridor wall: fg=0x%08X bg=0x%08X lit=%d",
+					      fg, bg, lit);
+				} else {
+					int mIdx = mapObjColorToMacColor(colorIdx);
+					pattern = _macColors[mIdx].pattern;
+					fg = packMacColor(_macColors[mIdx].fg);
+					bg = packMacColor(_macColors[mIdx].bg);
+					debugC(5, kColonyDebugRender, "draw3DPrism Mac: colorIdx=%d mIdx=%d pat=%d fg=0x%08X bg=0x%08X lit=%d",
+					      colorIdx, mIdx, pattern, fg, bg, lit);
+
+					if (!lit) {
+						// Mac unlit: all non-wall surfaces fill solid black
+						fg = 0xFF000000;
+						bg = 0xFF000000;
+						pattern = 4; // force BLACK (solid fill)
+					}
 				}
 
 				if (pattern == 5) {
@@ -2724,10 +2749,16 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjFWall:
-		draw3DPrism(obj, kFWallPart, false);
+		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
+			draw3DPrism(obj, kFWallPart, false, kColorCorridorWall);
+		else
+			draw3DPrism(obj, kFWallPart, false);
 		break;
 	case kObjCWall:
-		draw3DPrism(obj, kCWallPart, false);
+		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
+			draw3DPrism(obj, kCWallPart, false, kColorCorridorWall);
+		else
+			draw3DPrism(obj, kCWallPart, false);
 		break;
 	case kObjScreen:
 		draw3DPrism(obj, kScreenPart, false);


Commit: f8f970bba2496f368800cea55f384f5d97feba60
    https://github.com/scummvm/scummvm/commit/f8f970bba2496f368800cea55f384f5d97feba60
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:36+02:00

Commit Message:
COLONY: proper tunnel effect

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 497b9e2ba1d..c6e94444dcd 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -371,6 +371,7 @@ public:
 	bool waitForInput();
 	void checkCenter();
 	void fallThroughHole();
+	void playTunnelEffect(bool falling);
 
 	void doText(int entry, int center);
 	void inform(const char *text, bool hold);
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 03709de16cc..a2a66d98947 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -27,6 +27,183 @@
 
 namespace Colony {
 
+static const int kTunnelXT[] = {
+	4, 8, 8, 15, 16, 16, 16, 17, 20, 22,
+	22, 22, 25, 25, 28, 25, 25, 23, 20, 18,
+	18, 16, 14, 14, 13, 12, 10, 9, 7, 3,
+	1, 0, 0, -2, -6, -8, -10, -12, -14, -16,
+	-20, -20, -23, -20, -14, -8, -4, 0, 0, 0,
+	0, 0, 0, 0, 0, 0
+};
+
+static const int kTunnelYT[] = {
+	2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+	7, 8, 9, 10, 11, 12, 11, 9, 7, 6,
+	5, 4, 3, 2, 1, 1, 0, 0, -1, -2,
+	-3, -4, -5, -6, -6, -6, -7, -7, -8, -8,
+	-9, -9, -10, -11, -12, -12, -13, -14, -12, -10,
+	-7, -4, -2, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const int kTunnelST[] = {
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2
+};
+
+static uint32 packTunnelMacColor(const uint16 rgb[3]) {
+	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
+	       ((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
+}
+
+static void fillTunnelPattern(Renderer *gfx, const Common::Rect &rect, uint32 fg, uint32 bg, int pattern) {
+	if (rect.isEmpty())
+		return;
+
+	switch (pattern) {
+	case 4:
+		gfx->fillRect(rect, fg);
+		return;
+	case 0:
+		gfx->fillRect(rect, bg);
+		return;
+	default:
+		break;
+	}
+
+	if (pattern == 3)
+		gfx->fillRect(rect, fg);
+	else
+		gfx->fillRect(rect, bg);
+
+	for (int y = rect.top; y < rect.bottom; ++y) {
+		const int py = y - rect.top;
+
+		switch (pattern) {
+		case 1: {
+			const int start = rect.left + ((py & 1) ? 2 : 0);
+			for (int x = start; x < rect.right; x += 4)
+				gfx->setPixel(x, y, fg);
+			break;
+		}
+		case 2: {
+			const int start = rect.left + (py & 1);
+			for (int x = start; x < rect.right; x += 2)
+				gfx->setPixel(x, y, fg);
+			break;
+		}
+		case 3: {
+			const int start = rect.left + ((py & 1) ? 2 : 0);
+			for (int x = start; x < rect.right; x += 4)
+				gfx->setPixel(x, y, bg);
+			break;
+		}
+		default:
+			break;
+		}
+	}
+}
+
+static void projectTunnelPoint(const Common::Rect &rect, int pnt[2], int rox, int roy) {
+	if (roy <= 0)
+		roy = 1;
+
+	const int centerX = (rect.left + rect.right) >> 1;
+	const int centerY = (rect.top + rect.bottom) >> 1;
+	long p = centerX + (((long)rox) << 9) / roy;
+
+	if (p < -32000)
+		p = -32000;
+	else if (p > 32000)
+		p = 32000;
+
+	pnt[0] = (int)p;
+	pnt[1] = centerY - (((160 * 128) / roy) << 1);
+}
+
+enum {
+	kTunnelClipLeft = 1,
+	kTunnelClipRight = 2,
+	kTunnelClipTop = 4,
+	kTunnelClipBottom = 8
+};
+
+static int tunnelClipCode(const Common::Rect &rect, int x, int y) {
+	int code = 0;
+
+	if (x < rect.left)
+		code |= kTunnelClipLeft;
+	else if (x >= rect.right)
+		code |= kTunnelClipRight;
+
+	if (y < rect.top)
+		code |= kTunnelClipTop;
+	else if (y >= rect.bottom)
+		code |= kTunnelClipBottom;
+
+	return code;
+}
+
+static void drawTunnelLine(Renderer *gfx, const Common::Rect &rect, int x1, int y1, int x2, int y2, uint32 color) {
+	if (rect.isEmpty())
+		return;
+
+	int code1 = tunnelClipCode(rect, x1, y1);
+	int code2 = tunnelClipCode(rect, x2, y2);
+
+	while (true) {
+		if (!(code1 | code2)) {
+			gfx->drawLine(x1, y1, x2, y2, color);
+			return;
+		}
+
+		if (code1 & code2)
+			return;
+
+		const int codeOut = code1 ? code1 : code2;
+		double x = 0.0;
+		double y = 0.0;
+
+		if (codeOut & kTunnelClipBottom) {
+			if (y2 == y1)
+				return;
+			y = rect.bottom - 1;
+			x = x1 + (double)(x2 - x1) * (y - y1) / (double)(y2 - y1);
+		} else if (codeOut & kTunnelClipTop) {
+			if (y2 == y1)
+				return;
+			y = rect.top;
+			x = x1 + (double)(x2 - x1) * (y - y1) / (double)(y2 - y1);
+		} else if (codeOut & kTunnelClipRight) {
+			if (x2 == x1)
+				return;
+			x = rect.right - 1;
+			y = y1 + (double)(y2 - y1) * (x - x1) / (double)(x2 - x1);
+		} else {
+			if (x2 == x1)
+				return;
+			x = rect.left;
+			y = y1 + (double)(y2 - y1) * (x - x1) / (double)(x2 - x1);
+		}
+
+		const int ix = (int)round(x);
+		const int iy = (int)round(y);
+
+		if (codeOut == code1) {
+			x1 = ix;
+			y1 = iy;
+			code1 = tunnelClipCode(rect, x1, y1);
+		} else {
+			x2 = ix;
+			y2 = iy;
+			code2 = tunnelClipCode(rect, x2, y2);
+		}
+	}
+}
+
 int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
 	if (x < 0 || x >= 32 || y < 0 || y >= 32)
 		return -1;
@@ -444,65 +621,156 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 	}
 }
 
+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 int tunnelColor = 24; // c_tunnel
+	const int tunnelFrames = falling ? 10 : 49;
+	const uint32 fillFg = macColor ? packTunnelMacColor(_macColors[tunnelColor].fg) : 0;
+	const uint32 fillBg = macColor ? packTunnelMacColor(_macColors[tunnelColor].bg) : 0;
+	const uint32 lineColor = macColor ? 0xFF000000 : 15;
+	int troy = 180;
+	int cnt = 0;
+	int counter = falling ? 2 : kTunnelST[0];
+	int spd = 180 / counter;
+
+	_sound->play(Sound::kTunnel2);
+
+	for (int remaining = tunnelFrames; remaining > 0 && !shouldQuit(); ) {
+		if (!_sound->isPlaying())
+			_sound->play(Sound::kTunnel2);
+
+		if (macColor) {
+			fillTunnelPattern(_gfx, effectRect, fillFg, fillBg, _macColors[tunnelColor].pattern);
+		} else {
+			_gfx->fillRect(effectRect, 0);
+			_gfx->drawRect(effectRect, 15);
+		}
+
+		const int n = MIN<int>(remaining, 16);
+		const double fcnt = falling ? (double)counter / 2.0 : (double)counter / kTunnelST[cnt];
+		const int *pathX = &kTunnelXT[cnt];
+		const int *pathY = &kTunnelYT[cnt];
+		Common::Rect clipRect = effectRect;
+		int prevDL[3] = {0, 0, 0};
+		int prevDR[3] = {0, 0, 0};
+		int prevLT = 0;
+		int prevRT = 0;
+		int rox = -100;
+		int roy = troy;
+		int ty = 0;
+
+		for (int i = 0; i < n; ++i) {
+			int tx;
+			if (i == 0) {
+				tx = (int)((pathX[i] << 2) * fcnt);
+				ty = (int)(pathY[i] * fcnt);
+			} else {
+				tx = pathX[i] << 2;
+				ty += pathY[i];
+			}
+
+			rox += tx;
+
+			int dl[2];
+			int dr[2];
+			projectTunnelPoint(effectRect, dl, rox, roy);
+			projectTunnelPoint(effectRect, dr, rox + 200, roy);
+
+			int left[3] = {
+				dl[0],
+				dl[1] + ty,
+				effectRect.bottom - 1 - (dl[1] - effectRect.top) + ty
+			};
+			int right[3] = {
+				dr[0],
+				dr[1] + ty,
+				effectRect.bottom - 1 - (dr[1] - effectRect.top) + ty
+			};
+
+			drawTunnelLine(_gfx, clipRect, left[0], left[2], left[0], left[1], lineColor);
+			drawTunnelLine(_gfx, clipRect, left[0], left[1], right[0], right[1], lineColor);
+			drawTunnelLine(_gfx, clipRect, right[0], right[1], right[0], right[2], lineColor);
+
+			int lt = 0;
+			int rt = 0;
+			if (!falling) {
+				const int center = (left[0] + right[0]) >> 1;
+				lt = (left[0] + center) >> 1;
+				rt = (right[0] + center) >> 1;
+			} else {
+				drawTunnelLine(_gfx, clipRect, right[0], right[2], left[0], left[2], lineColor);
+			}
+
+			if (i > 0) {
+				drawTunnelLine(_gfx, clipRect, left[0], left[1], prevDL[0], prevDL[1], lineColor);
+				drawTunnelLine(_gfx, clipRect, right[0], right[1], prevDR[0], prevDR[1], lineColor);
+				drawTunnelLine(_gfx, clipRect, right[0], right[2], prevDR[0], prevDR[2], lineColor);
+				drawTunnelLine(_gfx, clipRect, left[0], left[2], prevDL[0], prevDL[2], lineColor);
+				if (!falling) {
+					drawTunnelLine(_gfx, clipRect, prevLT, prevDL[2], lt, left[2], lineColor);
+					drawTunnelLine(_gfx, clipRect, lt, left[2], rt, right[2], lineColor);
+					drawTunnelLine(_gfx, clipRect, rt, right[2], prevRT, prevDR[2], lineColor);
+				}
+			}
+
+			if (clipRect.bottom < right[2])
+				drawTunnelLine(_gfx, clipRect, clipRect.left, clipRect.bottom - 1, clipRect.right - 1, clipRect.bottom - 1, lineColor);
+
+			clipRect.left = MAX<int>(clipRect.left, left[0]);
+			clipRect.right = MIN<int>(clipRect.right, right[0]);
+			clipRect.top = MAX<int>(clipRect.top, left[1]);
+			clipRect.bottom = MIN<int>(clipRect.bottom, right[2]);
+			if (clipRect.bottom <= clipRect.top || clipRect.left >= clipRect.right)
+				break;
+
+			prevDL[0] = left[0];
+			prevDL[1] = left[1];
+			prevDL[2] = left[2];
+			prevDR[0] = right[0];
+			prevDR[1] = right[1];
+			prevDR[2] = right[2];
+			prevLT = lt;
+			prevRT = rt;
+			roy += 256;
+		}
+
+		_gfx->copyToScreen();
+		_system->delayMillis(50);
+
+		counter--;
+		troy -= spd;
+		if (counter == 0) {
+			troy = 180;
+			cnt++;
+			counter = falling ? 2 : kTunnelST[cnt];
+			spd = 256 / counter;
+			remaining--;
+		}
+	}
+
+	_sound->stop();
+}
+
 void ColonyEngine::fallThroughHole() {
-	// DOS tunnel(TRUE, mapdata) + GoTo(mapdata)
+	// DOS/Mac tunnel(TRUE, mapdata) + GoTo(mapdata)
 	// Called when player falls through a floor hole (SMHOLEFLR or LGHOLEFLR)
 	const uint8 *mapdata = _mapData[_me.xindex][_me.yindex][4];
 	int targetMap = mapdata[2];
 	int targetX = mapdata[3];
 	int targetY = mapdata[4];
+	const bool deadFall = (targetMap == 0 && targetX == 0 && targetY == 0);
 
-	if (targetMap == 0 && targetX == 0 && targetY == 0) {
-		terminateGame(false); // you're dead
-		return;
-	}
-
-	// DOS tunnel(TRUE,...): power damage from falling
+	// Original tunnel(TRUE): damage the player's three power bars.
 	int damage = -(_level << 7);
-	for (int i = 0; i < 3; i++)
-		_corePower[i] += damage;
-
-	_sound->play(Sound::kClatter);
-
-	// DOS tunnel(pt=TRUE): falling animation  nested rectangles shrinking toward
-	// center, simulating falling down a shaft. White outlines on black background.
-	// DOS runs 10 steps × 2 frames = 20 display frames at ~15fps = ~1.3 seconds.
-	// At 60fps we use 80 frames for the same duration, paced by the frame limiter.
-	{
-		const int cx = (_screenR.left + _screenR.right) / 2;
-		const int cy = (_screenR.top + _screenR.bottom) / 2;
-		const int hw = _screenR.width() / 2;
-		const int hh = _screenR.height() / 2;
-		const int totalFrames = 80;
-		const int maxRings = 10;
-
-		for (int frame = 0; frame < totalFrames && !shouldQuit(); frame++) {
-			_frameLimiter->startFrame();
-
-			_gfx->fillRect(_screenR, 0); // black background
-
-			float progress = (float)frame / totalFrames;
-
-			// Draw nested rectangles  outer ring shrinks in, inner rings follow
-			// The number of visible rings decreases as we fall deeper
-			int visibleRings = maxRings - (int)(progress * (maxRings - 1));
-			for (int ring = 0; ring < visibleRings; ring++) {
-				// Each ring's depth combines the overall fall progress with per-ring spacing
-				float depth = progress * 0.6f + (float)ring / (maxRings + 2.0f);
-				if (depth >= 1.0f)
-					break;
-				float scale = 1.0f - depth;
-				int rw = (int)(hw * scale);
-				int rh = (int)(hh * scale);
-				if (rw < 2 || rh < 2)
-					break;
-				Common::Rect r(cx - rw, cy - rh, cx + rw, cy + rh);
-				_gfx->drawRect(r, 15); // white outline
-			}
+	setPower(damage, damage, damage);
+	playTunnelEffect(true);
 
-			_frameLimiter->delayBeforeSwap();
-			_gfx->copyToScreen();
-		}
+	if (deadFall) {
+		terminateGame(false); // original tunnel(TRUE) still animates before death
+		return;
 	}
 
 	// DOS GoTo(): preserve sub-cell offset, move to destination
@@ -553,9 +821,8 @@ void ColonyEngine::checkCenter() {
 		fallThroughHole();
 		break;
 	case 5: // HOTFOOT  electric floor, damages power
-		// DOS: SetPower(-(5<<level),-(5<<level),-(5<<level))
-		for (int i = 0; i < 3; i++)
-			_corePower[i] -= (5 << _level);
+		_sound->play(Sound::kBzzz);
+		setPower(-(5 << _level), -(5 << _level), -(5 << _level));
 		break;
 	default:
 		break;


Commit: 1a9e3c6f5f17340a80ef3da4d3bf95e58e9dd6d1
    https://github.com/scummvm/scummvm/commit/1a9e3c6f5f17340a80ef3da4d3bf95e58e9dd6d1
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:36+02:00

Commit Message:
COLONY: airlock rendering fixes

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 720ab2797e0..cfc2d85021c 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -2102,7 +2102,12 @@ 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 lit = (_corePower[_coreIndex] > 0);
+	const uint32 wallFeatureFill = macColors
+		? packMacColor(lit ? _macColors[8 + _level - 1].fg : _macColors[6].bg)
+		: (lit ? (macMode ? 255u : 7u) : 0u);
 
 	// Wall faces are already filled with level-specific color (c_char0+level-1.fg)
 	// by the wall grid in renderCorridor3D(). Features are drawn on top.
@@ -2119,14 +2124,13 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 
 	switch (map[0]) {
 	case kWallFeatureDoor: {
-		// EGA: vDKGRAY outlines; Mac B&W: black outlines + gray fills
-		const bool macMode = (_renderMode == Common::kRenderMacintosh);
 		const uint32 doorColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
 		bool shipLevel = (_level == 1 || _level == 5 || _level == 6);
 
 		if (shipLevel) {
 			static const float uSs[8] = { 0.375f, 0.250f, 0.250f, 0.375f, 0.625f, 0.750f, 0.750f, 0.625f };
 			static const float vSs[8] = { 0.125f, 0.250f, 0.750f, 0.875f, 0.875f, 0.750f, 0.250f, 0.125f };
+			const uint32 shipDoorColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : (_wireframe ? 8u : 0u));
 
 			if (macMode) {
 				if (map[1] != 0) {
@@ -2141,18 +2145,30 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 					// Open: fill with BLACK (passable opening)
 					_gfx->setWireframe(true, 0);
 					wallPolygon(corners, uSs, vSs, 8, 0);
-					_gfx->setWireframe(true, 255);
 				}
+			} else if (!_wireframe) {
+				if (map[1] != 0) {
+					const byte *stipple = setupMacPattern(_gfx, kPatternLtGray, 0, 15);
+					wallPolygon(corners, uSs, vSs, 8, 0);
+					if (stipple)
+						_gfx->setStippleData(nullptr);
+				} else {
+					_gfx->setWireframe(true, 0);
+					wallPolygon(corners, uSs, vSs, 8, 0);
+				}
+			} else {
+				_gfx->setWireframe(true);
+				wallPolygon(corners, uSs, vSs, 8, 8);
 			}
 
 			for (int i = 0; i < 8; i++)
-				wallLine(corners, uSs[i], vSs[i], uSs[(i + 1) % 8], vSs[(i + 1) % 8], doorColor);
+				wallLine(corners, uSs[i], vSs[i], uSs[(i + 1) % 8], vSs[(i + 1) % 8], shipDoorColor);
 
 			if (map[1] != 0) {
-				wallLine(corners, 0.375f, 0.25f, 0.375f, 0.75f, doorColor);
-				wallLine(corners, 0.375f, 0.75f, 0.625f, 0.75f, doorColor);
-				wallLine(corners, 0.625f, 0.75f, 0.625f, 0.25f, doorColor);
-				wallLine(corners, 0.625f, 0.25f, 0.375f, 0.25f, doorColor);
+				wallLine(corners, 0.375f, 0.25f, 0.375f, 0.75f, shipDoorColor);
+				wallLine(corners, 0.375f, 0.75f, 0.625f, 0.75f, shipDoorColor);
+				wallLine(corners, 0.625f, 0.75f, 0.625f, 0.25f, shipDoorColor);
+				wallLine(corners, 0.625f, 0.25f, 0.375f, 0.25f, shipDoorColor);
 			}
 		} else {
 			static const float xl = 0.25f, xr = 0.75f;
@@ -2189,7 +2205,6 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	}
 	case kWallFeatureWindow: {
-		const bool macMode = (_renderMode == Common::kRenderMacintosh);
 		const uint32 winColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
 		float xl = 0.25f, xr = 0.75f;
 		float yb = 0.25f, yt = 0.75f;
@@ -2218,7 +2233,6 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	}
 	case kWallFeatureShelves: {
 		// DOS drawbooks: recessed bookcase with 3D depth.
-		const bool macMode = (_renderMode == Common::kRenderMacintosh);
 		const uint32 shelfColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
 
 		// Mac: fill shelves area
@@ -2388,7 +2402,6 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	case kWallFeatureGlyph: {
 		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
-		const bool macMode = (_renderMode == Common::kRenderMacintosh);
 		const uint32 glyphColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
 
 		// Mac: fill glyph area
@@ -2411,7 +2424,6 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	}
 	case kWallFeatureElevator: {
-		const bool macMode = (_renderMode == Common::kRenderMacintosh);
 		const uint32 elevColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
 		float xl = 0.2f, xr = 0.8f;
 		float yb = 0.1f, yt = 0.9f;
@@ -2454,20 +2466,22 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		break;
 	}
 	case kWallFeatureAirlock: {
-		const bool macMode = (_renderMode == Common::kRenderMacintosh);
-		float pts[][2] = {{0.0f, 0.5f}, {0.15f, 0.85f}, {0.5f, 1.0f}, {0.85f, 0.85f},
-		                  {1.0f, 0.5f}, {0.85f, 0.15f}, {0.5f, 0.0f}, {0.15f, 0.15f}};
-		float u[8], v[8];
-		for (int i = 0; i < 8; i++) {
-			u[i] = 0.1f + pts[i][0] * 0.8f;
-			v[i] = 0.1f + pts[i][1] * 0.8f;
-		}
+		// Direct port of drawALOpen/drawALClosed from WALLFTRS.C / wallftrs.c.
+		// These are the exact split7x7 positions on the wall face.
+		static const float u[8] = {0.125f, 0.25f, 0.5f, 0.75f, 0.875f, 0.75f, 0.5f, 0.25f};
+		static const float v[8] = {0.5f, 0.75f, 0.875f, 0.75f, 0.5f, 0.25f, 0.125f, 0.25f};
+		static const float spokeU[8] = {0.375f, 0.5f, 0.625f, 0.625f, 0.625f, 0.5f, 0.375f, 0.375f};
+		static const float spokeV[8] = {0.625f, 0.625f, 0.625f, 0.5f, 0.375f, 0.375f, 0.375f, 0.5f};
+		static const float centerU = 0.5f;
+		static const float centerV = 0.5f;
+
 		if (map[1] == 0) {
-			// Open: black fill (passable opening)
-			_gfx->setWireframe(true, 0);
+			// Original drawALOpen: solid black opening on both DOS and Mac.
+			if (macMode || !_wireframe)
+				_gfx->setWireframe(true, 0);
+			else
+				_gfx->setWireframe(true);
 			wallPolygon(corners, u, v, 8, 0);
-			bool lit = (_corePower[_coreIndex] > 0);
-			_gfx->setWireframe(true, lit ? (macMode ? 255 : 7) : 0);
 		} else {
 			// Mac: fill airlock when closed
 			if (macMode) {
@@ -2478,6 +2492,11 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 					wallPolygon(corners, u, v, 8, 0);
 					_gfx->setStippleData(nullptr);
 				}
+			} else if (!_wireframe) {
+				const byte *stipple = setupMacPattern(_gfx, kPatternLtGray, 0, 15);
+				wallPolygon(corners, u, v, 8, 0);
+				if (stipple)
+					_gfx->setStippleData(nullptr);
 			}
 
 			const uint32 airlockColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
@@ -2485,15 +2504,9 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 				int n = (i + 1) % 8;
 				wallLine(corners, u[i], v[i], u[n], v[n], airlockColor);
 			}
-			if (macMode) {
-				// Mac: 8 radial lines from each vertex to center (drawALClosed)
-				float cu = 0.5f, cv = 0.5f;
-				for (int i = 0; i < 8; i++)
-					wallLine(corners, u[i], v[i], cu, cv, airlockColor);
-			} else {
-				// EGA: simple crosshairs
-				wallLine(corners, 0.1f, 0.5f, 0.9f, 0.5f, airlockColor);
-				wallLine(corners, 0.5f, 0.1f, 0.5f, 0.9f, airlockColor);
+			for (int i = 0; i < 8; i++) {
+				wallLine(corners, u[i], v[i], spokeU[i], spokeV[i], airlockColor);
+				wallLine(corners, spokeU[i], spokeV[i], centerU, centerV, airlockColor);
 			}
 		}
 		break;
@@ -2571,6 +2584,9 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	default:
 		break;
 	}
+
+	_gfx->setStippleData(nullptr);
+	_gfx->setWireframe(true, wallFeatureFill);
 }
 
 void ColonyEngine::drawWallFeatures3D() {


Commit: ce693ddc050ef6564ecfba28836cccad813f51a8
    https://github.com/scummvm/scummvm/commit/ce693ddc050ef6564ecfba28836cccad813f51a8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:36+02:00

Commit Message:
COLONY: airlock logic fixes

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 ac4efc0d509..b1a8de8de00 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -30,6 +30,15 @@
 
 namespace Colony {
 
+static bool isBackdoorCode111111(const uint8 display[6]) {
+	for (int i = 0; i < 6; i++) {
+		if (display[i] != 3)
+			return false;
+	}
+
+	return true;
+}
+
 // Mac color indices from colordef.h enum (cColor[] table in Color256).
 enum {
 	mc_dwall = 6, mc_lwall = 7,
@@ -347,16 +356,25 @@ void ColonyEngine::playAnimation() {
 	CursorMan.showMouse(true);
 	_system->updateScreen();
 
-	if (_animationName == "security" && !_unlocked) {
-		for (int i = 0; i < 4; i++) {
-			_decode1[i] = (uint8)(2 + _randomSource.getRandomNumber(3));
-			setObjectState(27 + i, _decode1[i]);
-		}
-	} else if (_animationName == "reactor") {
+	if (_animationName == "reactor" || _animationName == "security") {
 		for (int i = 0; i < 6; i++) {
+			_animDisplay[i] = 1;
 			setObjectOnOff(14 + i * 2, false);
+			setObjectOnOff(13 + i * 2, true);
 			setObjectState(13 + i * 2, 1);
 		}
+	}
+
+	if (_animationName == "security") {
+		for (int i = 0; i < 4; i++)
+			setObjectState(27 + i, _unlocked ? _decode1[i] : 1);
+
+		if (!_unlocked) {
+			for (int i = 0; i < 4; i++) {
+				_decode1[i] = (uint8)(2 + _randomSource.getRandomNumber(3));
+				setObjectState(27 + i, _decode1[i]);
+			}
+		}
 	} else if (_animationName == "controls") {
 		switch (_corePower[_coreIndex]) {
 		case 0: setObjectState(2, 1); setObjectState(5, 1); break;
@@ -1057,7 +1075,7 @@ void ColonyEngine::handleKeypadClick(int item) {
 				if (testarray[i] != _animDisplay[5 - i])
 					match = false;
 			}
-			if (match) {
+			if (match || isBackdoorCode111111(_animDisplay)) {
 				_unlocked = true;
 				_gametest = true;
 			}
@@ -1070,6 +1088,8 @@ void ColonyEngine::handleKeypadClick(int item) {
 			setObjectState(item, 1);
 		drawAnimation();
 		_gfx->copyToScreen();
+	} else if (item == 25 && _animationName == "security") {
+		doText(14, 0);
 	} else if (item == 27 && _animationName == "reactor") {
 		doText(12, 0);
 	}
@@ -1171,6 +1191,9 @@ void ColonyEngine::handleAirlockClick(int item) {
 	const int doorFrames = getAnimationStateCount(_lSprites, doorItem);
 	int openStart = isMac ? 2 : 1;
 	int closeStart = isMac ? doorFrames - 1 : doorFrames;
+	const uint8 *airlock = (_airlockX >= 0 && _airlockY >= 0 && _airlockDirection >= 0) ?
+		mapFeatureAt(_airlockX, _airlockY, _airlockDirection) : nullptr;
+	const bool locked = airlock ? (airlock[1] != 0) : !_doorOpen;
 
 	if (openStart > doorFrames)
 		openStart = doorFrames;
@@ -1179,18 +1202,20 @@ void ColonyEngine::handleAirlockClick(int item) {
 
 	// Original DoAirLock() also differs by platform:
 	// DOS uses sprite 2 as the 3-state door, Mac uses sprite 3 as the 5-state door.
-	if ((item == exitItem || item == 101) && _doorOpen) {
+	if ((item == exitItem || (!isMac && item == 101)) && !locked) {
 		_animationResult = 1;
 		_animationRunning = false;
-	} else if (item == toggleItem && _corePower[_coreIndex] && _unlocked) {
+	} else if (item == toggleItem && _corePower[_coreIndex] && _unlocked && airlock) {
 		_sound->play(Sound::kAirlock);
-		if (_doorOpen) {
+		if (!locked) {
 			for (int i = closeStart; i >= 1; i--) {
 				setObjectState(doorItem, i);
 				drawAnimation();
 				_gfx->copyToScreen();
 				_system->delayMillis(80);
 			}
+			setDoorState(_airlockX, _airlockY, _airlockDirection, 1);
+			_doorOpen = false;
 		} else {
 			for (int i = openStart; i <= doorFrames; i++) {
 				setObjectState(doorItem, i);
@@ -1198,10 +1223,17 @@ void ColonyEngine::handleAirlockClick(int item) {
 				_gfx->copyToScreen();
 				_system->delayMillis(80);
 			}
+
+			setDoorState(_airlockX, _airlockY, _airlockDirection, 0);
+			_doorOpen = true;
+			if (_level >= 1 && _level <= 8 && _levelData[_level - 1].count == 2) {
+				_airlockTerminate = true;
+				_animationResult = 0;
+				_animationRunning = false;
+				return;
+			}
 		}
-		_doorOpen = !_doorOpen;
-	} else if (item == 101 && !_doorOpen) {
-		// Exit without opening
+	} else if (item == 101) {
 		_animationRunning = false;
 	}
 }
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 0e7e45d6083..7dfa504823b 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -90,6 +90,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 
 	// PATCH.C init
 	memset(_levelData, 0, sizeof(_levelData));
+	_levelData[1].count = 1; // original uses 1-based leveldata[2] for map 2
 	memset(_carryPatch, 0, sizeof(_carryPatch));
 	_carryType = 0;
 	_fl = 0;
@@ -160,6 +161,8 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_resMan = new Common::MacResManager();
 	_colorResMan = new Common::MacResManager();
 	initTrig();
+	battleInit();
+	battleSet();
 }
 
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index c6e94444dcd..299b0debe0d 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -559,6 +559,7 @@ private:
 	void dropCarriedObject();
 	bool setDoorState(int x, int y, int direction, int state);
 	int openAdjacentDoors(int x, int y);
+	int goToDestination(const uint8 *map, Locate *pobject);
 	int tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject);
 	void updateViewportLayout();
 	void drawDashboardStep1();
@@ -595,6 +596,10 @@ private:
 	int _animationResult;
 	bool _doorOpen;
 	int _elevatorFloor;
+	int _airlockX = -1;
+	int _airlockY = -1;
+	int _airlockDirection = -1;
+	bool _airlockTerminate = false;
 
 	void playIntro();
 	bool makeStars(const Common::Rect &r, int btn);
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index a2a66d98947..2e8c1fd0df6 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -389,8 +389,9 @@ bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
 	if (wallType != kWallFeatureDoor && wallType != kWallFeatureAirlock)
 		return false;
 
+	const uint8 oldState = _mapData[x][y][direction][1];
 	_mapData[x][y][direction][1] = (uint8)state;
-	if (state == 0)
+	if (wallType == kWallFeatureDoor && state == 0)
 		_sound->play(Sound::kDoor);
 
 
@@ -425,6 +426,13 @@ bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
 
 	// Persist airlock state changes across level loads
 	if (wallType == kWallFeatureAirlock) {
+		if (oldState != state && _level >= 1 && _level <= 8) {
+			LevelData &ld = _levelData[_level - 1];
+			if (state == 0)
+				ld.count++;
+			else if (ld.count > 0)
+				ld.count--;
+		}
 		saveWall(x, y, direction);
 		if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31 && opposite >= 0)
 			saveWall(nx, ny, opposite);
@@ -447,6 +455,70 @@ int ColonyEngine::openAdjacentDoors(int x, int y) {
 	return opened;
 }
 
+int ColonyEngine::goToDestination(const uint8 *map, Locate *pobject) {
+	if (!map)
+		return 0;
+
+	const int targetMap = map[2];
+	const int targetX = map[3];
+	const int targetY = map[4];
+
+	if (targetMap == 0 && targetX == 0 && targetY == 0)
+		return 1;
+
+	if (targetMap == 127) {
+		if (pobject != &_me)
+			return 0;
+
+		if (_orbit || !(_armor || _fl)) {
+			terminateGame(false);
+			return 0;
+		}
+
+		if (_me.xindex >= 0 && _me.xindex < 32 &&
+		    _me.yindex >= 0 && _me.yindex < 32)
+			_robotArray[_me.xindex][_me.yindex] = 0;
+
+		_gameMode = kModeBattle;
+		_projon = false;
+		_pcount = 0;
+		_me.xloc = targetX << 8;
+		_me.yloc = targetY << 8;
+		_me.xindex = targetX;
+		_me.yindex = targetY;
+		return 2;
+	}
+
+	if (targetMap == 100) {
+		doText(78, 0);
+		return 0;
+	}
+
+	if (targetX > 0 && targetX < 31 && targetY > 0 && targetY < 31) {
+		if (targetMap == 0 && _robotArray[targetX][targetY] != 0)
+			return 0;
+
+		const int xmod = pobject->xloc - (pobject->xindex << 8);
+		const int ymod = pobject->yloc - (pobject->yindex << 8);
+		_robotArray[pobject->xindex][pobject->yindex] = 0;
+		pobject->xloc = (targetX << 8) + xmod;
+		pobject->xindex = targetX;
+		pobject->yloc = (targetY << 8) + ymod;
+		pobject->yindex = targetY;
+	}
+
+	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)
+		_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
+
+	return 2;
+}
+
 // Returns: 0 = blocked, 1 = can pass through normally, 2 = teleported (position already set)
 int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject) {
 	const uint8 *map = mapFeatureAt(fromX, fromY, direction);
@@ -479,28 +551,38 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 			return 0; // player didn't open the door
 		}
 	case kWallFeatureAirlock:
-		if (map[1] == 0)
-			return 1; // already open  pass through
-		if (pobject != &_me)
-			return 0;
+		if (pobject != &_me) {
+			if (map[1] || map[2] || map[3] || map[4])
+				return 0;
+			return 1;
+		}
 		// DOS DoAirLock: play airlock animation
 		{
 			if (!loadAnimation("airlock"))
 				return 0;
 			_animationResult = 0;
-			_doorOpen = false;
+			_airlockX = fromX;
+			_airlockY = fromY;
+			_airlockDirection = direction;
+			_airlockTerminate = false;
+			_doorOpen = (map[1] == 0);
 			if (getPlatform() == Common::kPlatformMacintosh) {
-				setObjectState(3, 1); // original Mac DoAirLock: closed airlock frame
-				setObjectState(2, 2); // control sprite starts in locked/closed state
+				setObjectState(3, _doorOpen ? 5 : 1);
+				setObjectState(2, _doorOpen ? 1 : 2);
 			} else {
-				setObjectState(2, 1); // original DOS DoAirLock: closed door frame
-				setObjectState(1, 1); // control sprite starts in locked/closed state
+				setObjectState(2, _doorOpen ? 3 : 1);
+				setObjectState(1, _doorOpen ? 2 : 1);
 			}
 			playAnimation();
-			if (_animationResult) {
-				setDoorState(fromX, fromY, direction, 0);
-				return 1;
+			_airlockX = -1;
+			_airlockY = -1;
+			_airlockDirection = -1;
+			if (_airlockTerminate) {
+				terminateGame(true);
+				return 0;
 			}
+			if (_animationResult)
+				return goToDestination(map, pobject);
 			return 0;
 		}
 
@@ -510,52 +592,17 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 		if (pobject != &_me)
 			return 0; // robots don't use stairs/tunnels
 
-		// DOS GoTo(): mapdata[2]=level, [3]=xcell, [4]=ycell
-		// Must copy values BEFORE loadMap, which overwrites _mapData
-		const int targetMap = map[2];
-		const int targetX = map[3];
-		const int targetY = map[4];
-
-		if (targetMap == 0 && targetX == 0 && targetY == 0)
-			return 1; // no destination data  just pass through
-
-		// Special cases from DOS
-		if (targetMap == 100) {
-			doText(78, 0); // Dimension error
-			return 0;
-		}
-
 		// Play appropriate sound
 		if (map[0] == kWallFeatureDnStairs)
 			_sound->play(Sound::kClatter);
-
-		// Move player to target position (preserve sub-cell offset like DOS)
-		if (targetX > 0 && targetX < 31 && targetY > 0 && targetY < 31) {
-			const int xmod = pobject->xloc - (pobject->xindex << 8);
-			const int ymod = pobject->yloc - (pobject->yindex << 8);
-			_robotArray[pobject->xindex][pobject->yindex] = 0;
-			pobject->xloc = (targetX << 8) + xmod;
-			pobject->xindex = targetX;
-			pobject->yloc = (targetY << 8) + ymod;
-			pobject->yindex = targetY;
-		}
-
-		// Load new map if target level specified
-		if (targetMap > 0 && targetMap != _level) {
-			loadMap(targetMap);
-			// DOS: coreindex depends on level
-			_coreIndex = (targetMap == 1) ? 0 : 1;
+		const int result = goToDestination(map, pobject);
+		if (result == 2) {
+			debugC(1, kColonyDebugMove, "Level change via %s: level=%d pos=(%d,%d)",
+			      map[0] == kWallFeatureUpStairs ? "upstairs" :
+			      map[0] == kWallFeatureDnStairs ? "downstairs" : "tunnel",
+			      _level, pobject->xindex, pobject->yindex);
 		}
-
-		if (pobject->xindex >= 0 && pobject->xindex < 32 &&
-		    pobject->yindex >= 0 && pobject->yindex < 32)
-			_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
-
-		debugC(1, kColonyDebugMove, "Level change via %s: level=%d pos=(%d,%d)",
-		      map[0] == kWallFeatureUpStairs ? "upstairs" :
-		      map[0] == kWallFeatureDnStairs ? "downstairs" : "tunnel",
-		      _level, pobject->xindex, pobject->yindex);
-		return 2; // teleported
+		return result;
 	}
 
 	case kWallFeatureElevator: {


Commit: 76928886dc54ba5e3e07a6451ef43fae52690a1f
    https://github.com/scummvm/scummvm/commit/76928886dc54ba5e3e07a6451ef43fae52690a1f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:37+02:00

Commit Message:
COLONY: add ship launch animation, weapon controls and skip-intro support

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/debugger.cpp
    engines/colony/debugger.h
    engines/colony/interaction.cpp
    engines/colony/intro.cpp
    engines/colony/map.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index b1a8de8de00..0b8f00983a5 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -1431,29 +1431,24 @@ void ColonyEngine::handleElevatorClick(int item) {
 void ColonyEngine::handleControlsClick(int item) {
 	switch (item) {
 	case 4: // Accelerator
-		if (_corePower[_coreIndex] >= 2 && _coreState[_coreIndex] == 0 && !_orbit) {
-			_orbit = 1;
-			debugC(1, kColonyDebugAnimation, "Taking off!");
-			// Animate the lever moving full range
-			for (int i = 1; i <= 6; i++) {
-				setObjectState(4, i);
-				drawAnimation();
-				_gfx->copyToScreen();
-				_system->delayMillis(30);
-			}
-			_animationRunning = false;
-			return; // Exit animation immediately on success
-		} else {
+		if (_corePower[_coreIndex] < 2 || _coreState[_coreIndex] != 0) {
 			debugC(1, kColonyDebugAnimation, "Accelerator failed: power=%d, state=%d", _corePower[_coreIndex], _coreState[_coreIndex]);
-			// Fail animation click
 			setObjectState(4, 1);
-			// dolSprite(3); // Animate lever moving and returning - handled by top dolSprite
 			for (int i = 6; i > 0; i--) {
 				setObjectState(4, i);
 				drawAnimation();
 				_gfx->copyToScreen();
 				_system->delayMillis(20);
 			}
+			break;
+		}
+
+		_animationRunning = false;
+		if (_orbit) {
+			gameOver(false);
+		} else {
+			takeOff();
+			_orbit = 1;
 		}
 		break;
 	case 5: // Emergency power
@@ -1474,6 +1469,13 @@ void ColonyEngine::handleControlsClick(int item) {
 		drawAnimation();
 		_gfx->copyToScreen();
 		break;
+	case 6: // Ship weapons
+		_animationRunning = false;
+		if (!_orbit)
+			terminateGame(true);
+		else
+			gameOver(true);
+		break;
 	case 7: // Damage report
 	{
 		// dolSprite(6); // Button animation - handled by top dolSprite
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 7dfa504823b..1c3fce5c39f 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -444,10 +444,33 @@ Common::Error ColonyEngine::run() {
 	int mouseDX = 0, mouseDY = 0;
 	bool mouseMoved = false;
 	uint32 lastMoveTick = _system->getMillis();
+	uint32 lastBattleTick = lastMoveTick;
 	while (!shouldQuit()) {
 		_frameLimiter->startFrame();
-		if (_gameMode == kModeBattle)
+
+		auto qlog = [](int32 x) -> int {
+			int i = 0;
+			while (x > 0) {
+				x >>= 1;
+				i++;
+			}
+			return i;
+		};
+		const int warningLevel = 4;
+		const int lifePower = qlog(_me.power[1]);
+		const uint32 now = _system->getMillis();
+		if (lifePower <= warningLevel && !_sound->isPlaying() && now - _lastWarningChimeTime >= 250) {
+			_sound->play(Sound::kChime);
+			_lastWarningChimeTime = now;
+		}
+
+		// The original battle loop advanced AI on the game loop cadence, not
+		// every rendered frame. Running this at 60 fps makes enemies and
+		// projectiles several times more aggressive than DOS/Mac.
+		if (_gameMode == kModeBattle && now - lastBattleTick >= 66) {
+			lastBattleTick = now;
 			battleThink();
+		}
 
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
@@ -616,7 +639,6 @@ Common::Error ColonyEngine::run() {
 
 		// Apply continuous movement/rotation from held keys,
 		// throttled to ~15 ticks/sec to match original key-repeat feel
-		uint32 now = _system->getMillis();
 		if (now - lastMoveTick >= 66) {
 			lastMoveTick = now;
 			const int moveX = (_cost[_me.look] * (1 << _speedShift)) >> 4;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 299b0debe0d..94c9a51c8c9 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -289,6 +289,7 @@ struct PassPatch {
 struct LevelData {
 	uint8 visit;
 	uint8 queen;
+	uint8 object[kBaseObject + 1];
 	uint8 count;           // airlock open count (termination check)
 	uint8 size;            // number of saved wall changes (max 10)
 	uint8 location[10][3]; // [x, y, direction] of each changed wall
@@ -439,6 +440,7 @@ private:
 	uint32 _lastClickTime = 0;
 	uint32 _displayCount = 0; // Frame counter for COLOR wall animation (Mac: count)
 	uint32 _lastAnimUpdate = 0;
+	uint32 _lastWarningChimeTime = 0;
 	int _action0, _action1;
 	int _creature;
 
@@ -549,6 +551,7 @@ private:
 
 	// PATCH.C: object relocation + wall state persistence
 	void createObject(int type, int xloc, int yloc, uint8 ang);
+	void saveLevelState();
 	void doPatch();
 	void saveWall(int x, int y, int direction);
 	void getWall();
@@ -608,6 +611,9 @@ private:
 	bool drawPict(int resID);
 	bool loadAnimation(const Common::String &name);
 	void deleteAnimation();
+	void takeOff();
+	void gameOver(bool kill);
+	int countSavedCryos() const;
 	void playAnimation();
 	void updateAnimation();
 	void drawAnimation();
diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
index 4e9d3731e59..333a306bc95 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/debugger.cpp
@@ -24,6 +24,13 @@
 
 namespace Colony {
 
+static int wrapBattleDebugCoord(int coord) {
+	coord = (int16)coord;
+	if (coord < 0)
+		coord += 0x8000;
+	return coord;
+}
+
 static const char *robotTypeName(int type) {
 	switch (type) {
 	case kRobEye: return "Eye";
@@ -71,6 +78,7 @@ Debugger::Debugger(ColonyEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("power", WRAP_METHOD(Debugger, cmdPower));
 	registerCmd("core", WRAP_METHOD(Debugger, cmdCore));
 	registerCmd("battle", WRAP_METHOD(Debugger, cmdBattle));
+	registerCmd("colony", WRAP_METHOD(Debugger, cmdColony));
 }
 
 bool Debugger::cmdTeleport(int argc, const char **argv) {
@@ -310,31 +318,76 @@ bool Debugger::cmdCore(int argc, const char **argv) {
 }
 
 bool Debugger::cmdBattle(int argc, const char **argv) {
-	// Initialize battle data if not already done
-	_vm->battleInit();
-	_vm->battleSet();
-
-	// Place player just outside the entrance (Enter is at 16000, 16000)
-	// Original BattleCommand entrance check: x in [Enter.xloc-2*BSIZE, Enter.xloc)
-	// Spawn facing the entrance (angle ~96 = east-ish toward entrance)
-	_vm->_me.xloc = 16000 - 500;
-	_vm->_me.yloc = 16000;
-	_vm->_me.ang = 96;
-	_vm->_me.look = 96;
-
-	// Ensure suit and weapons are set up for combat
-	_vm->_me.power[0] = 256; // weapons power
-	_vm->_me.power[1] = 256; // life power
-	_vm->_me.power[2] = 256; // armor power
-	_vm->_weapons = 3;
-	_vm->_armor = 3;
-	_vm->_hasKeycard = true;
-
-	// Switch to battle mode
-	_vm->_gameMode = kModeBattle;
-	_vm->_level = 0;
-
-	debugPrintf("Entered battle mode at (%d, %d) ang=%d\n",
+	auto prepareBattleDebugState = [&](int xloc, int yloc, int ang) {
+		_vm->battleInit();
+		_vm->battleSet();
+
+		_vm->_me.xloc = xloc;
+		_vm->_me.yloc = yloc;
+		_vm->_me.xindex = wrapBattleDebugCoord(xloc) >> 8;
+		_vm->_me.yindex = wrapBattleDebugCoord(yloc) >> 8;
+		_vm->_me.ang = ang;
+		_vm->_me.look = ang;
+		_vm->_me.lookY = 0;
+
+		_vm->_me.power[0] = 256;
+		_vm->_me.power[1] = 256;
+		_vm->_me.power[2] = 256;
+		_vm->_weapons = 3;
+		_vm->_armor = 3;
+		_vm->_hasKeycard = true;
+		_vm->_orbit = 0;
+		_vm->_projon = false;
+		_vm->_pcount = 0;
+
+		_vm->_gameMode = kModeBattle;
+		_vm->_level = 0;
+	};
+
+	// Place player just outside the ship entrance (Ship is at 0, 0).
+	// Original BattleCommand ship check: x in [Ship.xloc-2*BSIZE, Ship.xloc).
+	prepareBattleDebugState(-500, 0, 96);
+
+	debugPrintf("Entered battle mode outside the ship at (%d, %d) ang=%d\n",
+	            _vm->_me.xloc, _vm->_me.yloc, _vm->_me.ang);
+	debugPrintf("Suit: power=[%d,%d,%d] weapons=%d armor=%d\n",
+	            (int)_vm->_me.power[0], (int)_vm->_me.power[1],
+	            (int)_vm->_me.power[2], _vm->_weapons, _vm->_armor);
+	return false;
+}
+
+bool Debugger::cmdColony(int argc, const char **argv) {
+	auto prepareBattleDebugState = [&](int xloc, int yloc, int ang) {
+		_vm->battleInit();
+		_vm->battleSet();
+
+		_vm->_me.xloc = xloc;
+		_vm->_me.yloc = yloc;
+		_vm->_me.xindex = wrapBattleDebugCoord(xloc) >> 8;
+		_vm->_me.yindex = wrapBattleDebugCoord(yloc) >> 8;
+		_vm->_me.ang = ang;
+		_vm->_me.look = ang;
+		_vm->_me.lookY = 0;
+
+		_vm->_me.power[0] = 256;
+		_vm->_me.power[1] = 256;
+		_vm->_me.power[2] = 256;
+		_vm->_weapons = 3;
+		_vm->_armor = 3;
+		_vm->_hasKeycard = true;
+		_vm->_orbit = 0;
+		_vm->_projon = false;
+		_vm->_pcount = 0;
+
+		_vm->_gameMode = kModeBattle;
+		_vm->_level = 0;
+	};
+
+	// Place player just outside the colony entrance (Enter is at 16000, 16000).
+	// Original BattleCommand entrance check: x in [Enter.xloc-2*BSIZE, Enter.xloc).
+	prepareBattleDebugState(16000 - 500, 16000, 96);
+
+	debugPrintf("Entered battle mode outside the colony at (%d, %d) ang=%d\n",
 	            _vm->_me.xloc, _vm->_me.yloc, _vm->_me.ang);
 	debugPrintf("Suit: power=[%d,%d,%d] weapons=%d armor=%d\n",
 	            (int)_vm->_me.power[0], (int)_vm->_me.power[1],
diff --git a/engines/colony/debugger.h b/engines/colony/debugger.h
index b3ddb64f445..379dc1dec14 100644
--- a/engines/colony/debugger.h
+++ b/engines/colony/debugger.h
@@ -43,6 +43,7 @@ private:
 	bool cmdPower(int argc, const char **argv);
 	bool cmdCore(int argc, const char **argv);
 	bool cmdBattle(int argc, const char **argv);
+	bool cmdColony(int argc, const char **argv);
 };
 
 }
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 18421fa83b2..383c9ad10e8 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -283,9 +283,9 @@ void ColonyEngine::setPower(int p0, int p1, int p2) {
 	_me.power[2] = MAX<int32>(_me.power[2] + p2, 0);
 
 	if (_me.power[1] <= 0) {
-		// TODO: player death (Terminate)
 		debugC(1, kColonyDebugUI, "Player died! power=[%d,%d,%d]",
 			(int)_me.power[0], (int)_me.power[1], (int)_me.power[2]);
+		terminateGame(false);
 	}
 }
 
@@ -436,20 +436,32 @@ void ColonyEngine::destroyRobot(int num) {
 		num, obj.type, damage, (int)obj.where.power[1]);
 
 	if (obj.where.power[1] <= 0) {
-		if (obj.count != 0) {
+		if (obj.type == kRobQueen) {
+			obj.alive = 0;
+			const int gx = obj.where.xindex;
+			const int gy = obj.where.yindex;
+			if (gx >= 0 && gx < 32 && gy >= 0 && gy < 32)
+				_robotArray[gx][gy] = 0;
+			if (_level >= 1 && _level <= 7) {
+				_levelData[_level - 1].visit = 1;
+				_levelData[_level - 1].queen = 0;
+			}
+			_sound->play(Sound::kExplode);
+			debugC(1, kColonyDebugAnimation, "Queen destroyed on level %d", _level);
+		} else if (obj.count != 0) {
 			// Robot fully destroyed: remove and drop egg
 			obj.alive = 0;
 			int gx = obj.where.xindex;
 			int gy = obj.where.yindex;
 			if (gx >= 0 && gx < 32 && gy >= 0 && gy < 32)
 				_robotArray[gx][gy] = 0;
-			// TODO: explosion sound + visual, spawn egg (foodarray)
+			_sound->play(Sound::kExplode);
 			debugC(1, kColonyDebugAnimation, "Robot %d destroyed!", num);
 		} else {
 			// Robot regresses to egg form
 			obj.where.power[1] = 10 + ((_randomSource.getRandomNumber(15)) << _level);
+			obj.grow = -1;
 			obj.count = 0;
-			// TODO: set grow = -1, change to egg type
 			debugC(1, kColonyDebugAnimation, "Robot %d regressed to egg", num);
 		}
 	}
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 2f868046e62..97da8abbd88 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -27,6 +27,7 @@
 #include "graphics/font.h"
 #include "graphics/fonts/dosfont.h"
 #include "graphics/fonts/macfont.h"
+#include "graphics/cursorman.h"
 #include "image/pict.h"
 #include <math.h>
 
@@ -671,17 +672,196 @@ bool ColonyEngine::drawPict(int resID) {
 }
 
 void ColonyEngine::terminateGame(bool blowup) {
-	debugC(1, kColonyDebugUI, "YOU HAVE BEEN TERMINATED! (blowup=%d)", blowup);
-	if (blowup)
-		_sound->play(Sound::kExplode);
+	Common::Rect savedScreenR = _screenR;
+	Common::Rect savedClip = _clip;
+	int savedCenterX = _centerX;
+	int savedCenterY = _centerY;
+
+	_screenR = Common::Rect(0, 0, _width, _height);
+	_clip = _screenR;
+	_centerX = _width / 2;
+	_centerY = _height / 2;
+
+	_mouseLocked = false;
+	_system->lockMouse(false);
+	_system->showMouse(true);
+	CursorMan.setDefaultArrowCursor(true);
+	CursorMan.showMouse(true);
+
+	debugC(1, kColonyDebugUI, "terminateGame(blowup=%d)", blowup);
+
+	_sound->stop();
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+
+	_sound->play(blowup ? Sound::kExplode : Sound::kOuch);
+
+	if (_sound->isPlaying()) {
+		bool inverted = false;
+		while (_sound->isPlaying() && !shouldQuit()) {
+			_gfx->clear(inverted ? _gfx->white() : _gfx->black());
+			_gfx->copyToScreen();
+			inverted = !inverted;
+			_system->delayMillis(50);
+		}
+	} else {
+		for (int i = 0; i < (blowup ? 8 : 4); i++) {
+			_gfx->clear((i & 1) ? _gfx->white() : _gfx->black());
+			_gfx->copyToScreen();
+			_system->delayMillis(50);
+		}
+	}
+
+	_sound->stop();
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
 
 	const char *msg[] = {
-		"   YOU HAVE BEEN TERMINATED!   ",
-		" Type 'q' to quit the game.    ",
+		"YOU HAVE BEEN TERMINATED",
 		nullptr
 	};
 	printMessage(msg, true);
 
+	_screenR = savedScreenR;
+	_clip = savedClip;
+	_centerX = savedCenterX;
+	_centerY = savedCenterY;
+
+	_system->quit();
+}
+
+int ColonyEngine::countSavedCryos() const {
+	int saved = 0;
+
+	for (uint i = 0; i < _patches.size(); i++) {
+		if (_patches[i].type == kObjCryo && _patches[i].to.level == 1)
+			saved++;
+	}
+
+	return saved;
+}
+
+void ColonyEngine::takeOff() {
+	Common::Rect savedScreenR = _screenR;
+	Common::Rect savedClip = _clip;
+	int savedCenterX = _centerX;
+	int savedCenterY = _centerY;
+
+	_screenR = Common::Rect(0, 0, _width, _height);
+	_clip = _screenR;
+	_centerX = _width / 2;
+	_centerY = _height / 2;
+
+	debugC(1, kColonyDebugUI, "takeOff()");
+
+	_sound->stop();
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+
+	if (getPlatform() == Common::kPlatformMacintosh)
+		_sound->play(Sound::kMars);
+	else
+		_sound->play(Sound::kStars1);
+
+	makeStars(_screenR, 0);
+	_sound->stop();
+
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+
+	_screenR = savedScreenR;
+	_clip = savedClip;
+	_centerX = savedCenterX;
+	_centerY = savedCenterY;
+}
+
+void ColonyEngine::gameOver(bool kill) {
+	Common::Rect savedScreenR = _screenR;
+	Common::Rect savedClip = _clip;
+	int savedCenterX = _centerX;
+	int savedCenterY = _centerY;
+
+	_screenR = Common::Rect(0, 0, _width, _height);
+	_clip = _screenR;
+	_centerX = _width / 2;
+	_centerY = _height / 2;
+
+	_mouseLocked = false;
+	_system->lockMouse(false);
+	_system->showMouse(true);
+	CursorMan.setDefaultArrowCursor(true);
+	CursorMan.showMouse(true);
+
+	const int savedCryos = countSavedCryos();
+	int textEntry;
+
+	if (kill)
+		textEntry = (savedCryos == 6) ? 256 : (savedCryos > 0 ? 257 : 258);
+	else
+		textEntry = (savedCryos == 6) ? 259 : (savedCryos > 0 ? 260 : 261);
+
+	debugC(1, kColonyDebugUI, "gameOver(kill=%d, savedCryos=%d, text=%d)", kill, savedCryos, textEntry);
+
+	_sound->stop();
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+
+	if (kill) {
+		_sound->play(Sound::kPShot);
+		makeStars(_screenR, 0);
+		_sound->stop();
+		_sound->play(Sound::kExplode);
+		for (int i = 0; i < 4; i++) {
+			_gfx->clear((i & 1) ? _gfx->black() : _gfx->white());
+			_gfx->copyToScreen();
+			_system->delayMillis(50);
+		}
+	} else {
+		_sound->play(Sound::kStars4);
+		makeStars(_screenR, 0);
+		_sound->stop();
+	}
+
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+	doText(textEntry, 2);
+
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+	_sound->play(Sound::kStars4);
+	makeStars(_screenR, 0);
+	_sound->stop();
+
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+	timeSquare("...THE END...", nullptr);
+
+	_gfx->clear(_gfx->black());
+	_gfx->copyToScreen();
+	_sound->play(Sound::kExplode);
+	if (_sound->isPlaying()) {
+		while (_sound->isPlaying() && !shouldQuit()) {
+			_gfx->clear(_gfx->white());
+			_gfx->copyToScreen();
+			_system->delayMillis(50);
+			_gfx->clear(_gfx->black());
+			_gfx->copyToScreen();
+			_system->delayMillis(50);
+		}
+	} else {
+		for (int i = 0; i < 4; i++) {
+			_gfx->clear((i & 1) ? _gfx->black() : _gfx->white());
+			_gfx->copyToScreen();
+			_system->delayMillis(50);
+		}
+	}
+	_sound->stop();
+
+	_screenR = savedScreenR;
+	_clip = savedClip;
+	_centerX = savedCenterX;
+	_centerY = savedCenterY;
+
 	_system->quit();
 }
 
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 5bb2d958efe..f22e30d2d52 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -25,7 +25,33 @@
 
 namespace Colony {
 
+static const int kRobotTypeOrder[] = {
+	0,
+	kRobQueen,
+	kRobSnoop,
+	kRobDrone,
+	kRobSoldier,
+	kRobEye,
+	kRobPyramid,
+	kRobUPyramid,
+	kRobCube,
+	kRobMEye,
+	kRobMPyramid,
+	kRobMUPyramid,
+	kRobMCube,
+	kRobFEye,
+	kRobFPyramid,
+	kRobFUPyramid,
+	kRobFCube,
+	kRobSEye,
+	kRobSPyramid,
+	kRobSUPyramid,
+	kRobSCube
+};
+
 void ColonyEngine::loadMap(int mnum) {
+	saveLevelState();
+
 	Common::Path mapPath(Common::String::format("MAP.%d", mnum));
 	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(mapPath);
 	if (!file) {
@@ -113,8 +139,10 @@ void ColonyEngine::loadMap(int mnum) {
 void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
 	Thing obj;
 	memset(&obj, 0, sizeof(obj));
+	const int lvl = MIN<int>(MAX<int>(_level - 1, 0), 5);
 	while (ang > 255)
 		ang -= 256;
+	obj.opcode = 3; // FORWARD
 	obj.alive = 1;
 	obj.visible = 0;
 	obj.type = type;
@@ -125,6 +153,28 @@ void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
 	obj.where.delta = 4;
 	obj.where.ang = ang;
 	obj.where.look = ang;
+	obj.where.lookx = 0;
+	obj.count = 0;
+	obj.time = 10 + (_randomSource.getRandomNumber(0x3F) & 0x3F);
+	obj.grow = 0;
+
+	if (type <= kBaseObject) {
+		int basePower = 0;
+		if (type == kRobQueen) {
+			if (_level == 7)
+				basePower = 32000;
+			else
+				basePower = 40 + ((_randomSource.getRandomNumber(0x5F) & 0x5F) << lvl);
+		} else if (type == kRobSoldier) {
+			basePower = 30 + ((_randomSource.getRandomNumber(0x1F) & 0x1F) << lvl);
+		} else {
+			basePower = 20 + ((_randomSource.getRandomNumber(0x0F) & 0x0F) << lvl);
+		}
+
+		obj.where.power[0] = basePower;
+		obj.where.power[1] = basePower;
+		obj.where.power[2] = basePower;
+	}
 
 	// Try to reuse a dead slot (starting after kMeNum)
 	int slot = -1;
@@ -148,6 +198,28 @@ void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
 		_robotNum = slot + 1;
 }
 
+void ColonyEngine::saveLevelState() {
+	if (_level < 1 || _level > 7 || _gameMode != kModeColony)
+		return;
+
+	LevelData &ld = _levelData[_level - 1];
+	ld.visit = 1;
+	ld.queen = 0;
+	memset(ld.object, 0, sizeof(ld.object));
+
+	for (uint i = 0; i < _objects.size(); i++) {
+		const Thing &obj = _objects[i];
+		if (!obj.alive || obj.type <= 0 || obj.type > kBaseObject)
+			continue;
+
+		if (obj.type == kRobQueen)
+			ld.queen = 1;
+
+		if (ld.object[obj.type] < 255)
+			ld.object[obj.type]++;
+	}
+}
+
 // PATCH.C: DoPatch()  remove originals and install relocated objects.
 void ColonyEngine::doPatch() {
 	// Pass 1: remove objects that were moved away from this level
@@ -291,6 +363,7 @@ void ColonyEngine::initRobots() {
 	if (_level == 1)
 		return;  // Level 1 has no robots
 
+	LevelData &ld = _levelData[_level - 1];
 	int maxrob;
 	switch (_level) {
 	case 2:  maxrob = 25; break;
@@ -303,13 +376,13 @@ void ColonyEngine::initRobots() {
 	if (lvl > 5)
 		lvl = 5;
 
-	for (int i = 1; i <= maxrob; i++) {
+	auto spawnType = [&](int type) {
 		uint8 ang = _randomSource.getRandomNumber(255);
 		int xloc, yloc;
 
 		// Find unoccupied cell (avoiding borders)
 		do {
-			if (_level == 7 && i == 1) {
+			if (_level == 7 && type == kRobQueen) {
 				// Queen on level 7 has fixed position
 				xloc = 27;
 				yloc = 10;
@@ -322,14 +395,27 @@ void ColonyEngine::initRobots() {
 		// Convert grid coords to world coords (center of cell)
 		int wxloc = (xloc << 8) + 128;
 		int wyloc = (yloc << 8) + 128;
+		createObject(type, wxloc, wyloc, ang);
+	};
 
+	if (ld.visit) {
+		for (uint i = 1; i < ARRAYSIZE(kRobotTypeOrder); i++) {
+			const int type = kRobotTypeOrder[i];
+			for (int count = 0; count < ld.object[type]; count++)
+				spawnType(type);
+		}
+		debugC(1, kColonyDebugMap, "initRobots: restored %d robot/object types on level %d", maxrob, _level);
+		return;
+	}
+
+	ld.queen = 1;
+	for (int i = 1; i <= maxrob; i++) {
 		int type;
-		if (i == 1)
+		if (i == 1) {
 			type = kRobQueen;
-		else if (i == 2)
+		} else if (i == 2) {
 			type = kRobSnoop;
-		else {
-			// Random type weighted by level difficulty
+		} else {
 			int rnd = _randomSource.getRandomNumber(lvl);
 			if (rnd > 5)
 				rnd = 5;
@@ -344,7 +430,7 @@ void ColonyEngine::initRobots() {
 			}
 		}
 
-		createObject(type, wxloc, wyloc, ang);
+		spawnType(type);
 	}
 
 	debugC(1, kColonyDebugMap, "initRobots: spawned %d robots on level %d", maxrob, _level);


Commit: 5f29c6786a17f03103b3eea076e3937a0f55147a
    https://github.com/scummvm/scummvm/commit/5f29c6786a17f03103b3eea076e3937a0f55147a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:37+02:00

Commit Message:
COLONY: implement robot AI think logic and easter eggs

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/interaction.cpp
    engines/colony/map.cpp
    engines/colony/module.mk
    engines/colony/movement.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 1c3fce5c39f..12be2293fb3 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -87,6 +87,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	memset(_mapData, 0, sizeof(_mapData));
 	memset(_robotArray, 0, sizeof(_robotArray));
 	memset(_foodArray, 0, sizeof(_foodArray));
+	memset(_dirXY, 0, sizeof(_dirXY));
 
 	// PATCH.C init
 	memset(_levelData, 0, sizeof(_levelData));
@@ -444,6 +445,7 @@ Common::Error ColonyEngine::run() {
 	int mouseDX = 0, mouseDY = 0;
 	bool mouseMoved = false;
 	uint32 lastMoveTick = _system->getMillis();
+	uint32 lastColonyTick = lastMoveTick;
 	uint32 lastBattleTick = lastMoveTick;
 	while (!shouldQuit()) {
 		_frameLimiter->startFrame();
@@ -464,6 +466,11 @@ Common::Error ColonyEngine::run() {
 			_lastWarningChimeTime = now;
 		}
 
+		if (_gameMode == kModeColony && now - lastColonyTick >= 66) {
+			lastColonyTick = now;
+			cThink();
+		}
+
 		// The original battle loop advanced AI on the game loop cadence, not
 		// every rendered frame. Running this at 60 fps makes enemies and
 		// projectiles several times more aggressive than DOS/Mac.
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 94c9a51c8c9..ba96bc9b7d7 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -386,11 +386,13 @@ private:
 	uint8 _mapData[31][31][5][5];
 	uint8 _robotArray[32][32];
 	uint8 _foodArray[32][32];
+	uint8 _dirXY[32][32];
 
 	Locate _me;
 	Common::Array<Thing> _objects;
 	int _level;
 	int _robotNum;
+	int _dynamicObjectBase = 0;
 
 	Renderer *_gfx;
 	Sound *_sound;
@@ -443,6 +445,8 @@ private:
 	uint32 _lastWarningChimeTime = 0;
 	int _action0, _action1;
 	int _creature;
+	bool _allGrow = false;
+	bool _suppressCollisionSound = false;
 
 	// Battle state (battle.c)
 	int _gameMode = kModeColony;
@@ -550,6 +554,7 @@ private:
 	void battleDrawTanks();
 
 	// PATCH.C: object relocation + wall state persistence
+	void resetObjectSlot(int slot, int type, int xloc, int yloc, uint8 ang);
 	void createObject(int type, int xloc, int yloc, uint8 ang);
 	void saveLevelState();
 	void doPatch();
@@ -646,6 +651,26 @@ private:
 	void refreshAnimationDisplay();
 	void crypt(uint8 sarray[6], int i, int j, int k, int l);
 	void terminateGame(bool blowup);
+
+	// think.c / shoot.c: colony robot AI, egg growth, and egg eating
+	void cThink();
+	void cubeThink(int num);
+	void pyramidThink(int num);
+	void upyramidThink(int num);
+	void eyeThink(int num);
+	void queenThink(int num);
+	void droneThink(int num);
+	void snoopThink(int num);
+	void eggThink(int num);
+	bool layEgg(int type, int xindex, int yindex);
+	void moveThink(int num);
+	void bigGrow(int num);
+	void growRobot(int num);
+	int scanForPlayer(int num);
+	void robotShoot(int num);
+	void meEat();
+	void respawnObject(int num, int type);
+	void notePlayerTrail(int oldX, int oldY, int newX, int newY);
 };
 
 } // End of namespace Colony
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 383c9ad10e8..4f359608aa8 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -428,6 +428,10 @@ void ColonyEngine::destroyRobot(int num) {
 	int weapons2 = _weapons * _weapons;
 	int damage = (epower0 * weapons2) << 1;
 
+	// shoot.c: if not already morphing, force the robot into its firing pose.
+	if (!obj.grow)
+		obj.opcode = 4; // FSHOOT
+
 	// Face robot towards player
 	obj.where.look = obj.where.ang = (uint8)(_me.ang + 128);
 
@@ -436,25 +440,19 @@ void ColonyEngine::destroyRobot(int num) {
 		num, obj.type, damage, (int)obj.where.power[1]);
 
 	if (obj.where.power[1] <= 0) {
-		if (obj.type == kRobQueen) {
-			obj.alive = 0;
-			const int gx = obj.where.xindex;
-			const int gy = obj.where.yindex;
-			if (gx >= 0 && gx < 32 && gy >= 0 && gy < 32)
-				_robotArray[gx][gy] = 0;
-			if (_level >= 1 && _level <= 7) {
-				_levelData[_level - 1].visit = 1;
-				_levelData[_level - 1].queen = 0;
-			}
-			_sound->play(Sound::kExplode);
-			debugC(1, kColonyDebugAnimation, "Queen destroyed on level %d", _level);
-		} else if (obj.count != 0) {
+		if (obj.count != 0) {
 			// Robot fully destroyed: remove and drop egg
 			obj.alive = 0;
 			int gx = obj.where.xindex;
 			int gy = obj.where.yindex;
-			if (gx >= 0 && gx < 32 && gy >= 0 && gy < 32)
-				_robotArray[gx][gy] = 0;
+			if (gx >= 0 && gx < 32 && gy >= 0 && gy < 32) {
+				if (obj.type > kRobUPyramid && obj.type < kRobQueen) {
+					if (_foodArray[gx][gy] == num)
+						_foodArray[gx][gy] = 0;
+				} else if (_robotArray[gx][gy] == num) {
+					_robotArray[gx][gy] = 0;
+				}
+			}
 			_sound->play(Sound::kExplode);
 			debugC(1, kColonyDebugAnimation, "Robot %d destroyed!", num);
 		} else {
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index f22e30d2d52..eaf490a497e 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -82,6 +82,7 @@ void ColonyEngine::loadMap(int mnum) {
 	memset(_mapData, 0, sizeof(_mapData));
 	memset(_robotArray, 0, sizeof(_robotArray));
 	memset(_foodArray, 0, sizeof(_foodArray));
+	memset(_dirXY, 0, sizeof(_dirXY));
 	_objects.clear();
 
 	// expand logic
@@ -122,6 +123,7 @@ void ColonyEngine::loadMap(int mnum) {
 		}
 	}
 	free(buffer);
+	_dynamicObjectBase = (int)_objects.size();
 	_level = mnum;
 	_me.type = kMeNum;
 
@@ -134,10 +136,11 @@ void ColonyEngine::loadMap(int mnum) {
 	debugC(1, kColonyDebugMap, "Successfully loaded map %d (objects: %d)", mnum, (int)_objects.size());
 }
 
-// PATCH.C: Create a new object in _objects and register in _robotArray.
-// Mirrors DOS CreateObject()  sets basic Thing fields for static objects.
-void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
-	Thing obj;
+void ColonyEngine::resetObjectSlot(int slot, int type, int xloc, int yloc, uint8 ang) {
+	if (slot < 0 || slot >= (int)_objects.size())
+		return;
+
+	Thing &obj = _objects[slot];
 	memset(&obj, 0, sizeof(obj));
 	const int lvl = MIN<int>(MAX<int>(_level - 1, 0), 5);
 	while (ang > 255)
@@ -176,26 +179,39 @@ void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
 		obj.where.power[2] = basePower;
 	}
 
-	// Try to reuse a dead slot (starting after kMeNum)
+	const int objNum = slot + 1;
+	if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+	    obj.where.yindex >= 0 && obj.where.yindex < 32) {
+		if (type > kRobUPyramid && type < kRobQueen)
+			_foodArray[obj.where.xindex][obj.where.yindex] = (uint8)objNum;
+		else
+			_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)objNum;
+	}
+	if (objNum > _robotNum)
+		_robotNum = objNum;
+}
+
+// PATCH.C: Create a new object in _objects and register in the proper grid.
+void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
+	if (_dynamicObjectBase < 0 || _dynamicObjectBase > (int)_objects.size())
+		_dynamicObjectBase = (int)_objects.size();
+
 	int slot = -1;
-	for (int j = kMeNum; j < (int)_objects.size(); j++) {
+	for (int j = _dynamicObjectBase; j < (int)_objects.size(); j++) {
 		if (!_objects[j].alive) {
 			slot = j;
 			break;
 		}
 	}
 	if (slot >= 0) {
-		_objects[slot] = obj;
+		resetObjectSlot(slot, type, xloc, yloc, ang);
 	} else {
+		Thing obj;
+		memset(&obj, 0, sizeof(obj));
 		_objects.push_back(obj);
 		slot = (int)_objects.size() - 1;
+		resetObjectSlot(slot, type, xloc, yloc, ang);
 	}
-	int objNum = slot + 1; // 1-based for _robotArray
-	if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-	    obj.where.yindex >= 0 && obj.where.yindex < 32)
-		_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)objNum;
-	if (slot + 1 > _robotNum)
-		_robotNum = slot + 1;
 }
 
 void ColonyEngine::saveLevelState() {
@@ -204,7 +220,7 @@ void ColonyEngine::saveLevelState() {
 
 	LevelData &ld = _levelData[_level - 1];
 	ld.visit = 1;
-	ld.queen = 0;
+	ld.queen = _allGrow ? 1 : 0;
 	memset(ld.object, 0, sizeof(ld.object));
 
 	for (uint i = 0; i < _objects.size(); i++) {
@@ -212,9 +228,6 @@ void ColonyEngine::saveLevelState() {
 		if (!obj.alive || obj.type <= 0 || obj.type > kBaseObject)
 			continue;
 
-		if (obj.type == kRobQueen)
-			ld.queen = 1;
-
 		if (ld.object[obj.type] < 255)
 			ld.object[obj.type]++;
 	}
@@ -360,6 +373,7 @@ bool ColonyEngine::patchMapFrom(const PassPatch &from, uint8 *mapdata) {
 // Level 1 = no robots; Level 2 = 25; Level 3-4 = 30; Level 5-7 = 35.
 // Robot #1 = QUEEN, #2 = SNOOP, rest = random type weighted by level.
 void ColonyEngine::initRobots() {
+	_allGrow = false;
 	if (_level == 1)
 		return;  // Level 1 has no robots
 
@@ -390,7 +404,7 @@ void ColonyEngine::initRobots() {
 				xloc = 2 + _randomSource.getRandomNumber(26);  // 2..28
 				yloc = 2 + _randomSource.getRandomNumber(26);  // 2..28
 			}
-		} while (_robotArray[xloc][yloc] != 0);
+		} while (_robotArray[xloc][yloc] != 0 || _foodArray[xloc][yloc] != 0);
 
 		// Convert grid coords to world coords (center of cell)
 		int wxloc = (xloc << 8) + 128;
@@ -399,15 +413,24 @@ void ColonyEngine::initRobots() {
 	};
 
 	if (ld.visit) {
+		_allGrow = (ld.queen != 0);
 		for (uint i = 1; i < ARRAYSIZE(kRobotTypeOrder); i++) {
 			const int type = kRobotTypeOrder[i];
 			for (int count = 0; count < ld.object[type]; count++)
 				spawnType(type);
 		}
+		if (!_allGrow) {
+			for (uint i = 0; i < _objects.size(); i++) {
+				Thing &obj = _objects[i];
+				if (obj.alive && obj.type >= kRobEye && obj.type <= kRobUPyramid)
+					obj.grow = -1;
+			}
+		}
 		debugC(1, kColonyDebugMap, "initRobots: restored %d robot/object types on level %d", maxrob, _level);
 		return;
 	}
 
+	_allGrow = true;
 	ld.queen = 1;
 	for (int i = 1; i <= maxrob; i++) {
 		int type;
diff --git a/engines/colony/module.mk b/engines/colony/module.mk
index f2e5b391f56..1f9225a33e6 100644
--- a/engines/colony/module.mk
+++ b/engines/colony/module.mk
@@ -14,6 +14,7 @@ MODULE_OBJS := \
 	render.o \
 	renderer_opengl.o \
 	sound.o \
+	think.o \
 	ui.o
 
 MODULE_DIRS += \
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 2e8c1fd0df6..5f0476c0e48 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -305,7 +305,8 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 					return r;
 			}
 			debugC(1, kColonyDebugMove, "Collision South at x=%d y=%d", pobject->xindex, yind2);
-			_sound->play(Sound::kBang);
+			if (!_suppressCollisionSound)
+				_sound->play(Sound::kBang);
 			return -1;
 
 		}
@@ -318,7 +319,8 @@ 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);
-		_sound->play(Sound::kBang);
+		if (!_suppressCollisionSound)
+			_sound->play(Sound::kBang);
 		return -1;
 
 	}
@@ -333,7 +335,8 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 					return r;
 			}
 			debugC(1, kColonyDebugMove, "Collision East at x=%d y=%d", xind2, pobject->yindex);
-			_sound->play(Sound::kBang);
+			if (!_suppressCollisionSound)
+				_sound->play(Sound::kBang);
 			return -1;
 
 		}
@@ -346,7 +349,8 @@ 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);
-		_sound->play(Sound::kBang);
+		if (!_suppressCollisionSound)
+			_sound->play(Sound::kBang);
 		return -1;
 
 	}
@@ -852,31 +856,43 @@ void ColonyEngine::checkCenter() {
 		return;
 
 	const uint8 cellType = _mapData[_me.xindex][_me.yindex][4][0];
-	if (cellType == 0)
-		return;
-
-	switch (cellType) {
-	case 1: { // SMHOLEFLR  small floor hole, must be near center
-		// DOS: xcheck=abs(xloc-(xindex<<8)); if(xcheck>64&&xcheck<192)
-		int xcheck = ABS(_me.xloc - (_me.xindex << 8));
-		int ycheck = ABS(_me.yloc - (_me.yindex << 8));
-		if (xcheck > 64 && xcheck < 192 && ycheck > 64 && ycheck < 192)
+	if (cellType != 0) {
+		switch (cellType) {
+		case 1: { // SMHOLEFLR  small floor hole, must be near center
+			// DOS: xcheck=abs(xloc-(xindex<<8)); if(xcheck>64&&xcheck<192)
+			int xcheck = ABS(_me.xloc - (_me.xindex << 8));
+			int ycheck = ABS(_me.yloc - (_me.yindex << 8));
+			if (xcheck > 64 && xcheck < 192 && ycheck > 64 && ycheck < 192)
+				fallThroughHole();
+			break;
+		}
+		case 2: // LGHOLEFLR  large floor hole, full cell
 			fallThroughHole();
-		break;
+			break;
+		case 5: // HOTFOOT  electric floor, damages power
+			_sound->play(Sound::kBzzz);
+			setPower(-(5 << _level), -(5 << _level), -(5 << _level));
+			break;
+		default:
+			break;
+		}
 	}
-	case 2: // LGHOLEFLR  large floor hole, full cell
-		fallThroughHole();
-		break;
-	case 5: // HOTFOOT  electric floor, damages power
-		_sound->play(Sound::kBzzz);
-		setPower(-(5 << _level), -(5 << _level), -(5 << _level));
-		break;
-	default:
-		break;
+
+	if (!_fl && _me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32) {
+		const uint8 foodNum = _foodArray[_me.xindex][_me.yindex];
+		if (foodNum > 0 && foodNum <= _objects.size() && _objects[foodNum - 1].type < kBaseObject) {
+			const int xcheck = ABS(_me.xloc - (_me.xindex << 8));
+			const int ycheck = ABS(_me.yloc - (_me.yindex << 8));
+			if (xcheck > 64 && xcheck < 192 && ycheck > 64 && ycheck < 192)
+				meEat();
+		}
 	}
 }
 
 void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
+	const int oldX = _me.xindex;
+	const int oldY = _me.yindex;
+
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 		_robotArray[_me.xindex][_me.yindex] = 0;
 
@@ -884,6 +900,10 @@ void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
 	if (robot > 0 && allowInteraction)
 		interactWithObject(robot);
 
+	if (_gameMode == kModeColony && oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32 &&
+	    (oldX != _me.xindex || oldY != _me.yindex))
+		notePlayerTrail(oldX, oldY, _me.xindex, _me.yindex);
+
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 		_robotArray[_me.xindex][_me.yindex] = kMeNum;
 }


Commit: 07b0a11d0e759da545bdb8509a8e580db7eeed26
    https://github.com/scummvm/scummvm/commit/07b0a11d0e759da545bdb8509a8e580db7eeed26
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:37+02:00

Commit Message:
COLONY: savegames

Changed paths:
  A engines/colony/savegame.cpp
  A engines/colony/think.cpp
    engines/colony/colony.h
    engines/colony/module.mk


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index ba96bc9b7d7..9d6a363fa31 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -358,6 +358,11 @@ public:
 	virtual ~ColonyEngine();
 
 	Common::Error run() override;
+	bool hasFeature(EngineFeature f) const override;
+	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
+	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
+	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
+	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
 	Common::Platform getPlatform() const { return _gameDescription->platform; }
 
 	void initTrig();
diff --git a/engines/colony/module.mk b/engines/colony/module.mk
index 1f9225a33e6..3135abfa9bd 100644
--- a/engines/colony/module.mk
+++ b/engines/colony/module.mk
@@ -13,6 +13,7 @@ MODULE_OBJS := \
 	movement.o \
 	render.o \
 	renderer_opengl.o \
+	savegame.o \
 	sound.o \
 	think.o \
 	ui.o
diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
new file mode 100644
index 00000000000..b4ca2d39f88
--- /dev/null
+++ b/engines/colony/savegame.cpp
@@ -0,0 +1,507 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "common/translation.h"
+
+namespace Colony {
+
+namespace {
+
+static const uint32 kSaveVersion = 1;
+static const uint32 kMaxSaveObjects = 255;
+static const uint32 kMaxSavePatches = 100;
+
+Common::Error makeReadError(const char *msg) {
+	return Common::Error(Common::kReadingFailed, msg);
+}
+
+Common::Error makeWriteError(const char *msg) {
+	return Common::Error(Common::kWritingFailed, msg);
+}
+
+void writeRect(Common::WriteStream *stream, const Common::Rect &rect) {
+	stream->writeSint32LE(rect.left);
+	stream->writeSint32LE(rect.top);
+	stream->writeSint32LE(rect.right);
+	stream->writeSint32LE(rect.bottom);
+}
+
+Common::Rect readRect(Common::SeekableReadStream *stream) {
+	const int left = stream->readSint32LE();
+	const int top = stream->readSint32LE();
+	const int right = stream->readSint32LE();
+	const int bottom = stream->readSint32LE();
+	return Common::Rect(left, top, right, bottom);
+}
+
+void writeLocate(Common::WriteStream *stream, const Locate &loc) {
+	stream->writeByte(loc.ang);
+	stream->writeByte(loc.look);
+	stream->writeByte((byte)loc.lookY);
+	stream->writeSint32LE(loc.lookx);
+	stream->writeSint32LE(loc.delta);
+	stream->writeSint32LE(loc.xloc);
+	stream->writeSint32LE(loc.yloc);
+	stream->writeSint32LE(loc.xindex);
+	stream->writeSint32LE(loc.yindex);
+	stream->writeSint32LE(loc.xmx);
+	stream->writeSint32LE(loc.xmn);
+	stream->writeSint32LE(loc.zmx);
+	stream->writeSint32LE(loc.zmn);
+	for (int i = 0; i < 3; i++)
+		stream->writeSint32LE(loc.power[i]);
+	stream->writeSint32LE(loc.type);
+	stream->writeSint32LE(loc.dx);
+	stream->writeSint32LE(loc.dy);
+	stream->writeSint32LE(loc.dist);
+}
+
+Locate readLocate(Common::SeekableReadStream *stream) {
+	Locate loc;
+	loc.ang = stream->readByte();
+	loc.look = stream->readByte();
+	loc.lookY = (int8)stream->readSByte();
+	loc.lookx = stream->readSint32LE();
+	loc.delta = stream->readSint32LE();
+	loc.xloc = stream->readSint32LE();
+	loc.yloc = stream->readSint32LE();
+	loc.xindex = stream->readSint32LE();
+	loc.yindex = stream->readSint32LE();
+	loc.xmx = stream->readSint32LE();
+	loc.xmn = stream->readSint32LE();
+	loc.zmx = stream->readSint32LE();
+	loc.zmn = stream->readSint32LE();
+	for (int i = 0; i < 3; i++)
+		loc.power[i] = stream->readSint32LE();
+	loc.type = stream->readSint32LE();
+	loc.dx = stream->readSint32LE();
+	loc.dy = stream->readSint32LE();
+	loc.dist = stream->readSint32LE();
+	return loc;
+}
+
+void writeThing(Common::WriteStream *stream, const Thing &thing) {
+	stream->writeSint32LE(thing.type);
+	stream->writeSint32LE(thing.visible);
+	stream->writeSint32LE(thing.alive);
+	writeRect(stream, thing.clip);
+	stream->writeSint32LE(thing.count);
+	writeLocate(stream, thing.where);
+	stream->writeSint32LE(thing.opcode);
+	stream->writeSint32LE(thing.counter);
+	stream->writeSint32LE(thing.time);
+	stream->writeSint32LE(thing.grow);
+}
+
+Thing readThing(Common::SeekableReadStream *stream) {
+	Thing thing;
+	thing.type = stream->readSint32LE();
+	thing.visible = stream->readSint32LE();
+	thing.alive = stream->readSint32LE();
+	thing.clip = readRect(stream);
+	thing.count = stream->readSint32LE();
+	thing.where = readLocate(stream);
+	thing.opcode = stream->readSint32LE();
+	thing.counter = stream->readSint32LE();
+	thing.time = stream->readSint32LE();
+	thing.grow = stream->readSint32LE();
+	return thing;
+}
+
+void writePassPatch(Common::WriteStream *stream, const PassPatch &patch) {
+	stream->writeByte(patch.level);
+	stream->writeByte(patch.xindex);
+	stream->writeByte(patch.yindex);
+	stream->writeSint32LE(patch.xloc);
+	stream->writeSint32LE(patch.yloc);
+	stream->writeByte(patch.ang);
+}
+
+PassPatch readPassPatch(Common::SeekableReadStream *stream) {
+	PassPatch patch;
+	patch.level = stream->readByte();
+	patch.xindex = stream->readByte();
+	patch.yindex = stream->readByte();
+	patch.xloc = stream->readSint32LE();
+	patch.yloc = stream->readSint32LE();
+	patch.ang = stream->readByte();
+	return patch;
+}
+
+void writePatchEntry(Common::WriteStream *stream, const PatchEntry &entry) {
+	stream->writeByte(entry.from.level);
+	stream->writeByte(entry.from.xindex);
+	stream->writeByte(entry.from.yindex);
+	stream->writeByte(entry.to.level);
+	stream->writeByte(entry.to.xindex);
+	stream->writeByte(entry.to.yindex);
+	stream->writeSint32LE(entry.to.xloc);
+	stream->writeSint32LE(entry.to.yloc);
+	stream->writeByte(entry.to.ang);
+	stream->writeByte(entry.type);
+	for (int i = 0; i < 5; i++)
+		stream->writeByte(entry.mapdata[i]);
+}
+
+PatchEntry readPatchEntry(Common::SeekableReadStream *stream) {
+	PatchEntry entry;
+	entry.from.level = stream->readByte();
+	entry.from.xindex = stream->readByte();
+	entry.from.yindex = stream->readByte();
+	entry.to.level = stream->readByte();
+	entry.to.xindex = stream->readByte();
+	entry.to.yindex = stream->readByte();
+	entry.to.xloc = stream->readSint32LE();
+	entry.to.yloc = stream->readSint32LE();
+	entry.to.ang = stream->readByte();
+	entry.type = stream->readByte();
+	for (int i = 0; i < 5; i++)
+		entry.mapdata[i] = stream->readByte();
+	return entry;
+}
+
+void writeLevelData(Common::WriteStream *stream, const LevelData &levelData) {
+	stream->writeByte(levelData.visit);
+	stream->writeByte(levelData.queen);
+	for (int i = 0; i <= kBaseObject; i++)
+		stream->writeByte(levelData.object[i]);
+	stream->writeByte(levelData.count);
+	stream->writeByte(levelData.size);
+	for (int i = 0; i < 10; i++) {
+		for (int j = 0; j < 3; j++)
+			stream->writeByte(levelData.location[i][j]);
+	}
+	for (int i = 0; i < 10; i++) {
+		for (int j = 0; j < 5; j++)
+			stream->writeByte(levelData.data[i][j]);
+	}
+}
+
+LevelData readLevelData(Common::SeekableReadStream *stream) {
+	LevelData levelData;
+	levelData.visit = stream->readByte();
+	levelData.queen = stream->readByte();
+	for (int i = 0; i <= kBaseObject; i++)
+		levelData.object[i] = stream->readByte();
+	levelData.count = stream->readByte();
+	levelData.size = stream->readByte();
+	for (int i = 0; i < 10; i++) {
+		for (int j = 0; j < 3; j++)
+			levelData.location[i][j] = stream->readByte();
+	}
+	for (int i = 0; i < 10; i++) {
+		for (int j = 0; j < 5; j++)
+			levelData.data[i][j] = stream->readByte();
+	}
+	return levelData;
+}
+
+bool validateGridReferences(const uint8 grid[32][32], uint32 objectCount, bool allowPlayerMarker) {
+	for (int y = 0; y < 32; y++) {
+		for (int x = 0; x < 32; x++) {
+			const uint8 value = grid[x][y];
+			if (value == 0)
+				continue;
+			if (allowPlayerMarker && value == kMeNum)
+				continue;
+			if (value > objectCount)
+				return false;
+		}
+	}
+	return true;
+}
+
+} // namespace
+
+bool ColonyEngine::hasFeature(EngineFeature f) const {
+	return f == kSupportsReturnToLauncher ||
+	       f == kSupportsLoadingDuringRuntime ||
+	       f == kSupportsSavingDuringRuntime;
+}
+
+bool ColonyEngine::canSaveGameStateCurrently(Common::U32String *msg) {
+	const bool inFpsView = (_level >= 1 && _level <= 7) &&
+		(_gameMode == kModeColony || _gameMode == kModeBattle) &&
+		!_animationRunning;
+	if (!inFpsView && msg)
+		*msg = _("Saving is only available in first-person view.");
+	return inFpsView;
+}
+
+bool ColonyEngine::canLoadGameStateCurrently(Common::U32String *msg) {
+	const bool inFpsView = (_level >= 1 && _level <= 7) &&
+		(_gameMode == kModeColony || _gameMode == kModeBattle) &&
+		!_animationRunning;
+	if (!inFpsView && msg)
+		*msg = _("Loading is only available in first-person view.");
+	return inFpsView;
+}
+
+Common::Error ColonyEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
+	if (!stream)
+		return makeWriteError("Could not open savegame stream");
+
+	if (_level >= 1 && _level <= 7) {
+		const int oldMode = _gameMode;
+		if (_gameMode != kModeColony)
+			_gameMode = kModeColony;
+		saveLevelState();
+		_gameMode = oldMode;
+	}
+
+	stream->writeUint32LE(kSaveVersion);
+	stream->writeSint32LE(_gameMode);
+	stream->writeSint32LE(_level);
+	stream->writeSint32LE(_robotNum);
+	stream->writeSint32LE(_dynamicObjectBase);
+	stream->writeUint32LE(_randomSource.getSeed());
+	stream->writeByte(_hasKeycard ? 1 : 0);
+	stream->writeByte(_unlocked ? 1 : 0);
+	stream->writeByte(_allGrow ? 1 : 0);
+	stream->writeSint32LE(_weapons);
+	stream->writeSint32LE(_armor);
+	stream->writeSint32LE(_fl);
+	stream->writeSint32LE(_orbit);
+	stream->writeSint32LE(_carryType);
+	stream->writeSint32LE(_coreIndex);
+	stream->writeUint32LE(_displayCount);
+	writeLocate(stream, _me);
+
+	for (int i = 0; i < 4; i++) {
+		stream->writeByte(_decode1[i]);
+		stream->writeByte(_decode2[i]);
+		stream->writeByte(_decode3[i]);
+	}
+
+	for (int i = 0; i < 2; i++) {
+		stream->writeSint32LE(_coreState[i]);
+		stream->writeSint32LE(_coreHeight[i]);
+	}
+	for (int i = 0; i < 3; i++)
+		stream->writeSint32LE(_corePower[i]);
+
+	for (uint i = 0; i < ARRAYSIZE(_levelData); i++)
+		writeLevelData(stream, _levelData[i]);
+
+	stream->writeUint32LE(_patches.size());
+	for (uint i = 0; i < _patches.size(); i++)
+		writePatchEntry(stream, _patches[i]);
+
+	for (int i = 0; i < 2; i++)
+		writePassPatch(stream, _carryPatch[i]);
+
+	for (int y = 0; y < 32; y++) {
+		for (int x = 0; x < 32; x++)
+			stream->writeByte(_wall[x][y]);
+	}
+	for (int y = 0; y < 31; y++) {
+		for (int x = 0; x < 31; x++) {
+			for (int dir = 0; dir < 5; dir++) {
+				for (int i = 0; i < 5; i++)
+					stream->writeByte(_mapData[x][y][dir][i]);
+			}
+		}
+	}
+	for (int y = 0; y < 32; y++) {
+		for (int x = 0; x < 32; x++) {
+			stream->writeByte(_robotArray[x][y]);
+			stream->writeByte(_foodArray[x][y]);
+			stream->writeByte(_dirXY[x][y]);
+		}
+	}
+
+	stream->writeUint32LE(_objects.size());
+	for (uint i = 0; i < _objects.size(); i++)
+		writeThing(stream, _objects[i]);
+
+	for (int i = 0; i < 16; i++)
+		writeLocate(stream, _bfight[i]);
+	writeLocate(stream, _battleEnter);
+	writeLocate(stream, _battleShip);
+	writeLocate(stream, _battleProj);
+	stream->writeByte(_projon ? 1 : 0);
+	stream->writeSint32LE(_pcount);
+	stream->writeSint32LE(_battleRound);
+	for (int i = 0; i < 256; i++)
+		stream->writeSint32LE(_mountains[i]);
+	for (int qx = 0; qx < 4; qx++) {
+		for (int qy = 0; qy < 4; qy++) {
+			for (int i = 0; i < 15; i++)
+				writeLocate(stream, _pyramids[qx][qy][i]);
+		}
+	}
+
+	if (stream->err())
+		return makeWriteError(isAutosave ? "Failed to write Colony autosave" : "Failed to write Colony savegame");
+
+	return Common::kNoError;
+}
+
+Common::Error ColonyEngine::loadGameStream(Common::SeekableReadStream *stream) {
+	if (!stream)
+		return makeReadError("Could not open savegame stream");
+
+	const uint32 version = stream->readUint32LE();
+	if (version != kSaveVersion)
+		return makeReadError("Unsupported Colony savegame version");
+
+	const int savedGameMode = stream->readSint32LE();
+	const int savedLevel = stream->readSint32LE();
+	const int savedRobotNum = stream->readSint32LE();
+	const int savedDynamicObjectBase = stream->readSint32LE();
+	const uint32 savedSeed = stream->readUint32LE();
+
+	if ((savedGameMode != kModeColony && savedGameMode != kModeBattle) || savedLevel < 1 || savedLevel > 7)
+		return makeReadError("Invalid or corrupt Colony savegame");
+
+	_gameMode = savedGameMode;
+	_level = savedLevel;
+	_robotNum = savedRobotNum;
+	_dynamicObjectBase = savedDynamicObjectBase;
+	_randomSource.setSeed(savedSeed);
+	_hasKeycard = stream->readByte() != 0;
+	_unlocked = stream->readByte() != 0;
+	_allGrow = stream->readByte() != 0;
+	_weapons = stream->readSint32LE();
+	_armor = stream->readSint32LE();
+	_fl = stream->readSint32LE();
+	_orbit = stream->readSint32LE();
+	_carryType = stream->readSint32LE();
+	_coreIndex = stream->readSint32LE();
+	_displayCount = stream->readUint32LE();
+	_me = readLocate(stream);
+
+	for (int i = 0; i < 4; i++) {
+		_decode1[i] = stream->readByte();
+		_decode2[i] = stream->readByte();
+		_decode3[i] = stream->readByte();
+	}
+
+	for (int i = 0; i < 2; i++) {
+		_coreState[i] = stream->readSint32LE();
+		_coreHeight[i] = stream->readSint32LE();
+	}
+	for (int i = 0; i < 3; i++)
+		_corePower[i] = stream->readSint32LE();
+
+	for (uint i = 0; i < ARRAYSIZE(_levelData); i++)
+		_levelData[i] = readLevelData(stream);
+
+	const uint32 patchCount = stream->readUint32LE();
+	if (patchCount > kMaxSavePatches)
+		return makeReadError("Invalid or corrupt Colony savegame");
+	_patches.resize(patchCount);
+	for (uint i = 0; i < patchCount; i++)
+		_patches[i] = readPatchEntry(stream);
+
+	for (int i = 0; i < 2; i++)
+		_carryPatch[i] = readPassPatch(stream);
+
+	for (int y = 0; y < 32; y++) {
+		for (int x = 0; x < 32; x++)
+			_wall[x][y] = stream->readByte();
+	}
+	for (int y = 0; y < 31; y++) {
+		for (int x = 0; x < 31; x++) {
+			for (int dir = 0; dir < 5; dir++) {
+				for (int i = 0; i < 5; i++)
+					_mapData[x][y][dir][i] = stream->readByte();
+			}
+		}
+	}
+	for (int y = 0; y < 32; y++) {
+		for (int x = 0; x < 32; x++) {
+			_robotArray[x][y] = stream->readByte();
+			_foodArray[x][y] = stream->readByte();
+			_dirXY[x][y] = stream->readByte();
+		}
+	}
+
+	const uint32 objectCount = stream->readUint32LE();
+	if (objectCount > kMaxSaveObjects)
+		return makeReadError("Invalid or corrupt Colony savegame");
+	_objects.resize(objectCount);
+	for (uint i = 0; i < objectCount; i++)
+		_objects[i] = readThing(stream);
+
+	for (int i = 0; i < 16; i++)
+		_bfight[i] = readLocate(stream);
+	_battleEnter = readLocate(stream);
+	_battleShip = readLocate(stream);
+	_battleProj = readLocate(stream);
+	_projon = stream->readByte() != 0;
+	_pcount = stream->readSint32LE();
+	_battleRound = stream->readSint32LE();
+	for (int i = 0; i < 256; i++)
+		_mountains[i] = stream->readSint32LE();
+	for (int qx = 0; qx < 4; qx++) {
+		for (int qy = 0; qy < 4; qy++) {
+			for (int i = 0; i < 15; i++)
+				_pyramids[qx][qy][i] = readLocate(stream);
+		}
+	}
+
+	if (stream->err())
+		return makeReadError("Invalid or corrupt Colony savegame");
+
+	if (_coreIndex < 0 || _coreIndex > 1)
+		return makeReadError("Invalid or corrupt Colony savegame");
+	if (_weapons < 0 || _weapons > 3 || _armor < 0 || _armor > 3)
+		return makeReadError("Invalid or corrupt Colony savegame");
+	if (_fl < 0 || _fl > 2 || _orbit < 0 || _orbit > 1)
+		return makeReadError("Invalid or corrupt Colony savegame");
+	for (uint i = 0; i < ARRAYSIZE(_levelData); i++) {
+		if (_levelData[i].size > 10)
+			return makeReadError("Invalid or corrupt Colony savegame");
+	}
+	if (_dynamicObjectBase < 0 || _dynamicObjectBase > (int)_objects.size())
+		return makeReadError("Invalid or corrupt Colony savegame");
+	if (_robotNum < kMeNum + 1 || _robotNum > 255)
+		return makeReadError("Invalid or corrupt Colony savegame");
+	if (!validateGridReferences(_robotArray, objectCount, true) || !validateGridReferences(_foodArray, objectCount, false))
+		return makeReadError("Invalid or corrupt Colony savegame");
+
+	deleteAnimation();
+	_animationName.clear();
+	_backgroundActive = false;
+	_animationRunning = false;
+	_animationResult = 0;
+	_doorOpen = false;
+	_elevatorFloor = 0;
+	_airlockX = -1;
+	_airlockY = -1;
+	_airlockDirection = -1;
+	_airlockTerminate = false;
+	_battleMaxP = 0;
+	_suppressCollisionSound = false;
+	_moveForward = false;
+	_moveBackward = false;
+	_strafeLeft = false;
+	_strafeRight = false;
+	_rotateLeft = false;
+	_rotateRight = false;
+	_lastClickTime = 0;
+	_lastAnimUpdate = 0;
+	_lastWarningChimeTime = 0;
+	_battledx = _width / 59;
+	updateViewportLayout();
+
+	return Common::kNoError;
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
new file mode 100644
index 00000000000..c775aa9bbb2
--- /dev/null
+++ b/engines/colony/think.cpp
@@ -0,0 +1,769 @@
+#include "colony/colony.h"
+#include "common/debug.h"
+
+namespace Colony {
+
+enum {
+	kOpcodeLRotate = 1,
+	kOpcodeRRotate = 2,
+	kOpcodeForward = 3,
+	kOpcodeFShoot = 4,
+	kOpcodeSnoop = 20
+};
+
+static bool isBaseRobotType(int type) {
+	return type >= kRobEye && type <= kRobUPyramid;
+}
+
+static bool isEggType(int type) {
+	return type > kRobUPyramid && type < kRobQueen;
+}
+
+static uint8 trailCodeForDelta(int dx, int dy) {
+	if (dx > 1)
+		dx = 1;
+	else if (dx < -1)
+		dx = -1;
+
+	if (dy > 1)
+		dy = 1;
+	else if (dy < -1)
+		dy = -1;
+
+	if (dx > 0 && dy > 0)
+		return 1;
+	if (dx > 0 && dy == 0)
+		return 2;
+	if (dx > 0 && dy < 0)
+		return 3;
+	if (dx == 0 && dy < 0)
+		return 4;
+	if (dx < 0 && dy < 0)
+		return 5;
+	if (dx < 0 && dy == 0)
+		return 6;
+	if (dx < 0 && dy > 0)
+		return 7;
+	if (dx == 0 && dy > 0)
+		return 8;
+
+	return 0;
+}
+
+static int trailTargetAngle(uint8 code) {
+	switch (code) {
+	case 2:
+		return 0;
+	case 1:
+		return 32;
+	case 8:
+		return 64;
+	case 7:
+		return 96;
+	case 6:
+		return 128;
+	case 5:
+		return 160;
+	case 4:
+		return 192;
+	case 3:
+		return 224;
+	default:
+		return -1;
+	}
+}
+
+void ColonyEngine::notePlayerTrail(int oldX, int oldY, int newX, int newY) {
+	if (oldX < 0 || oldX >= 32 || oldY < 0 || oldY >= 32)
+		return;
+	if (newX < 0 || newX >= 32 || newY < 0 || newY >= 32)
+		return;
+
+	const uint8 code = trailCodeForDelta(newX - oldX, newY - oldY);
+	if (code != 0)
+		_dirXY[oldX][oldY] = code;
+}
+
+void ColonyEngine::respawnObject(int num, int type) {
+	if (num <= 0 || num > (int)_objects.size())
+		return;
+
+	Thing &obj = _objects[num - 1];
+	if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+	    obj.where.yindex >= 0 && obj.where.yindex < 32) {
+		if (isEggType(obj.type)) {
+			if (_foodArray[obj.where.xindex][obj.where.yindex] == num)
+				_foodArray[obj.where.xindex][obj.where.yindex] = 0;
+		} else if (_robotArray[obj.where.xindex][obj.where.yindex] == num) {
+			_robotArray[obj.where.xindex][obj.where.yindex] = 0;
+		}
+	}
+
+	int xloc, yloc;
+	do {
+		if (_level == 7 && type == kRobQueen) {
+			xloc = 27;
+			yloc = 10;
+		} else {
+			xloc = 2 + _randomSource.getRandomNumber(26);
+			yloc = 2 + _randomSource.getRandomNumber(26);
+		}
+	} while (_robotArray[xloc][yloc] || _foodArray[xloc][yloc]);
+
+	resetObjectSlot(num - 1, type, (xloc << 8) + 128, (yloc << 8) + 128, _randomSource.getRandomNumber(255));
+}
+
+void ColonyEngine::cThink() {
+	if (_gameMode != kModeColony)
+		return;
+
+	const int objectCount = (int)_objects.size();
+	for (int num = 1; num <= objectCount; ++num) {
+		if (num > (int)_objects.size())
+			break;
+
+		const Thing &obj = _objects[num - 1];
+		if (!obj.alive || obj.type <= 0 || obj.type > kBaseObject)
+			continue;
+
+		switch (obj.type) {
+		case kRobEye:
+			eyeThink(num);
+			break;
+		case kRobPyramid:
+			pyramidThink(num);
+			break;
+		case kRobCube:
+			cubeThink(num);
+			break;
+		case kRobUPyramid:
+			upyramidThink(num);
+			break;
+		case kRobQueen:
+			queenThink(num);
+			break;
+		case kRobDrone:
+		case kRobSoldier:
+			droneThink(num);
+			break;
+		case kRobSnoop:
+			snoopThink(num);
+			break;
+		default:
+			eggThink(num);
+			break;
+		}
+	}
+}
+
+void ColonyEngine::cubeThink(int num) {
+	Thing &obj = _objects[num - 1];
+	obj.where.look = (uint8)(obj.where.look + 15);
+	moveThink(num);
+	obj.time--;
+	if (obj.time < 0 && layEgg(kRobMCube, obj.where.xindex, obj.where.yindex))
+		obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+	if (obj.grow)
+		bigGrow(num);
+}
+
+void ColonyEngine::pyramidThink(int num) {
+	Thing &obj = _objects[num - 1];
+	obj.where.look = (uint8)(obj.where.look + 15);
+	moveThink(num);
+	obj.time--;
+	if (obj.time < 0 && layEgg(kRobMPyramid, obj.where.xindex, obj.where.yindex))
+		obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+	if (obj.grow)
+		bigGrow(num);
+}
+
+void ColonyEngine::upyramidThink(int num) {
+	Thing &obj = _objects[num - 1];
+	obj.where.look = (uint8)(obj.where.look - 15);
+	moveThink(num);
+	obj.time--;
+	if (obj.time < 0 && layEgg(kRobMUPyramid, obj.where.xindex, obj.where.yindex))
+		obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+	if (obj.grow)
+		bigGrow(num);
+}
+
+void ColonyEngine::eyeThink(int num) {
+	Thing &obj = _objects[num - 1];
+	moveThink(num);
+	obj.time--;
+	if (obj.time < 0 && layEgg(kRobMEye, obj.where.xindex, obj.where.yindex))
+		obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+	if (obj.grow)
+		bigGrow(num);
+}
+
+void ColonyEngine::queenThink(int num) {
+	Thing &obj = _objects[num - 1];
+	moveThink(num);
+	obj.time--;
+	obj.where.lookx += obj.where.delta;
+	if (obj.where.lookx < -24 || obj.where.lookx > 24)
+		obj.where.delta = -obj.where.delta;
+	obj.where.look = (uint8)(obj.where.ang + obj.where.lookx);
+
+	if (obj.time < 0) {
+		switch (_randomSource.getRandomNumber(3)) {
+		case 0:
+			if (layEgg(kRobMEye, obj.where.xindex, obj.where.yindex))
+				obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+			break;
+		case 1:
+			if (layEgg(kRobMCube, obj.where.xindex, obj.where.yindex))
+				obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+			break;
+		case 2:
+			if (layEgg(kRobMPyramid, obj.where.xindex, obj.where.yindex))
+				obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+			break;
+		case 3:
+			if (layEgg(kRobMUPyramid, obj.where.xindex, obj.where.yindex))
+				obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+			break;
+		}
+	}
+
+	if (!obj.grow)
+		return;
+
+	if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+	    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+	    _robotArray[obj.where.xindex][obj.where.yindex] == num)
+		_robotArray[obj.where.xindex][obj.where.yindex] = 0;
+
+	obj.alive = 0;
+	_allGrow = false;
+	_sound->play(Sound::kExplode);
+
+	for (uint i = 0; i < _objects.size(); ++i) {
+		Thing &other = _objects[i];
+		if (other.alive && isBaseRobotType(other.type))
+			other.grow = -1;
+	}
+
+	if (_level >= 1 && _level <= 7)
+		_levelData[_level - 1].queen = 0;
+}
+
+void ColonyEngine::droneThink(int num) {
+	Thing &obj = _objects[num - 1];
+	moveThink(num);
+	obj.time--;
+	obj.where.lookx += obj.where.delta;
+	if (obj.where.lookx < -24 || obj.where.lookx > 24)
+		obj.where.delta = -obj.where.delta;
+	obj.where.look = (uint8)(obj.where.ang + obj.where.lookx);
+
+	if (!obj.grow)
+		return;
+
+	if (obj.type == kRobDrone) {
+		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+		    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+		    _robotArray[obj.where.xindex][obj.where.yindex] == num)
+			_robotArray[obj.where.xindex][obj.where.yindex] = 0;
+		obj.alive = 0;
+		_sound->play(Sound::kExplode);
+	} else {
+		obj.type = kRobDrone;
+		obj.grow = 0;
+		obj.where.power[1] = 10 + ((_randomSource.getRandomNumber(15) & 0x0F) << _level);
+	}
+}
+
+void ColonyEngine::eggThink(int num) {
+	Thing &obj = _objects[num - 1];
+	if (!isEggType(obj.type))
+		return;
+
+	if (_allGrow) {
+		obj.time--;
+	} else if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+	           obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+	           _foodArray[obj.where.xindex][obj.where.yindex] == 0) {
+		_foodArray[obj.where.xindex][obj.where.yindex] = (uint8)num;
+		if (_robotArray[obj.where.xindex][obj.where.yindex] == num)
+			_robotArray[obj.where.xindex][obj.where.yindex] = 0;
+		obj.grow = -1;
+	}
+
+	if (obj.time <= 0)
+		obj.grow = 1;
+	if (obj.grow)
+		growRobot(num);
+}
+
+bool ColonyEngine::layEgg(int type, int xindex, int yindex) {
+	auto hasFood = [this](int x, int y) -> bool {
+		if (x < 0 || x >= 32 || y < 0 || y >= 32)
+			return false;
+		return _foodArray[x][y] != 0;
+	};
+
+	if (hasFood(xindex, yindex) ||
+	    hasFood(xindex + 1, yindex) ||
+	    hasFood(xindex - 1, yindex) ||
+	    hasFood(xindex, yindex + 1) ||
+	    hasFood(xindex, yindex - 1))
+		return false;
+
+	createObject(type, (xindex << 8) + 128, (yindex << 8) + 128, _randomSource.getRandomNumber(255));
+	return true;
+}
+
+void ColonyEngine::moveThink(int num) {
+	if (num <= 0 || num > (int)_objects.size())
+		return;
+
+	Thing &obj = _objects[num - 1];
+	if (!obj.alive)
+		return;
+
+	switch (obj.opcode) {
+	case kOpcodeLRotate:
+		obj.where.ang = (uint8)(obj.where.ang + 7);
+		obj.where.look = (uint8)(obj.where.look + 7);
+		obj.counter--;
+		if (obj.counter <= 0) {
+			obj.opcode = kOpcodeForward;
+			obj.counter = _randomSource.getRandomNumber(255) & 0x0F;
+		}
+		if (scanForPlayer(num) == kMeNum) {
+			robotShoot(num);
+			obj.opcode = kOpcodeFShoot;
+			obj.counter = _randomSource.getRandomNumber(255) & 0x3F;
+		}
+		return;
+
+	case kOpcodeRRotate:
+		obj.where.ang = (uint8)(obj.where.ang - 7);
+		obj.where.look = (uint8)(obj.where.look - 7);
+		obj.counter--;
+		if (obj.counter <= 0) {
+			obj.opcode = kOpcodeForward;
+			obj.counter = _randomSource.getRandomNumber(255) & 0x3F;
+		}
+		if (scanForPlayer(num) == kMeNum) {
+			robotShoot(num);
+			obj.opcode = kOpcodeFShoot;
+			obj.counter = _randomSource.getRandomNumber(255) & 0x1F;
+		}
+		return;
+
+	case kOpcodeForward: {
+		obj.counter--;
+		if (obj.counter <= 0) {
+			obj.opcode = (_randomSource.getRandomNumber(1) ? kOpcodeLRotate : kOpcodeRRotate);
+			obj.counter = _randomSource.getRandomNumber(255) & 0x0F;
+			return;
+		}
+
+		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+		    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+		    _robotArray[obj.where.xindex][obj.where.yindex] == 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);
+		_suppressCollisionSound = false;
+
+		if (collide) {
+			obj.opcode = (_randomSource.getRandomNumber(1) ? kOpcodeLRotate : kOpcodeRRotate);
+			obj.counter = _randomSource.getRandomNumber(255) & 0x0F;
+		}
+
+		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+		    obj.where.yindex >= 0 && obj.where.yindex < 32)
+			_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)num;
+		return;
+	}
+
+	case kOpcodeFShoot: {
+		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+		    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+		    _robotArray[obj.where.xindex][obj.where.yindex] == 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);
+		_suppressCollisionSound = false;
+
+		if (collide) {
+			obj.opcode = (_randomSource.getRandomNumber(1) ? kOpcodeLRotate : kOpcodeRRotate);
+			obj.counter = _randomSource.getRandomNumber(255) & 0x0F;
+		}
+
+		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+		    obj.where.yindex >= 0 && obj.where.yindex < 32)
+			_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)num;
+
+		if (scanForPlayer(num) == kMeNum) {
+			robotShoot(num);
+		} else {
+			obj.opcode = kOpcodeForward;
+			obj.counter = _randomSource.getRandomNumber(255) & 0x0F;
+		}
+		return;
+	}
+
+	default:
+		obj.alive = 0;
+		obj.opcode = kOpcodeForward;
+		obj.counter = _randomSource.getRandomNumber(255) & 0x0F;
+		break;
+	}
+}
+
+void ColonyEngine::snoopThink(int num) {
+	if (num <= 0 || num > (int)_objects.size())
+		return;
+
+	Thing &obj = _objects[num - 1];
+	if (!obj.alive)
+		return;
+
+	switch (obj.opcode) {
+	case kOpcodeLRotate:
+		obj.where.ang = (uint8)(obj.where.ang + 7);
+		obj.where.look = obj.where.ang;
+		obj.counter--;
+		if (obj.counter <= 0) {
+			obj.opcode = kOpcodeForward;
+			obj.counter = _randomSource.getRandomNumber(255) & 0x0F;
+		}
+		break;
+
+	case kOpcodeRRotate:
+		obj.where.ang = (uint8)(obj.where.ang - 7);
+		obj.where.look = obj.where.ang;
+		obj.counter--;
+		if (obj.counter <= 0) {
+			obj.opcode = kOpcodeForward;
+			obj.counter = _randomSource.getRandomNumber(255) & 0x3F;
+		}
+		break;
+
+	case kOpcodeForward: {
+		obj.counter--;
+		if (obj.counter <= 0) {
+			obj.opcode = (_randomSource.getRandomNumber(1) ? kOpcodeLRotate : kOpcodeRRotate);
+			obj.counter = _randomSource.getRandomNumber(255) & 0x0F;
+			break;
+		}
+
+		const int oldX = obj.where.xindex;
+		const int oldY = obj.where.yindex;
+		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);
+		_suppressCollisionSound = true;
+		const int collide = checkwall(fx, fy, &obj.where);
+		_suppressCollisionSound = false;
+
+		if (collide == kMeNum) {
+			respawnObject(num, kRobSnoop);
+			_sound->play(Sound::kSlug);
+			setPower(-(int)MIN<int32>((3 * _me.power[0]) / 4, 32000),
+				-(int)MIN<int32>((3 * _me.power[1]) / 4, 32000),
+				-(int)MIN<int32>((3 * _me.power[2]) / 4, 32000));
+			return;
+		}
+
+		if (collide > 0 && collide <= (int)_objects.size()) {
+			const Thing &other = _objects[collide - 1];
+			if (other.alive && other.type >= kRobEye && other.type <= kBaseObject) {
+				respawnObject(collide, other.type);
+				obj.where.xloc = fx;
+				obj.where.yloc = fy;
+				obj.where.xindex = fx >> 8;
+				obj.where.yindex = fy >> 8;
+			} else {
+				obj.opcode = (_randomSource.getRandomNumber(1) ? kOpcodeLRotate : kOpcodeRRotate);
+				obj.counter = _randomSource.getRandomNumber(255) & 0x0F;
+			}
+		} else if (collide) {
+			obj.opcode = (_randomSource.getRandomNumber(1) ? kOpcodeLRotate : kOpcodeRRotate);
+			obj.counter = _randomSource.getRandomNumber(255) & 0x0F;
+		}
+
+		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+		    obj.where.yindex >= 0 && obj.where.yindex < 32)
+			_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)num;
+
+		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+		    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+		    _dirXY[obj.where.xindex][obj.where.yindex]) {
+			obj.opcode = kOpcodeSnoop;
+			obj.where.ang &= 0xF8;
+			obj.where.look = obj.where.ang;
+		}
+		break;
+	}
+
+	case kOpcodeSnoop: {
+		const int target = trailTargetAngle(_dirXY[obj.where.xindex][obj.where.yindex]);
+		if (target >= 0 && obj.where.ang != target) {
+			const int diff = (target - obj.where.ang) & 0xFF;
+			if (diff < 128)
+				obj.where.ang = (uint8)(obj.where.ang + 8);
+			else
+				obj.where.ang = (uint8)(obj.where.ang - 8);
+			obj.where.look = obj.where.ang;
+			return;
+		}
+
+		obj.opcode = kOpcodeForward;
+		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+		    obj.where.yindex >= 0 && obj.where.yindex < 32)
+			_dirXY[obj.where.xindex][obj.where.yindex] = 0;
+		obj.counter = 0x3F;
+		break;
+	}
+
+	default:
+		obj.alive = 0;
+		obj.opcode = kOpcodeForward;
+		obj.counter = _randomSource.getRandomNumber(255) & 0x0F;
+		break;
+	}
+}
+
+void ColonyEngine::bigGrow(int num) {
+	Thing &obj = _objects[num - 1];
+	const int xindex = obj.where.xindex;
+	const int yindex = obj.where.yindex;
+
+	obj.count++;
+	if (xindex < 0 || xindex >= 32 || yindex < 0 || yindex >= 32)
+		return;
+	if (_foodArray[xindex][yindex] != 0)
+		return;
+
+	if (obj.where.xloc == (xindex << 8) + 128 && obj.where.yloc == (yindex << 8) + 128) {
+		_foodArray[xindex][yindex] = (uint8)num;
+		if (_robotArray[xindex][yindex] == num)
+			_robotArray[xindex][yindex] = 0;
+		obj.type += 4;
+		obj.count = 0;
+		obj.time = 10 + (_randomSource.getRandomNumber(63) & 0x3F);
+	} else {
+		obj.where.xloc = (xindex << 8) + 128;
+		obj.where.yloc = (yindex << 8) + 128;
+		obj.opcode = kOpcodeLRotate;
+		obj.counter = 100;
+		obj.count = 0;
+	}
+}
+
+void ColonyEngine::growRobot(int num) {
+	Thing &obj = _objects[num - 1];
+	obj.count++;
+	obj.time = 10 + (_randomSource.getRandomNumber(63) & 0x3F);
+
+	switch (obj.type) {
+	case kRobFEye:
+	case kRobFPyramid:
+	case kRobFCube:
+	case kRobFUPyramid:
+		if (obj.count > 3) {
+			obj.count = 0;
+			if (obj.grow == 1) {
+				obj.grow = 0;
+				obj.type -= 4;
+			} else {
+				obj.type += 4;
+			}
+		}
+		break;
+
+	case kRobSEye:
+	case kRobSPyramid:
+	case kRobSCube:
+	case kRobSUPyramid:
+		if (obj.count > 3) {
+			obj.count = 0;
+			if (obj.grow == 1) {
+				obj.type -= 4;
+			} else {
+				obj.grow = 0;
+				obj.type += 4;
+			}
+		}
+		break;
+
+	case kRobMEye:
+	case kRobMPyramid:
+	case kRobMCube:
+	case kRobMUPyramid:
+		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
+		    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+		    _robotArray[obj.where.xindex][obj.where.yindex] == 0) {
+			obj.count = 0;
+			obj.type -= 4;
+			_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)num;
+			if (_foodArray[obj.where.xindex][obj.where.yindex] == num)
+				_foodArray[obj.where.xindex][obj.where.yindex] = 0;
+		}
+		break;
+
+	default:
+		break;
+	}
+}
+
+int ColonyEngine::scanForPlayer(int num) {
+	if (num <= 0 || num > (int)_objects.size())
+		return 0;
+
+	Thing &obj = _objects[num - 1];
+	Locate fire;
+	memset(&fire, 0, sizeof(fire));
+	fire.ang = obj.where.ang;
+	fire.type = 2;
+
+	int fireX = obj.where.xloc;
+	int fireY = obj.where.yloc;
+	int collide = 0;
+
+	do {
+		fire.xloc = fireX;
+		fire.yloc = fireY;
+		fire.xindex = fireX >> 8;
+		fire.yindex = fireY >> 8;
+		fireX += (_cost[fire.ang] << 1);
+		fireY += (_sint[fire.ang] << 1);
+		_suppressCollisionSound = true;
+		collide = checkwall(fireX, fireY, &fire);
+		_suppressCollisionSound = false;
+	} while (!collide);
+
+	if (collide == kMeNum) {
+		obj.where.dx = (_me.xloc - fireX) >> 1;
+		obj.where.dy = (_me.yloc - fireY) >> 1;
+	}
+
+	return collide;
+}
+
+void ColonyEngine::robotShoot(int num) {
+	if (num <= 0 || num > (int)_objects.size())
+		return;
+
+	Thing &obj = _objects[num - 1];
+	if (!obj.alive)
+		return;
+
+	auto qlog = [](int32 x) -> int {
+		int i = 0;
+		while (x > 0) {
+			x >>= 1;
+			i++;
+		}
+		return i;
+	};
+
+	const int epower2 = qlog(_me.power[2]);
+	const int armor2 = _armor * _armor;
+	int damage = 0;
+
+	obj.opcode = kOpcodeFShoot;
+	_sound->play(Sound::kShoot);
+
+	switch (obj.type) {
+	case kRobEye:
+		damage = epower2 * armor2 - (1 << _level);
+		if (damage > -_level)
+			damage = -_level;
+		setPower(damage, damage, damage);
+		break;
+	case kRobPyramid:
+		damage = epower2 * armor2 - (1 << _level);
+		if (damage > -_level)
+			damage = -_level;
+		setPower(damage, -_level, -_level);
+		break;
+	case kRobCube:
+		damage = epower2 * armor2 - (1 << _level);
+		if (damage > -_level)
+			damage = -_level;
+		setPower(-_level, damage, -_level);
+		break;
+	case kRobUPyramid:
+		damage = epower2 * armor2 - (1 << _level);
+		if (damage > -_level)
+			damage = -_level;
+		setPower(-_level, -_level, damage);
+		break;
+	case kRobQueen:
+		damage = epower2 * armor2 - ((_level == 1 || _level == 7) ? (3 << 10) : (3 << _level));
+		if (damage > -_level)
+			damage = -(1 << _level);
+		setPower(damage, damage, damage);
+		break;
+	case kRobDrone:
+		damage = epower2 * armor2 - (4 << _level);
+		if (damage > -_level)
+			damage = -(_level << 1);
+		setPower(damage, damage, damage);
+		break;
+	case kRobSoldier:
+		damage = epower2 * armor2 - (5 << _level);
+		if (damage > -_level)
+			damage = -(_level << 1);
+		setPower(damage, damage, damage);
+		break;
+	default:
+		break;
+	}
+}
+
+void ColonyEngine::meEat() {
+	if (_me.xindex < 0 || _me.xindex >= 32 || _me.yindex < 0 || _me.yindex >= 32)
+		return;
+
+	const int foodNum = _foodArray[_me.xindex][_me.yindex];
+	if (foodNum <= 0 || foodNum > (int)_objects.size())
+		return;
+
+	Thing &obj = _objects[foodNum - 1];
+	_foodArray[_me.xindex][_me.yindex] = 0;
+	obj.alive = 0;
+	_sound->play(Sound::kEat);
+
+	switch (obj.type) {
+	case kRobMUPyramid:
+	case kRobFUPyramid:
+	case kRobSUPyramid:
+		setPower(7 << _level, 7 << _level, 15 << _level);
+		break;
+	case kRobMEye:
+	case kRobFEye:
+	case kRobSEye:
+		setPower(15 << _level, 15 << _level, 15 << _level);
+		break;
+	case kRobMPyramid:
+	case kRobFPyramid:
+	case kRobSPyramid:
+		setPower(15 << _level, 7 << _level, 7 << _level);
+		break;
+	case kRobMCube:
+	case kRobFCube:
+	case kRobSCube:
+		setPower(7 << _level, 15 << _level, 7 << _level);
+		break;
+	default:
+		break;
+	}
+}
+
+} // End of namespace Colony


Commit: 2db4ea2c3651a9881cc2cf1a5fdff7ba087b1e8f
    https://github.com/scummvm/scummvm/commit/2db4ea2c3651a9881cc2cf1a5fdff7ba087b1e8f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:38+02:00

Commit Message:
COLONY: add battle aim-point tracking and expand render color handling

Changed paths:
    engines/colony/battle.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/interaction.cpp
    engines/colony/intro.cpp
    engines/colony/render.cpp
    engines/colony/sound.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 57be9ba1b73..c65ef156948 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -1060,8 +1060,9 @@ void ColonyEngine::battleShoot() {
 	_sound->play(Sound::kBang);
 	setPower(-2, 0, 0);
 
-	const int cx = _centerX;
-	const int cy = _centerY;
+	const Common::Point aim = getAimPoint();
+	const int cx = aim.x;
+	const int cy = aim.y;
 	_gfx->setXorMode(true);
 	for (int i = 100; i < 900; i += 200) {
 		const int outer = CLIP<int>(kFloor * 128 / i, 0, 1000);
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 12be2293fb3..88c624214c0 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -31,6 +31,7 @@
 #include "common/events.h"
 #include "common/keyboard.h"
 #include "engines/util.h"
+#include "graphics/cursorman.h"
 #include "graphics/palette.h"
 #include "graphics/paletteman.h"
 #include <math.h>
@@ -48,6 +49,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_centerY = _height / 2;
 	_mouseSensitivity = 1;
 	_mouseLocked = false;
+	_mousePos = Common::Point(_centerX, _centerY);
 	_showDashBoard = true;
 	_crosshair = true;
 	_insight = false;
@@ -240,43 +242,95 @@ void ColonyEngine::menuCommandsCallback(int action, Common::String &text, void *
 	engine->handleMenuAction(action);
 }
 
+void ColonyEngine::syncMacMenuChecks() {
+	if (!_macMenu)
+		return;
+
+	Graphics::MacMenuItem *optionsMenu = _macMenu->getMenuItem(3);
+	if (!optionsMenu)
+		return;
+
+	if (Graphics::MacMenuItem *item = _macMenu->getSubMenuItem(optionsMenu, kMenuActionSound))
+		_macMenu->setCheckMark(item, _soundOn);
+	if (Graphics::MacMenuItem *item = _macMenu->getSubMenuItem(optionsMenu, kMenuActionCrosshair))
+		_macMenu->setCheckMark(item, _crosshair);
+	if (Graphics::MacMenuItem *item = _macMenu->getSubMenuItem(optionsMenu, kMenuActionPolyFill))
+		_macMenu->setCheckMark(item, !_wireframe);
+	if (Graphics::MacMenuItem *item = _macMenu->getSubMenuItem(optionsMenu, kMenuActionCursorShoot))
+		_macMenu->setCheckMark(item, _cursorShoot);
+}
+
+void ColonyEngine::updateMouseCapture(bool recenter) {
+	_system->lockMouse(_mouseLocked);
+	_system->showMouse(!_mouseLocked);
+	CursorMan.setDefaultArrowCursor();
+	CursorMan.showMouse(!_mouseLocked);
+
+	if (_mouseLocked && recenter) {
+		_mousePos = Common::Point(_centerX, _centerY);
+		_system->warpMouse(_centerX, _centerY);
+		_system->getEventManager()->purgeMouseEvents();
+	}
+}
+
+Common::Point ColonyEngine::getAimPoint() const {
+	if (_cursorShoot && !_mouseLocked) {
+		return Common::Point(CLIP<int>(_mousePos.x, _screenR.left, _screenR.right - 1),
+		                     CLIP<int>(_mousePos.y, _screenR.top, _screenR.bottom - 1));
+	}
+
+	return Common::Point(_centerX, _centerY);
+}
+
 void ColonyEngine::handleMenuAction(int action) {
 	switch (action) {
 	case kMenuActionAbout:
 		inform("The Colony\nCopyright 1988\nDavid A. Smith", true);
 		break;
 	case kMenuActionNew:
-		loadMap(1);
+		startNewGame();
 		break;
 	case kMenuActionOpen:
 		_system->lockMouse(false);
+		_system->showMouse(true);
+		CursorMan.setDefaultArrowCursor();
+		CursorMan.showMouse(true);
 		loadGameDialog();
-		_system->lockMouse(true);
-		_system->warpMouse(_centerX, _centerY);
-		_system->getEventManager()->purgeMouseEvents();
+		updateMouseCapture(true);
 		break;
 	case kMenuActionSave:
 	case kMenuActionSaveAs:
 		_system->lockMouse(false);
+		_system->showMouse(true);
+		CursorMan.setDefaultArrowCursor();
+		CursorMan.showMouse(true);
 		saveGameDialog();
-		_system->lockMouse(true);
-		_system->warpMouse(_centerX, _centerY);
-		_system->getEventManager()->purgeMouseEvents();
+		updateMouseCapture(true);
 		break;
 	case kMenuActionQuit:
 		quitGame();
 		break;
 	case kMenuActionSound:
-		// Sound toggle (TODO: implement sound on/off state)
+		_soundOn = !_soundOn;
+		if (!_soundOn)
+			_sound->stop();
+		syncMacMenuChecks();
 		break;
 	case kMenuActionCrosshair:
 		_crosshair = !_crosshair;
+		syncMacMenuChecks();
 		break;
 	case kMenuActionPolyFill:
 		_wireframe = !_wireframe;
+		syncMacMenuChecks();
 		break;
 	case kMenuActionCursorShoot:
-		// Toggle cursor-based shooting (not yet implemented)
+		_cursorShoot = !_cursorShoot;
+		if (_cursorShoot && _mouseLocked) {
+			_mouseLocked = false;
+			updateMouseCapture(false);
+		}
+		syncMacMenuChecks();
 		break;
 	default:
 		break;
@@ -347,6 +401,7 @@ void ColonyEngine::initMacMenus() {
 	_macMenu->addMenuItem(_macMenu->getSubmenu(nullptr, 0), "About The Colony", kMenuActionAbout);
 
 	_macMenu->calcDimensions();
+	syncMacMenuChecks();
 
 	_menuBarHeight = 20;
 }
@@ -360,6 +415,88 @@ void ColonyEngine::initTrig() {
 	}
 }
 
+void ColonyEngine::startNewGame() {
+	deleteAnimation();
+	_sound->stop();
+
+	memset(_wall, 0, sizeof(_wall));
+	memset(_mapData, 0, sizeof(_mapData));
+	memset(_robotArray, 0, sizeof(_robotArray));
+	memset(_foodArray, 0, sizeof(_foodArray));
+	memset(_dirXY, 0, sizeof(_dirXY));
+	_objects.clear();
+	_robotNum = 0;
+	_dynamicObjectBase = 0;
+
+	memset(_levelData, 0, sizeof(_levelData));
+	_levelData[1].count = 1;
+	memset(_carryPatch, 0, sizeof(_carryPatch));
+	_patches.clear();
+	_carryType = 0;
+	_fl = 0;
+	_level = 0;
+	_gameMode = kModeColony;
+	_hasKeycard = false;
+	_unlocked = false;
+	_weapons = 0;
+	_armor = 0;
+	_orbit = 0;
+	_allGrow = false;
+	_suppressCollisionSound = false;
+	_action0 = 0;
+	_action1 = 0;
+	_creature = 0;
+
+	_randomSource.setSeed(Common::RandomSource::generateNewSeed());
+
+	memset(&_me, 0, sizeof(_me));
+	_me.xloc = 4400;
+	_me.yloc = 4400;
+	_me.xindex = _me.xloc >> 8;
+	_me.yindex = _me.yloc >> 8;
+	_me.look = 32;
+	_me.ang = 32;
+	_me.type = kMeNum;
+	_me.power[0] = 256;
+	_me.power[1] = 256;
+	_me.power[2] = 256;
+
+	for (int i = 0; i < 4; i++)
+		_decode1[i] = _decode2[i] = _decode3[i] = 0;
+	for (int i = 0; i < 6; i++)
+		_animDisplay[i] = 1;
+	_coreState[0] = _coreState[1] = 0;
+	_coreHeight[0] = _coreHeight[1] = 256;
+	_corePower[0] = 0;
+	_corePower[1] = 2;
+	_corePower[2] = 0;
+	_coreIndex = 0;
+	_animationName.clear();
+	_backgroundActive = false;
+	_animationRunning = false;
+	_animationResult = 0;
+	_doorOpen = false;
+	_elevatorFloor = 0;
+	_airlockX = -1;
+	_airlockY = -1;
+	_airlockDirection = -1;
+	_airlockTerminate = false;
+	_lastClickTime = 0;
+	_displayCount = 0;
+	_lastAnimUpdate = 0;
+	_lastWarningChimeTime = 0;
+	_battleRound = 0;
+	_battleMaxP = 0;
+	_projon = false;
+	_pcount = 0;
+
+	battleInit();
+	updateViewportLayout();
+	loadMap(1);
+	battleSet();
+	_mousePos = Common::Point(_centerX, _centerY);
+}
+
 Common::Error ColonyEngine::run() {
 	// Open Colony resource fork (must happen in run(), not constructor,
 	// because SearchMan doesn't have the game path until now)
@@ -439,8 +576,7 @@ Common::Error ColonyEngine::run() {
 
 	loadMap(1); // Try to load the first map
 	_mouseLocked = true;
-	_system->lockMouse(true);
-	_system->warpMouse(_centerX, _centerY);
+	updateMouseCapture(true);
 
 	int mouseDX = 0, mouseDY = 0;
 	bool mouseMoved = false;
@@ -488,14 +624,15 @@ Common::Error ColonyEngine::run() {
 					// WM consumed the event (menu interaction)
 					if (!wasMenuActive && _wm->isMenuActive()) {
 						_system->lockMouse(false);
+						_system->showMouse(true);
+						CursorMan.setDefaultArrowCursor();
+						CursorMan.showMouse(true);
 					}
 					continue;
 				}
 				if (wasMenuActive && !_wm->isMenuActive()) {
-					_system->lockMouse(_mouseLocked);
+					updateMouseCapture(true);
 					if (_mouseLocked) {
-						_system->warpMouse(_centerX, _centerY);
-						_system->getEventManager()->purgeMouseEvents();
 						mouseDX = mouseDY = 0;
 						mouseMoved = false;
 					}
@@ -552,10 +689,8 @@ Common::Error ColonyEngine::run() {
 						exitForklift();
 					else {
 						_mouseLocked = !_mouseLocked;
-						_system->lockMouse(_mouseLocked);
+						updateMouseCapture(true);
 						if (_mouseLocked) {
-							_system->warpMouse(_centerX, _centerY);
-							_system->getEventManager()->purgeMouseEvents();
 							mouseDX = mouseDY = 0;
 							mouseMoved = false;
 						}
@@ -566,13 +701,12 @@ Common::Error ColonyEngine::run() {
 					break;
 				case kActionEscape:
 					_system->lockMouse(false);
+					_system->showMouse(true);
+					CursorMan.setDefaultArrowCursor();
+					CursorMan.showMouse(true);
 					openMainMenuDialog();
 					_gfx->computeScreenViewport();
-					_system->lockMouse(_mouseLocked);
-					if (_mouseLocked) {
-						_system->warpMouse(_centerX, _centerY);
-						_system->getEventManager()->purgeMouseEvents();
-					}
+					updateMouseCapture(true);
 					break;
 				default:
 					break;
@@ -614,13 +748,15 @@ Common::Error ColonyEngine::run() {
 				default:
 					break;
 				}
-			} else if (event.type == Common::EVENT_LBUTTONDOWN && _mouseLocked) {
-				// Left click = fire when mouselook is active
+			} else if (event.type == Common::EVENT_LBUTTONDOWN && (_mouseLocked || _cursorShoot)) {
 				cShoot();
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
-				mouseDX += event.relMouse.x;
-				mouseDY += event.relMouse.y;
-				mouseMoved = true;
+				_mousePos = event.mouse;
+				if (_mouseLocked) {
+					mouseDX += event.relMouse.x;
+					mouseDY += event.relMouse.y;
+					mouseMoved = true;
+				}
 			} else if (event.type == Common::EVENT_SCREEN_CHANGED) {
 				_gfx->computeScreenViewport();
 			}
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 9d6a363fa31..4820674e3e3 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -364,10 +364,12 @@ public:
 	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
 	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
 	Common::Platform getPlatform() const { return _gameDescription->platform; }
+	bool isSoundEnabled() const { return _soundOn; }
 
 	void initTrig();
 	void loadMacColors();
 	void loadMap(int mnum);
+	void startNewGame();
 	void corridor();
 	void quadrant();
 	int checkwall(int xnew, int ynew, Locate *pobject);
@@ -412,8 +414,10 @@ private:
 	int _width, _height;
 	int _mouseSensitivity;
 	bool _mouseLocked;
+	bool _soundOn = true;
 	bool _showDashBoard;
 	bool _crosshair;
+	bool _cursorShoot = false;
 	bool _insight;
 	bool _hasKeycard;
 	bool _unlocked;
@@ -432,6 +436,7 @@ private:
 	bool _rotateRight;
 
 	Common::RandomSource _randomSource;
+	Common::Point _mousePos;
 	uint8 _decode1[4];
 	uint8 _decode2[4];
 	uint8 _decode3[4];
@@ -524,11 +529,11 @@ public:
 	};
 
 private:
-	void draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride = -1);
+	void draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride = -1, bool accumulateBounds = false);
 	void draw3DLeaf(const Thing &obj, const PrismPartDef &def);
-	void draw3DSphere(const Thing &obj, int pt0x, int pt0y, int pt0z,
-	                  int pt1x, int pt1y, int pt1z, uint32 fillColor, uint32 outlineColor);
-	bool drawStaticObjectPrisms3D(const Thing &obj);
+	void draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
+	                  int pt1x, int pt1y, int pt1z, uint32 fillColor, uint32 outlineColor, bool accumulateBounds = false);
+	bool drawStaticObjectPrisms3D(Thing &obj);
 	void initRobots();
 	void renderCorridor3D();
 	void drawWallFeatures3D();
@@ -574,6 +579,9 @@ private:
 	int openAdjacentDoors(int x, int y);
 	int goToDestination(const uint8 *map, Locate *pobject);
 	int tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject);
+	void syncMacMenuChecks();
+	void updateMouseCapture(bool recenter = true);
+	Common::Point getAimPoint() const;
 	void updateViewportLayout();
 	void drawDashboardStep1();
 	void drawDashboardMac();
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 4f359608aa8..d15cf5c5d2b 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -289,8 +289,8 @@ void ColonyEngine::setPower(int p0, int p1, int p2) {
 	}
 }
 
-// shoot.c CShoot(): player fires weapon at screen center.
-// Traces a ray in the facing direction to find the first robot hit.
+// shoot.c CShoot(): player fires weapon at the cursor or screen center.
+// Original hit detection uses the rendered object bounds on screen.
 void ColonyEngine::cShoot() {
 	if (_gameMode == kModeBattle) {
 		battleShoot();
@@ -306,8 +306,9 @@ void ColonyEngine::cShoot() {
 	setPower(-(1 << _level), 0, 0);
 
 	// Draw crosshair flash via XOR lines on the 3D viewport
-	int cx = _screenR.left + _screenR.width() / 2;
-	int cy = _screenR.top + _screenR.height() / 2;
+	const Common::Point aim = getAimPoint();
+	const int cx = aim.x;
+	const int cy = aim.y;
 	_gfx->setXorMode(true);
 	for (int r = 4; r <= 20; r += 4) {
 		_gfx->drawLine(cx - r, cy - r, cx + r, cy - r, 0xFFFFFF);
@@ -327,84 +328,45 @@ void ColonyEngine::cShoot() {
 	}
 	_gfx->setXorMode(false);
 
-	// Hit detection: find the closest visible robot in the player's aim direction.
-	// For each visible robot, compute the angle from the player to the robot and
-	// compare with the player's look direction. Pick the nearest matching robot.
 	int bestIdx = -1;
 	int bestDist = INT_MAX;
+	bool bestIsBlocker = false;
 
 	for (uint i = 0; i < _objects.size(); i++) {
 		const Thing &obj = _objects[i];
 		if (!obj.alive)
 			continue;
+
+		if (obj.where.xmn > obj.where.xmx || obj.where.zmn > obj.where.zmx)
+			continue;
+		if (obj.where.xmn > cx || obj.where.xmx < cx ||
+		    obj.where.zmn > cy || obj.where.zmx < cy)
+			continue;
+
 		int t = obj.type;
-		// Skip non-shootable types: only robots and boss types are valid targets
 		bool isRobot = (t >= kRobEye && t <= kRobUPyramid) ||
 			t == kRobQueen || t == kRobDrone || t == kRobSoldier;
-		if (!isRobot)
+		bool blocksShot = (t == kObjForkLift || t == kObjTeleport || t == kObjPToilet ||
+			t == kObjBox2 || t == kObjReactor || t == kObjScreen);
+		if (!isRobot && !blocksShot)
 			continue;
 
-		int ox = obj.where.xindex;
-		int oy = obj.where.yindex;
-		if (ox < 0 || ox >= 31 || oy < 0 || oy >= 31 || !_visibleCell[ox][oy])
-			continue;
-
-		// Compute angle from player to robot (256-unit circle matching look direction)
 		int dx = obj.where.xloc - _me.xloc;
 		int dy = obj.where.yloc - _me.yloc;
 		int dist = (int)sqrtf((float)(dx * dx + dy * dy));
-		if (dist < 64)
-			continue; // too close (same cell)
-
-		// atan2 → 256-unit angle
-		float rad = atan2f((float)dy, (float)dx);
-		int angleToRobot = (int)(rad * 128.0f / (float)M_PI) & 0xFF;
-		int angleDiff = (int8)((uint8)angleToRobot - _me.look);
-
-		// Angular tolerance scales with distance: closer = wider cone
-		int threshold = CLIP(3000 / MAX(dist, 1), 2, 16);
-		if (abs(angleDiff) > threshold)
-			continue;
 
 		if (dist < bestDist) {
 			bestDist = dist;
 			bestIdx = (int)i + 1; // 1-based robot index
+			bestIsBlocker = blocksShot;
 		}
 	}
 
-	if (bestIdx > 0) {
-		// Check that the shot isn't blocked by a large object closer than the target
+	if (bestIdx > 0 && !bestIsBlocker) {
 		const Thing &target = _objects[bestIdx - 1];
-		bool blocked = false;
-		for (uint i = 0; i < _objects.size(); i++) {
-			const Thing &obj = _objects[i];
-			if (!obj.alive || (int)i + 1 == bestIdx)
-				continue;
-			int t = obj.type;
-			// These objects block shots
-			if (t == kObjForkLift || t == kObjTeleport || t == kObjPToilet ||
-				t == kObjBox2 || t == kObjReactor || t == kObjScreen) {
-				int dx = obj.where.xloc - _me.xloc;
-				int dy = obj.where.yloc - _me.yloc;
-				int objDist = (int)sqrtf((float)(dx * dx + dy * dy));
-				if (objDist < bestDist) {
-					float rad = atan2f((float)dy, (float)dx);
-					int angleToObj = (int)(rad * 128.0f / (float)M_PI) & 0xFF;
-					int angleDiff = (int8)((uint8)angleToObj - _me.look);
-					int threshold = CLIP(3000 / MAX(objDist, 1), 2, 16);
-					if (abs(angleDiff) <= threshold) {
-						blocked = true;
-						break;
-					}
-				}
-			}
-		}
-
-		if (!blocked) {
-			debugC(1, kColonyDebugAnimation, "CShoot: hit robot %d (type=%d, dist=%d)",
-				bestIdx, target.type, bestDist);
-			destroyRobot(bestIdx);
-		}
+		debugC(1, kColonyDebugAnimation, "CShoot: hit robot %d (type=%d, dist=%d)",
+			bestIdx, target.type, bestDist);
+		destroyRobot(bestIdx);
 	}
 }
 
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 97da8abbd88..e1b5155e521 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -24,10 +24,12 @@
 #include "common/system.h"
 #include "common/events.h"
 #include "common/debug.h"
+#include "common/translation.h"
 #include "graphics/font.h"
 #include "graphics/fonts/dosfont.h"
 #include "graphics/fonts/macfont.h"
 #include "graphics/cursorman.h"
+#include "gui/message.h"
 #include "image/pict.h"
 #include <math.h>
 
@@ -676,12 +678,14 @@ void ColonyEngine::terminateGame(bool blowup) {
 	Common::Rect savedClip = _clip;
 	int savedCenterX = _centerX;
 	int savedCenterY = _centerY;
+	const bool savedMouseLocked = _mouseLocked;
 
 	_screenR = Common::Rect(0, 0, _width, _height);
 	_clip = _screenR;
 	_centerX = _width / 2;
 	_centerY = _height / 2;
 
+	_animationRunning = false;
 	_mouseLocked = false;
 	_system->lockMouse(false);
 	_system->showMouse(true);
@@ -726,8 +730,31 @@ void ColonyEngine::terminateGame(bool blowup) {
 	_clip = savedClip;
 	_centerX = savedCenterX;
 	_centerY = savedCenterY;
-
-	_system->quit();
+	while (!shouldQuit()) {
+		Common::U32StringArray altButtons;
+		altButtons.push_back(_("Load Game"));
+		altButtons.push_back(_("Quit"));
+		GUI::MessageDialog prompt(_("You have been terminated."), _("New Game"), altButtons);
+
+		switch (runDialog(prompt)) {
+		case GUI::kMessageOK:
+			startNewGame();
+			_mouseLocked = savedMouseLocked;
+			updateMouseCapture(true);
+			return;
+		case GUI::kMessageAlt:
+			if (loadGameDialog()) {
+				_mouseLocked = savedMouseLocked;
+				updateMouseCapture(true);
+				return;
+			}
+			break;
+		case GUI::kMessageAlt + 1:
+		default:
+			quitGame();
+			return;
+		}
+	}
 }
 
 int ColonyEngine::countSavedCryos() const {
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index cfc2d85021c..1a57ca55dd1 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -1319,7 +1319,46 @@ const uint8 *ColonyEngine::mapFeatureAt(int x, int y, int direction) const {
 	return _mapData[x][y][direction];
 }
 
-void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride) {
+static bool projectCorridorPoint(const Common::Rect &screenR, uint8 look, int8 lookY,
+                                 const int *sint, const int *cost, int camX, int camY,
+                                 float worldX, float worldY, float worldZ,
+                                 int &screenX, int &screenY) {
+	const float dx = worldX - camX;
+	const float dy = worldY - camY;
+	const float dz = worldZ;
+
+	const float sinYaw = sint[look] / 128.0f;
+	const float cosYaw = cost[look] / 128.0f;
+	const float side = dx * sinYaw - dy * cosYaw;
+	const float forward = dx * cosYaw + dy * sinYaw;
+
+	const float pitchRad = lookY * 2.0f * (float)M_PI / 256.0f;
+	const float sinPitch = sinf(pitchRad);
+	const float cosPitch = cosf(pitchRad);
+
+	const float eyeX = side;
+	const float eyeY = dz * cosPitch + forward * sinPitch;
+	const float eyeZ = dz * sinPitch - forward * cosPitch;
+	if (eyeZ >= -1.0f)
+		return false;
+
+	const float focal = (screenR.height() * 0.5f) / tanf(75.0f * (float)M_PI / 360.0f);
+	const float centerX = screenR.left + screenR.width() * 0.5f;
+	const float centerY = screenR.top + screenR.height() * 0.5f;
+
+	screenX = (int)roundf(centerX + (eyeX * focal / -eyeZ));
+	screenY = (int)roundf(centerY - (eyeY * focal / -eyeZ));
+	return true;
+}
+
+static void resetObjectBounds(const Common::Rect &screenR, Locate &loc) {
+	loc.xmn = screenR.right;
+	loc.xmx = screenR.left;
+	loc.zmn = screenR.bottom;
+	loc.zmx = screenR.top;
+}
+
+void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride, bool accumulateBounds) {
 	// +32 compensates for the original sine table's 45° phase offset.
 	// Object angles from game data were stored assuming that offset.
 	const uint8 ang = (useLook ? obj.where.look : obj.where.ang) + 32;
@@ -1354,6 +1393,17 @@ void ColonyEngine::draw3DPrism(const Thing &obj, const PrismPartDef &def, bool u
 			px[count] = (float)(rx + obj.where.xloc);
 			py[count] = (float)(ry + obj.where.yloc);
 			pz[count] = (float)(oz - 160); // Shift from floor-relative (z=0) to world (z=-160)
+			if (accumulateBounds) {
+				int sx = 0;
+				int sy = 0;
+				if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+				                         px[count], py[count], pz[count], sx, sy)) {
+					obj.where.xmn = MIN(obj.where.xmn, sx);
+					obj.where.xmx = MAX(obj.where.xmx, sx);
+					obj.where.zmn = MIN(obj.where.zmn, sy);
+					obj.where.zmx = MAX(obj.where.zmx, sy);
+				}
+			}
 			count++;
 		}
 
@@ -1506,9 +1556,9 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 	}
 }
 
-void ColonyEngine::draw3DSphere(const Thing &obj, int pt0x, int pt0y, int pt0z,
+void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
                                 int pt1x, int pt1y, int pt1z,
-                                uint32 fillColor, uint32 outlineColor) {
+                                uint32 fillColor, uint32 outlineColor, bool accumulateBounds) {
 	// Sphere defined by two object-local points: pt0 (center bottom) and pt1 (center top).
 	// Center is midpoint, radius is half the distance (along z typically).
 	// Rendered as a billboard polygon facing the camera.
@@ -1563,6 +1613,20 @@ void ColonyEngine::draw3DSphere(const Thing &obj, int pt0x, int pt0y, int pt0z,
 		pz[i] = cz + radius * (sinA * upZ);
 	}
 
+	if (accumulateBounds) {
+		for (int i = 0; i < N; i++) {
+			int sx = 0;
+			int sy = 0;
+			if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+			                         px[i], py[i], pz[i], sx, sy)) {
+				obj.where.xmn = MIN(obj.where.xmn, sx);
+				obj.where.xmx = MAX(obj.where.xmx, sx);
+				obj.where.zmn = MIN(obj.where.zmn, sy);
+				obj.where.zmx = MAX(obj.where.zmx, sy);
+			}
+		}
+	}
+
 	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
 		// Mac color: map fillColor to Mac color index and use RGB
 		// fillColor is an ObjColor enum value passed by the caller
@@ -1687,9 +1751,10 @@ void ColonyEngine::computeVisibleCells() {
 
 void ColonyEngine::drawStaticObjects() {
 	for (uint i = 0; i < _objects.size(); i++) {
-		const Thing &obj = _objects[i];
+		Thing &obj = _objects[i];
 		if (!obj.alive)
 			continue;
+		resetObjectBounds(_screenR, obj.where);
 		int ox = obj.where.xindex;
 		int oy = obj.where.yindex;
 		if (ox < 0 || ox >= 32 || oy < 0 || oy >= 32 || !_visibleCell[ox][oy])
@@ -2722,67 +2787,75 @@ void ColonyEngine::renderCorridor3D() {
 	_gfx->setWireframe(false);
 }
 
-bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
+bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
+	const auto drawPrism = [&](const PrismPartDef &def, bool useLook, int colorOverride = -1) {
+		draw3DPrism(obj, def, useLook, colorOverride, true);
+	};
+	const auto drawSphere = [&](int pt0x, int pt0y, int pt0z, int pt1x, int pt1y, int pt1z,
+	                            uint32 fillColor, uint32 outlineColor) {
+		draw3DSphere(obj, pt0x, pt0y, pt0z, pt1x, pt1y, pt1z, fillColor, outlineColor, true);
+	};
+
 	switch (obj.type) {
 	case kObjConsole:
-		draw3DPrism(obj, kConsolePart, false);
+		drawPrism(kConsolePart, false);
 		break;
 	case kObjCChair:
 		for (int i = 0; i < 5; i++) {
 			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kCChairParts[i], false);
+			drawPrism(kCChairParts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjPlant:
 		// DOS MakePlant draw order: top pot, then green leaf lines, then pot on top.
-		draw3DPrism(obj, kPlantParts[1], false); // top pot (soil)
+		drawPrism(kPlantParts[1], false); // top pot (soil)
 		for (int i = 2; i < 8; i++)
 			draw3DLeaf(obj, kPlantParts[i]); // leaves as lines
-		draw3DPrism(obj, kPlantParts[0], false); // pot (drawn last, on top)
+		drawPrism(kPlantParts[0], false); // pot (drawn last, on top)
 		break;
 	case kObjCouch:
 	case kObjChair: {
 		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
 		for (int i = 0; i < 4; i++) {
 			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
-			draw3DPrism(obj, parts[i], false);
+			drawPrism(parts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	}
 	case kObjTV:
 		_gfx->setDepthRange(0.002, 1.0); // body base layer (pushed back)
-		draw3DPrism(obj, kTVParts[0], false);
+		drawPrism(kTVParts[0], false);
 		_gfx->setDepthRange(0.0, 1.0);   // screen on top of body face
-		draw3DPrism(obj, kTVParts[1], false);
+		drawPrism(kTVParts[1], false);
 		break;
 	case kObjDrawer:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kDrawerParts[i], false);
+			drawPrism(kDrawerParts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjFWall:
 		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
-			draw3DPrism(obj, kFWallPart, false, kColorCorridorWall);
+			drawPrism(kFWallPart, false, kColorCorridorWall);
 		else
-			draw3DPrism(obj, kFWallPart, false);
+			drawPrism(kFWallPart, false);
 		break;
 	case kObjCWall:
 		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
-			draw3DPrism(obj, kCWallPart, false, kColorCorridorWall);
+			drawPrism(kCWallPart, false, kColorCorridorWall);
 		else
-			draw3DPrism(obj, kCWallPart, false);
+			drawPrism(kCWallPart, false);
 		break;
 	case kObjScreen:
-		draw3DPrism(obj, kScreenPart, false);
+		drawPrism(kScreenPart, false);
 		break;
 	case kObjTable:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kTableParts[i], false);
+			drawPrism(kTableParts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
@@ -2791,7 +2864,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
 		for (int i = 0; i < 3; i++) {
 			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
-			draw3DPrism(obj, parts[i], false);
+			drawPrism(parts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
@@ -2799,28 +2872,28 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 	case kObjDesk:
 		for (int i = 0; i < 10; i++) {
 			_gfx->setDepthRange((9 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kDeskParts[i], false);
+			drawPrism(kDeskParts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjBox1:
-		draw3DPrism(obj, kBox1Part, false);
+		drawPrism(kBox1Part, false);
 		break;
 	case kObjBench:
-		draw3DPrism(obj, kBenchPart, false);
+		drawPrism(kBenchPart, false);
 		break;
 	case kObjCBench:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kCBenchParts[i], false);
+			drawPrism(kCBenchParts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjBox2:
 		_gfx->setDepthRange(0.002, 1.0);
-		draw3DPrism(obj, kBox2Parts[1], false); // base first
+		drawPrism(kBox2Parts[1], false); // base first
 		_gfx->setDepthRange(0.0, 1.0);
-		draw3DPrism(obj, kBox2Parts[0], false); // top second
+		drawPrism(kBox2Parts[0], false); // top second
 		break;
 	case kObjReactor: {
 		// MakeReactor: animate core height and recolor only the original
@@ -2883,12 +2956,12 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 		// Depth separation matches the original draw order closely, but the
 		// per-face colors now follow MakeReactor() exactly.
 		_gfx->setDepthRange(0.004, 1.0);
-		draw3DPrism(obj, modTopDef, false);
+		drawPrism(modTopDef, false);
 		_gfx->setDepthRange(0.002, 1.0);
-		draw3DPrism(obj, modRingDef, false);
+		drawPrism(modRingDef, false);
 		if (_coreState[_coreIndex] < 2) {
 			_gfx->setDepthRange(0.0, 1.0);
-			draw3DPrism(obj, modCoreDef, false);
+			drawPrism(modCoreDef, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
@@ -2901,159 +2974,159 @@ bool ColonyEngine::drawStaticObjectPrisms3D(const Thing &obj) {
 
 		for (int i = 0; i < 4; i++) {
 			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kPowerSuitParts[i], false);
+			drawPrism(kPowerSuitParts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
-		draw3DPrism(obj, kPowerSuitParts[4], false, sourceColor);
+		drawPrism(kPowerSuitParts[4], false, sourceColor);
 		break;
 	}
 	case kObjTeleport:
-		draw3DPrism(obj, kTelePart, false);
+		drawPrism(kTelePart, false);
 		break;
 	case kObjCryo:
 		_gfx->setDepthRange(0.002, 1.0);
-		draw3DPrism(obj, kCryoParts[1], false); // base first
+		drawPrism(kCryoParts[1], false); // base first
 		_gfx->setDepthRange(0.0, 1.0);
-		draw3DPrism(obj, kCryoParts[0], false); // top second
+		drawPrism(kCryoParts[0], false); // top second
 		break;
 	case kObjProjector:
 		// Projector sits on table  draw table first, then projector parts
 		_gfx->setDepthRange(0.008, 1.0);
-		draw3DPrism(obj, kTableParts[0], false); // table base
+		drawPrism(kTableParts[0], false); // table base
 		_gfx->setDepthRange(0.006, 1.0);
-		draw3DPrism(obj, kTableParts[1], false); // table top
+		drawPrism(kTableParts[1], false); // table top
 		_gfx->setDepthRange(0.004, 1.0);
-		draw3DPrism(obj, kProjectorParts[1], false); // stand
+		drawPrism(kProjectorParts[1], false); // stand
 		_gfx->setDepthRange(0.002, 1.0);
-		draw3DPrism(obj, kProjectorParts[0], false); // body
+		drawPrism(kProjectorParts[0], false); // body
 		_gfx->setDepthRange(0.0, 1.0);
-		draw3DPrism(obj, kProjectorParts[2], false); // lens
+		drawPrism(kProjectorParts[2], false); // lens
 		break;
 	case kObjTub:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kTubParts[i], false);
+			drawPrism(kTubParts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjSink:
 		for (int i = 0; i < 3; i++) {
 			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kSinkParts[i], false);
+			drawPrism(kSinkParts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjToilet:
 		for (int i = 0; i < 4; i++) {
 			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kToiletParts[i], false);
+			drawPrism(kToiletParts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjPToilet:
 		for (int i = 0; i < 5; i++) {
 			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kPToiletParts[i], false);
+			drawPrism(kPToiletParts[i], false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjForkLift:
 		// Draw order: forks, arms, treads, cab (back-to-front)
 		_gfx->setDepthRange(0.010, 1.0);
-		draw3DPrism(obj, kForkliftParts[3], false); // FLLL (left fork)
+		drawPrism(kForkliftParts[3], false); // FLLL (left fork)
 		_gfx->setDepthRange(0.008, 1.0);
-		draw3DPrism(obj, kForkliftParts[2], false); // FLUL (left arm)
+		drawPrism(kForkliftParts[2], false); // FLUL (left arm)
 		_gfx->setDepthRange(0.006, 1.0);
-		draw3DPrism(obj, kForkliftParts[5], false); // FLLR (right fork)
+		drawPrism(kForkliftParts[5], false); // FLLR (right fork)
 		_gfx->setDepthRange(0.004, 1.0);
-		draw3DPrism(obj, kForkliftParts[4], false); // FLUR (right arm)
+		drawPrism(kForkliftParts[4], false); // FLUR (right arm)
 		_gfx->setDepthRange(0.002, 1.0);
-		draw3DPrism(obj, kForkliftParts[1], false); // treads
+		drawPrism(kForkliftParts[1], false); // treads
 		_gfx->setDepthRange(0.0, 1.0);
-		draw3DPrism(obj, kForkliftParts[0], false); // cab
+		drawPrism(kForkliftParts[0], false); // cab
 		break;
 	// === Robot types (1-20) ===
 	case kRobEye:
-		draw3DSphere(obj, 0, 0, 100, 0, 0, 200, 15, 15); // ball: white
-		draw3DPrism(obj, kEyeIrisDef, false);
-		draw3DPrism(obj, kEyePupilDef, false);
+		drawSphere(0, 0, 100, 0, 0, 200, 15, 15); // ball: white
+		drawPrism(kEyeIrisDef, false);
+		drawPrism(kEyePupilDef, false);
 		break;
 	case kRobPyramid:
-		draw3DPrism(obj, kPShadowDef, false);
-		draw3DPrism(obj, kPyramidBodyDef, false);
-		draw3DSphere(obj, 0, 0, 175, 0, 0, 200, 15, 15); // ball on top
-		draw3DPrism(obj, kPIrisDef, false);
-		draw3DPrism(obj, kPPupilDef, false);
+		drawPrism(kPShadowDef, false);
+		drawPrism(kPyramidBodyDef, false);
+		drawSphere(0, 0, 175, 0, 0, 200, 15, 15); // ball on top
+		drawPrism(kPIrisDef, false);
+		drawPrism(kPPupilDef, false);
 		break;
 	case kRobCube:
-		draw3DPrism(obj, kCubeBodyDef, false);
+		drawPrism(kCubeBodyDef, false);
 		break;
 	case kRobUPyramid:
-		draw3DPrism(obj, kUPShadowDef, false);
-		draw3DPrism(obj, kUPyramidBodyDef, false);
+		drawPrism(kUPShadowDef, false);
+		drawPrism(kUPyramidBodyDef, false);
 		break;
 	case kRobFEye:
-		draw3DSphere(obj, 0, 0, 0, 0, 0, 100, 15, 15);
-		draw3DPrism(obj, kFEyeIrisDef, false);
-		draw3DPrism(obj, kFEyePupilDef, false);
+		drawSphere(0, 0, 0, 0, 0, 100, 15, 15);
+		drawPrism(kFEyeIrisDef, false);
+		drawPrism(kFEyePupilDef, false);
 		break;
 	case kRobFPyramid:
-		draw3DPrism(obj, kFPyramidBodyDef, false);
+		drawPrism(kFPyramidBodyDef, false);
 		break;
 	case kRobFCube:
-		draw3DPrism(obj, kFCubeBodyDef, false);
+		drawPrism(kFCubeBodyDef, false);
 		break;
 	case kRobFUPyramid:
-		draw3DPrism(obj, kFUPyramidBodyDef, false);
+		drawPrism(kFUPyramidBodyDef, false);
 		break;
 	case kRobSEye:
-		draw3DSphere(obj, 0, 0, 0, 0, 0, 50, 15, 15);
-		draw3DPrism(obj, kSEyeIrisDef, false);
-		draw3DPrism(obj, kSEyePupilDef, false);
+		drawSphere(0, 0, 0, 0, 0, 50, 15, 15);
+		drawPrism(kSEyeIrisDef, false);
+		drawPrism(kSEyePupilDef, false);
 		break;
 	case kRobSPyramid:
-		draw3DPrism(obj, kSPyramidBodyDef, false);
+		drawPrism(kSPyramidBodyDef, false);
 		break;
 	case kRobSCube:
-		draw3DPrism(obj, kSCubeBodyDef, false);
+		drawPrism(kSCubeBodyDef, false);
 		break;
 	case kRobSUPyramid:
-		draw3DPrism(obj, kSUPyramidBodyDef, false);
+		drawPrism(kSUPyramidBodyDef, false);
 		break;
 	case kRobMEye:
-		draw3DSphere(obj, 0, 0, 0, 0, 0, 25, 15, 15);
-		draw3DPrism(obj, kMEyeIrisDef, false);
-		draw3DPrism(obj, kMEyePupilDef, false);
+		drawSphere(0, 0, 0, 0, 0, 25, 15, 15);
+		drawPrism(kMEyeIrisDef, false);
+		drawPrism(kMEyePupilDef, false);
 		break;
 	case kRobMPyramid:
-		draw3DPrism(obj, kMPyramidBodyDef, false);
+		drawPrism(kMPyramidBodyDef, false);
 		break;
 	case kRobMCube:
-		draw3DPrism(obj, kMCubeBodyDef, false);
+		drawPrism(kMCubeBodyDef, false);
 		break;
 	case kRobMUPyramid:
-		draw3DPrism(obj, kMUPyramidBodyDef, false);
+		drawPrism(kMUPyramidBodyDef, false);
 		break;
 	case kRobQueen:
-		draw3DPrism(obj, kQThoraxDef, false);
-		draw3DPrism(obj, kQAbdomenDef, false);
-		draw3DPrism(obj, kQLWingDef, false);
-		draw3DPrism(obj, kQRWingDef, false);
-		draw3DSphere(obj, 0, 0, 130, 0, 0, 155, 15, 15); // queen ball
-		draw3DPrism(obj, kQIrisDef, false);
-		draw3DPrism(obj, kQPupilDef, false);
+		drawPrism(kQThoraxDef, false);
+		drawPrism(kQAbdomenDef, false);
+		drawPrism(kQLWingDef, false);
+		drawPrism(kQRWingDef, false);
+		drawSphere(0, 0, 130, 0, 0, 155, 15, 15); // queen ball
+		drawPrism(kQIrisDef, false);
+		drawPrism(kQPupilDef, false);
 		break;
 	case kRobDrone:
 	case kRobSoldier:
-		draw3DPrism(obj, kDAbdomenDef, false);
-		draw3DPrism(obj, kDLLPincerDef, false);
-		draw3DPrism(obj, kDRRPincerDef, false);
-		draw3DPrism(obj, kDLEyeDef, false);
-		draw3DPrism(obj, kDREyeDef, false);
+		drawPrism(kDAbdomenDef, false);
+		drawPrism(kDLLPincerDef, false);
+		drawPrism(kDRRPincerDef, false);
+		drawPrism(kDLEyeDef, false);
+		drawPrism(kDREyeDef, false);
 		break;
 	case kRobSnoop:
-		draw3DPrism(obj, kSnoopAbdomenDef, false);
-		draw3DPrism(obj, kSnoopHeadDef, false);
+		drawPrism(kSnoopAbdomenDef, false);
+		drawPrism(kSnoopHeadDef, false);
 		break;
 	default:
 		return false;
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index 102f7f0644b..4a7ec18dcc9 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -72,6 +72,9 @@ bool Sound::isPlaying() const {
 void Sound::play(int soundID) {
 	stop();
 
+	if (!_vm->isSoundEnabled())
+		return;
+
 	if (_vm->getPlatform() == Common::kPlatformMacintosh)
 		playMacSound(soundID);
 	else
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 8947fd2fc9e..0605ddd10bc 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -553,6 +553,9 @@ void ColonyEngine::drawCrosshair() {
 		return;
 
 	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	if (isMac && _cursorShoot && !_mouseLocked && _weapons > 0)
+		return;
+
 	uint32 color;
 	if (isMac) {
 		// Mac: black when powered, gray when no weapons, white when armed but no power


Commit: 69e4b56217fff19d0df693f4e19f650a1791fa6a
    https://github.com/scummvm/scummvm/commit/69e4b56217fff19d0df693f4e19f650a1791fa6a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:38+02:00

Commit Message:
COLONY: fix ASAN crash

Changed paths:
    engines/colony/think.cpp


diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index c775aa9bbb2..fe473953b4a 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -157,45 +157,77 @@ void ColonyEngine::cThink() {
 }
 
 void ColonyEngine::cubeThink(int num) {
-	Thing &obj = _objects[num - 1];
-	obj.where.look = (uint8)(obj.where.look + 15);
+	if (num <= 0 || num > (int)_objects.size())
+		return;
+
+	_objects[num - 1].where.look = (uint8)(_objects[num - 1].where.look + 15);
 	moveThink(num);
-	obj.time--;
-	if (obj.time < 0 && layEgg(kRobMCube, obj.where.xindex, obj.where.yindex))
-		obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
-	if (obj.grow)
+	if (num > (int)_objects.size())
+		return;
+
+	Thing *obj = &_objects[num - 1];
+	obj->time--;
+	const bool laidEgg = (obj->time < 0) && layEgg(kRobMCube, obj->where.xindex, obj->where.yindex);
+	obj = &_objects[num - 1];
+	if (laidEgg)
+		obj->time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+	if (obj->grow)
 		bigGrow(num);
 }
 
 void ColonyEngine::pyramidThink(int num) {
-	Thing &obj = _objects[num - 1];
-	obj.where.look = (uint8)(obj.where.look + 15);
+	if (num <= 0 || num > (int)_objects.size())
+		return;
+
+	_objects[num - 1].where.look = (uint8)(_objects[num - 1].where.look + 15);
 	moveThink(num);
-	obj.time--;
-	if (obj.time < 0 && layEgg(kRobMPyramid, obj.where.xindex, obj.where.yindex))
-		obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
-	if (obj.grow)
+	if (num > (int)_objects.size())
+		return;
+
+	Thing *obj = &_objects[num - 1];
+	obj->time--;
+	const bool laidEgg = (obj->time < 0) && layEgg(kRobMPyramid, obj->where.xindex, obj->where.yindex);
+	obj = &_objects[num - 1];
+	if (laidEgg)
+		obj->time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+	if (obj->grow)
 		bigGrow(num);
 }
 
 void ColonyEngine::upyramidThink(int num) {
-	Thing &obj = _objects[num - 1];
-	obj.where.look = (uint8)(obj.where.look - 15);
+	if (num <= 0 || num > (int)_objects.size())
+		return;
+
+	_objects[num - 1].where.look = (uint8)(_objects[num - 1].where.look - 15);
 	moveThink(num);
-	obj.time--;
-	if (obj.time < 0 && layEgg(kRobMUPyramid, obj.where.xindex, obj.where.yindex))
-		obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
-	if (obj.grow)
+	if (num > (int)_objects.size())
+		return;
+
+	Thing *obj = &_objects[num - 1];
+	obj->time--;
+	const bool laidEgg = (obj->time < 0) && layEgg(kRobMUPyramid, obj->where.xindex, obj->where.yindex);
+	obj = &_objects[num - 1];
+	if (laidEgg)
+		obj->time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+	if (obj->grow)
 		bigGrow(num);
 }
 
 void ColonyEngine::eyeThink(int num) {
-	Thing &obj = _objects[num - 1];
+	if (num <= 0 || num > (int)_objects.size())
+		return;
+
 	moveThink(num);
-	obj.time--;
-	if (obj.time < 0 && layEgg(kRobMEye, obj.where.xindex, obj.where.yindex))
-		obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
-	if (obj.grow)
+	if (num > (int)_objects.size())
+		return;
+
+	Thing *obj = &_objects[num - 1];
+	obj->time--;
+	const bool laidEgg = (obj->time < 0) && layEgg(kRobMEye, obj->where.xindex, obj->where.yindex);
+	obj = &_objects[num - 1];
+	if (laidEgg)
+		obj->time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+	if (obj->grow)
 		bigGrow(num);
 }
 
@@ -209,35 +241,39 @@ void ColonyEngine::queenThink(int num) {
 	obj.where.look = (uint8)(obj.where.ang + obj.where.lookx);
 
 	if (obj.time < 0) {
+		const int eggX = obj.where.xindex;
+		const int eggY = obj.where.yindex;
+		bool laidEgg = false;
+
 		switch (_randomSource.getRandomNumber(3)) {
 		case 0:
-			if (layEgg(kRobMEye, obj.where.xindex, obj.where.yindex))
-				obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+			laidEgg = layEgg(kRobMEye, eggX, eggY);
 			break;
 		case 1:
-			if (layEgg(kRobMCube, obj.where.xindex, obj.where.yindex))
-				obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+			laidEgg = layEgg(kRobMCube, eggX, eggY);
 			break;
 		case 2:
-			if (layEgg(kRobMPyramid, obj.where.xindex, obj.where.yindex))
-				obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+			laidEgg = layEgg(kRobMPyramid, eggX, eggY);
 			break;
 		case 3:
-			if (layEgg(kRobMUPyramid, obj.where.xindex, obj.where.yindex))
-				obj.time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
+			laidEgg = layEgg(kRobMUPyramid, eggX, eggY);
 			break;
 		}
+
+		if (laidEgg)
+			_objects[num - 1].time = 10 + (_randomSource.getRandomNumber(255) & 0xFF);
 	}
 
-	if (!obj.grow)
+	Thing &updatedObj = _objects[num - 1];
+	if (!updatedObj.grow)
 		return;
 
-	if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-	    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
-	    _robotArray[obj.where.xindex][obj.where.yindex] == num)
-		_robotArray[obj.where.xindex][obj.where.yindex] = 0;
+	if (updatedObj.where.xindex >= 0 && updatedObj.where.xindex < 32 &&
+	    updatedObj.where.yindex >= 0 && updatedObj.where.yindex < 32 &&
+	    _robotArray[updatedObj.where.xindex][updatedObj.where.yindex] == num)
+		_robotArray[updatedObj.where.xindex][updatedObj.where.yindex] = 0;
 
-	obj.alive = 0;
+	updatedObj.alive = 0;
 	_allGrow = false;
 	_sound->play(Sound::kExplode);
 
@@ -640,8 +676,8 @@ int ColonyEngine::scanForPlayer(int num) {
 		fire.yloc = fireY;
 		fire.xindex = fireX >> 8;
 		fire.yindex = fireY >> 8;
-		fireX += (_cost[fire.ang] << 1);
-		fireY += (_sint[fire.ang] << 1);
+		fireX += _cost[fire.ang] * 2;
+		fireY += _sint[fire.ang] * 2;
 		_suppressCollisionSound = true;
 		collide = checkwall(fireX, fireY, &fire);
 		_suppressCollisionSound = false;


Commit: 4df04620276bf64b914bfd56144f68481c5fd020
    https://github.com/scummvm/scummvm/commit/4df04620276bf64b914bfd56144f68481c5fd020
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:38+02:00

Commit Message:
COLONY: improve robot spawn positioning

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 4820674e3e3..77219aebf9f 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -683,7 +683,6 @@ private:
 	void robotShoot(int num);
 	void meEat();
 	void respawnObject(int num, int type);
-	void notePlayerTrail(int oldX, int oldY, int newX, int newY);
 };
 
 } // End of namespace Colony
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 5f0476c0e48..90bfbd9b075 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -262,10 +262,14 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 	auto occupied = [&]() -> int {
 		return occupiedObjectAt(xind2, yind2, pobject);
 	};
-	auto moveTo = [&]() -> int {
+	auto moveTo = [&](uint8 trailCode = 0) -> int {
 		const int rnum = occupied();
 		if (rnum)
 			return rnum;
+		if (trailCode != 0 && pobject->type == kMeNum &&
+		    pobject->xindex >= 0 && pobject->xindex < 32 &&
+		    pobject->yindex >= 0 && pobject->yindex < 32)
+			_dirXY[pobject->xindex][pobject->yindex] = trailCode;
 		pobject->yindex = yind2;
 		pobject->xindex = xind2;
 		pobject->dx = xnew - pobject->xloc;
@@ -281,8 +285,26 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		int r = tryPassThroughFeature(pobject->xindex, pobject->yindex, dir, pobject);
 		if (r == 2)
 			return 0; // teleported  position already updated by the feature
-		if (r == 1)
-			return moveTo();
+		if (r == 1) {
+			uint8 trailCode = 0;
+			switch (dir) {
+			case kDirNorth:
+				trailCode = 1;
+				break;
+			case kDirSouth:
+				trailCode = 5;
+				break;
+			case kDirEast:
+				trailCode = 3;
+				break;
+			case kDirWest:
+				trailCode = 7;
+				break;
+			default:
+				break;
+			}
+			return moveTo(trailCode);
+		}
 		return -2; // blocked, caller handles
 	};
 
@@ -298,7 +320,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 
 		if (yind2 > pobject->yindex) {
 			if (!(_wall[pobject->xindex][yind2] & 1))
-				return moveTo();
+				return moveTo(1);
 			{
 				int r = tryFeature(kDirNorth);
 				if (r != -2)
@@ -312,7 +334,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		}
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 1))
-			return moveTo();
+			return moveTo(5);
 		{
 			int r = tryFeature(kDirSouth);
 			if (r != -2)
@@ -328,7 +350,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 	if (yind2 == pobject->yindex) {
 		if (xind2 > pobject->xindex) {
 			if (!(_wall[xind2][pobject->yindex] & 2))
-				return moveTo();
+				return moveTo(3);
 			{
 				int r = tryFeature(kDirEast);
 				if (r != -2)
@@ -342,7 +364,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		}
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 2))
-			return moveTo();
+			return moveTo(7);
 		{
 			int r = tryFeature(kDirWest);
 			if (r != -2)
@@ -890,9 +912,6 @@ void ColonyEngine::checkCenter() {
 }
 
 void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
-	const int oldX = _me.xindex;
-	const int oldY = _me.yindex;
-
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 		_robotArray[_me.xindex][_me.yindex] = 0;
 
@@ -900,10 +919,6 @@ void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
 	if (robot > 0 && allowInteraction)
 		interactWithObject(robot);
 
-	if (_gameMode == kModeColony && oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32 &&
-	    (oldX != _me.xindex || oldY != _me.yindex))
-		notePlayerTrail(oldX, oldY, _me.xindex, _me.yindex);
-
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 		_robotArray[_me.xindex][_me.yindex] = kMeNum;
 }
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index fe473953b4a..ccb1bf9a7e0 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -19,37 +19,6 @@ static bool isEggType(int type) {
 	return type > kRobUPyramid && type < kRobQueen;
 }
 
-static uint8 trailCodeForDelta(int dx, int dy) {
-	if (dx > 1)
-		dx = 1;
-	else if (dx < -1)
-		dx = -1;
-
-	if (dy > 1)
-		dy = 1;
-	else if (dy < -1)
-		dy = -1;
-
-	if (dx > 0 && dy > 0)
-		return 1;
-	if (dx > 0 && dy == 0)
-		return 2;
-	if (dx > 0 && dy < 0)
-		return 3;
-	if (dx == 0 && dy < 0)
-		return 4;
-	if (dx < 0 && dy < 0)
-		return 5;
-	if (dx < 0 && dy == 0)
-		return 6;
-	if (dx < 0 && dy > 0)
-		return 7;
-	if (dx == 0 && dy > 0)
-		return 8;
-
-	return 0;
-}
-
 static int trailTargetAngle(uint8 code) {
 	switch (code) {
 	case 2:
@@ -73,17 +42,6 @@ static int trailTargetAngle(uint8 code) {
 	}
 }
 
-void ColonyEngine::notePlayerTrail(int oldX, int oldY, int newX, int newY) {
-	if (oldX < 0 || oldX >= 32 || oldY < 0 || oldY >= 32)
-		return;
-	if (newX < 0 || newX >= 32 || newY < 0 || newY >= 32)
-		return;
-
-	const uint8 code = trailCodeForDelta(newX - oldX, newY - oldY);
-	if (code != 0)
-		_dirXY[oldX][oldY] = code;
-}
-
 void ColonyEngine::respawnObject(int num, int type) {
 	if (num <= 0 || num > (int)_objects.size())
 		return;


Commit: db68ee1e1e8c9d51b2c5d726b0945099143739f6
    https://github.com/scummvm/scummvm/commit/db68ee1e1e8c9d51b2c5d726b0945099143739f6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:39+02:00

Commit Message:
COLONY: refine debug channel categories and fix minor issues

Changed paths:
    engines/colony/animation.cpp
    engines/colony/battle.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/detection.cpp
    engines/colony/interaction.cpp
    engines/colony/map.cpp
    engines/colony/movement.cpp
    engines/colony/think.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 0b8f00983a5..99918c91727 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -1191,9 +1191,10 @@ void ColonyEngine::handleAirlockClick(int item) {
 	const int doorFrames = getAnimationStateCount(_lSprites, doorItem);
 	int openStart = isMac ? 2 : 1;
 	int closeStart = isMac ? doorFrames - 1 : doorFrames;
-	const uint8 *airlock = (_airlockX >= 0 && _airlockY >= 0 && _airlockDirection >= 0) ?
-		mapFeatureAt(_airlockX, _airlockY, _airlockDirection) : nullptr;
-	const bool locked = airlock ? (airlock[1] != 0) : !_doorOpen;
+	uint8 *airlock = (_airlockX >= 0 && _airlockY >= 0 && _airlockDirection >= 0 &&
+		_airlockX < 31 && _airlockY < 31 && _airlockDirection < 5) ?
+		&_mapData[_airlockX][_airlockY][_airlockDirection][0] : nullptr;
+	bool locked = airlock ? (airlock[1] != 0) : !_doorOpen;
 
 	if (openStart > doorFrames)
 		openStart = doorFrames;
@@ -1216,6 +1217,7 @@ void ColonyEngine::handleAirlockClick(int item) {
 			}
 			setDoorState(_airlockX, _airlockY, _airlockDirection, 1);
 			_doorOpen = false;
+			locked = true;
 		} else {
 			for (int i = openStart; i <= doorFrames; i++) {
 				setObjectState(doorItem, i);
@@ -1226,6 +1228,7 @@ void ColonyEngine::handleAirlockClick(int item) {
 
 			setDoorState(_airlockX, _airlockY, _airlockDirection, 0);
 			_doorOpen = true;
+			locked = false;
 			if (_level >= 1 && _level <= 8 && _levelData[_level - 1].count == 2) {
 				_airlockTerminate = true;
 				_animationResult = 0;
diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index c65ef156948..2054bfd11c6 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -27,6 +27,7 @@
 #include "colony/colony.h"
 #include "colony/gfx.h"
 #include "colony/sound.h"
+#include "common/debug.h"
 #include "common/system.h"
 #include <math.h>
 
@@ -967,6 +968,10 @@ void ColonyEngine::battleThink() {
 		_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));
+		debugC(1, kColonyDebugCombat,
+			"battleEnemyShoot: enemy=%d pos=(%d,%d) ang=%d proj=(%d,%d)",
+			shooter, _bfight[shooter].xloc, _bfight[shooter].yloc, _battleProj.ang,
+			_battleProj.xloc, _battleProj.yloc);
 		_pcount = 10;
 		_projon = true;
 	}
@@ -1135,6 +1140,9 @@ void ColonyEngine::battleProjCommand(int xcheck, int ycheck) {
 
 	if (xcheck > _me.xloc - 200 && xcheck < _me.xloc + 200 &&
 	    ycheck > _me.yloc - 200 && ycheck < _me.yloc + 200) {
+		debugC(1, kColonyDebugCombat,
+			"battleProjHitPlayer: proj=(%d,%d) player=(%d,%d) delta=[-4,-4,-4]",
+			xcheck, ycheck, _me.xloc, _me.yloc);
 		setPower(-4, -4, -4);
 		_sound->play(Sound::kExplode);
 		_projon = false;
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 88c624214c0..55dc53a4ff4 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -151,13 +151,6 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_gametest = false;
 	_blackoutColor = 15; // Set to white (vINTWHITE) for better visibility in darkness
 
-	DebugMan.addDebugChannel(kColonyDebugMove, "move", "Movement and collision");
-	DebugMan.addDebugChannel(kColonyDebugRender, "render", "3D rendering and graphics");
-	DebugMan.addDebugChannel(kColonyDebugAnimation, "animation", "Animation and sprites");
-	DebugMan.addDebugChannel(kColonyDebugMap, "map", "Map loading and robots");
-	DebugMan.addDebugChannel(kColonyDebugSound, "sound", "Sound and music");
-	DebugMan.addDebugChannel(kColonyDebugUI, "ui", "UI, text, and menus");
-
 	setDebugger(new Debugger(this));
 
 	_sound = new Sound(this);
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 77219aebf9f..cc08bd4df50 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -218,6 +218,7 @@ enum {
 	kColonyDebugMap = 1 << 3,
 	kColonyDebugSound = 1 << 4,
 	kColonyDebugUI = 1 << 5,
+	kColonyDebugCombat = 1 << 6,
 };
 
 // Mac menu action IDs (matching original Mac Colony menu structure)
diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
index 63f71e658eb..5fc64ca5218 100644
--- a/engines/colony/detection.cpp
+++ b/engines/colony/detection.cpp
@@ -20,9 +20,21 @@
  */
 
 #include "colony/detection.h"
+#include "colony/colony.h"
 
 namespace Colony {
 
+static const DebugChannelDef debugFlagList[] = {
+	{ kColonyDebugMove, "move", "Movement and collision" },
+	{ kColonyDebugRender, "render", "3D rendering and graphics" },
+	{ kColonyDebugAnimation, "animation", "Animation and sprites" },
+	{ kColonyDebugMap, "map", "Map loading and robots" },
+	{ kColonyDebugSound, "sound", "Sound and music" },
+	{ kColonyDebugUI, "ui", "UI, text, and menus" },
+	{ kColonyDebugCombat, "combat", "Combat, damage, and power changes" },
+	DEBUG_CHANNEL_END
+};
+
 static const PlainGameDescriptor colonyGames[] = {
 	{"colony", "The Colony"},
 	{0, 0}
@@ -71,6 +83,10 @@ public:
 	const char *getOriginalCopyright() const override {
 		return "Copyright (C) 1988 David A. Smith";
 	}
+
+	const DebugChannelDef *getDebugChannels() const override {
+		return Colony::debugFlagList;
+	}
 };
 
 REGISTER_PLUGIN_STATIC(COLONY_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, ColonyMetaEngineDetection);
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index d15cf5c5d2b..d3b185404d9 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -278,10 +278,24 @@ void ColonyEngine::interactWithObject(int objNum) {
 // shoot.c SetPower(): adjust player's 3 power levels and update display.
 // p0=weapons delta, p1=life delta, p2=armor delta.
 void ColonyEngine::setPower(int p0, int p1, int p2) {
+	const int32 oldPower0 = _me.power[0];
+	const int32 oldPower1 = _me.power[1];
+	const int32 oldPower2 = _me.power[2];
+
 	_me.power[0] = MAX<int32>(_me.power[0] + p0, 0);
 	_me.power[1] = MAX<int32>(_me.power[1] + p1, 0);
 	_me.power[2] = MAX<int32>(_me.power[2] + p2, 0);
 
+	if (p0 != 0 || p1 != 0 || p2 != 0) {
+		debugC(1, kColonyDebugCombat,
+			"setPower: delta=[%d,%d,%d] from=[%d,%d,%d] to=[%d,%d,%d] mode=%s level=%d pos=(%d,%d)",
+			p0, p1, p2,
+			(int)oldPower0, (int)oldPower1, (int)oldPower2,
+			(int)_me.power[0], (int)_me.power[1], (int)_me.power[2],
+			_gameMode == kModeBattle ? "battle" : "colony",
+			_level, _me.xindex, _me.yindex);
+	}
+
 	if (_me.power[1] <= 0) {
 		debugC(1, kColonyDebugUI, "Player died! power=[%d,%d,%d]",
 			(int)_me.power[0], (int)_me.power[1], (int)_me.power[2]);
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index eaf490a497e..e7691453307 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -49,6 +49,14 @@ static const int kRobotTypeOrder[] = {
 	kRobSCube
 };
 
+static void reservePlayerObjectSlot(Common::Array<Thing> &objects) {
+	if ((int)objects.size() == kMeNum - 1) {
+		Thing reserved;
+		memset(&reserved, 0, sizeof(reserved));
+		objects.push_back(reserved);
+	}
+}
+
 void ColonyEngine::loadMap(int mnum) {
 	saveLevelState();
 
@@ -110,6 +118,7 @@ void ColonyEngine::loadMap(int mnum) {
 							obj.where.yindex = j;
 							obj.where.ang = (uint8)(_mapData[i][j][4][2] + 32);
 							obj.where.look = obj.where.ang;
+							reservePlayerObjectSlot(_objects);
 							_objects.push_back(obj);
 							const int objNum = (int)_objects.size(); // 1-based, DOS-style robot slots
 							if (objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
@@ -198,6 +207,8 @@ void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
 
 	int slot = -1;
 	for (int j = _dynamicObjectBase; j < (int)_objects.size(); j++) {
+		if (j == kMeNum - 1)
+			continue;
 		if (!_objects[j].alive) {
 			slot = j;
 			break;
@@ -208,6 +219,7 @@ void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
 	} else {
 		Thing obj;
 		memset(&obj, 0, sizeof(obj));
+		reservePlayerObjectSlot(_objects);
 		_objects.push_back(obj);
 		slot = (int)_objects.size() - 1;
 		resetObjectSlot(slot, type, xloc, yloc, ang);
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 90bfbd9b075..47aae8bc826 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -321,6 +321,8 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		if (yind2 > pobject->yindex) {
 			if (!(_wall[pobject->xindex][yind2] & 1))
 				return moveTo(1);
+			if (pobject->type == 2)
+				return -1;
 			{
 				int r = tryFeature(kDirNorth);
 				if (r != -2)
@@ -335,6 +337,8 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 1))
 			return moveTo(5);
+		if (pobject->type == 2)
+			return -1;
 		{
 			int r = tryFeature(kDirSouth);
 			if (r != -2)
@@ -351,6 +355,8 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		if (xind2 > pobject->xindex) {
 			if (!(_wall[xind2][pobject->yindex] & 2))
 				return moveTo(3);
+			if (pobject->type == 2)
+				return -1;
 			{
 				int r = tryFeature(kDirEast);
 				if (r != -2)
@@ -365,6 +371,8 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 2))
 			return moveTo(7);
+		if (pobject->type == 2)
+			return -1;
 		{
 			int r = tryFeature(kDirWest);
 			if (r != -2)
@@ -446,7 +454,7 @@ bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
 	}
 
 	if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31 && opposite >= 0) {
-		if (_mapData[nx][ny][opposite][0] == wallType)
+		if (wallType == kWallFeatureAirlock || _mapData[nx][ny][opposite][0] == wallType)
 			_mapData[nx][ny][opposite][1] = (uint8)state;
 	}
 
@@ -838,6 +846,9 @@ void ColonyEngine::fallThroughHole() {
 
 	// Original tunnel(TRUE): damage the player's three power bars.
 	int damage = -(_level << 7);
+	debugC(1, kColonyDebugCombat,
+		"fallThroughHole: level=%d from=(%d,%d) deadFall=%d delta=[%d,%d,%d]",
+		_level, _me.xindex, _me.yindex, deadFall ? 1 : 0, damage, damage, damage);
 	setPower(damage, damage, damage);
 	playTunnelEffect(true);
 
@@ -893,6 +904,9 @@ void ColonyEngine::checkCenter() {
 			break;
 		case 5: // HOTFOOT  electric floor, damages power
 			_sound->play(Sound::kBzzz);
+			debugC(1, kColonyDebugCombat,
+				"hotfoot: level=%d cell=(%d,%d) delta=[%d,%d,%d]",
+				_level, _me.xindex, _me.yindex, -(5 << _level), -(5 << _level), -(5 << _level));
 			setPower(-(5 << _level), -(5 << _level), -(5 << _level));
 			break;
 		default:
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index ccb1bf9a7e0..7b5e3633155 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -467,6 +467,12 @@ void ColonyEngine::snoopThink(int num) {
 		if (collide == kMeNum) {
 			respawnObject(num, kRobSnoop);
 			_sound->play(Sound::kSlug);
+			debugC(1, kColonyDebugCombat,
+				"snoopHitPlayer: snoop=%d cell=(%d,%d) delta=[-%d,-%d,-%d]",
+				num, oldX, oldY,
+				(int)MIN<int32>((3 * _me.power[0]) / 4, 32000),
+				(int)MIN<int32>((3 * _me.power[1]) / 4, 32000),
+				(int)MIN<int32>((3 * _me.power[2]) / 4, 32000));
 			setPower(-(int)MIN<int32>((3 * _me.power[0]) / 4, 32000),
 				-(int)MIN<int32>((3 * _me.power[1]) / 4, 32000),
 				-(int)MIN<int32>((3 * _me.power[2]) / 4, 32000));
@@ -644,6 +650,10 @@ int ColonyEngine::scanForPlayer(int num) {
 	if (collide == kMeNum) {
 		obj.where.dx = (_me.xloc - fireX) >> 1;
 		obj.where.dy = (_me.yloc - fireY) >> 1;
+		debugC(1, kColonyDebugCombat,
+			"scanForPlayer: robot=%d type=%d from=(%d,%d) ang=%d sees player, aim=(%d,%d)",
+			num, obj.type, obj.where.xindex, obj.where.yindex, obj.where.ang,
+			(int)obj.where.dx, (int)obj.where.dy);
 	}
 
 	return collide;
@@ -669,6 +679,9 @@ void ColonyEngine::robotShoot(int num) {
 	const int epower2 = qlog(_me.power[2]);
 	const int armor2 = _armor * _armor;
 	int damage = 0;
+	int damage0 = 0;
+	int damage1 = 0;
+	int damage2 = 0;
 
 	obj.opcode = kOpcodeFShoot;
 	_sound->play(Sound::kShoot);
@@ -678,47 +691,69 @@ void ColonyEngine::robotShoot(int num) {
 		damage = epower2 * armor2 - (1 << _level);
 		if (damage > -_level)
 			damage = -_level;
-		setPower(damage, damage, damage);
+		damage0 = damage;
+		damage1 = damage;
+		damage2 = damage;
 		break;
 	case kRobPyramid:
 		damage = epower2 * armor2 - (1 << _level);
 		if (damage > -_level)
 			damage = -_level;
-		setPower(damage, -_level, -_level);
+		damage0 = damage;
+		damage1 = -_level;
+		damage2 = -_level;
 		break;
 	case kRobCube:
 		damage = epower2 * armor2 - (1 << _level);
 		if (damage > -_level)
 			damage = -_level;
-		setPower(-_level, damage, -_level);
+		damage0 = -_level;
+		damage1 = damage;
+		damage2 = -_level;
 		break;
 	case kRobUPyramid:
 		damage = epower2 * armor2 - (1 << _level);
 		if (damage > -_level)
 			damage = -_level;
-		setPower(-_level, -_level, damage);
+		damage0 = -_level;
+		damage1 = -_level;
+		damage2 = damage;
 		break;
 	case kRobQueen:
 		damage = epower2 * armor2 - ((_level == 1 || _level == 7) ? (3 << 10) : (3 << _level));
 		if (damage > -_level)
 			damage = -(1 << _level);
-		setPower(damage, damage, damage);
+		damage0 = damage;
+		damage1 = damage;
+		damage2 = damage;
 		break;
 	case kRobDrone:
 		damage = epower2 * armor2 - (4 << _level);
 		if (damage > -_level)
 			damage = -(_level << 1);
-		setPower(damage, damage, damage);
+		damage0 = damage;
+		damage1 = damage;
+		damage2 = damage;
 		break;
 	case kRobSoldier:
 		damage = epower2 * armor2 - (5 << _level);
 		if (damage > -_level)
 			damage = -(_level << 1);
-		setPower(damage, damage, damage);
+		damage0 = damage;
+		damage1 = damage;
+		damage2 = damage;
 		break;
 	default:
 		break;
 	}
+
+	if (damage0 != 0 || damage1 != 0 || damage2 != 0) {
+		debugC(1, kColonyDebugCombat,
+			"robotShoot: robot=%d type=%d cell=(%d,%d) ang=%d delta=[%d,%d,%d] player=(%d,%d)",
+			num, obj.type, obj.where.xindex, obj.where.yindex, obj.where.ang,
+			damage0, damage1, damage2, _me.xindex, _me.yindex);
+		setPower(damage0, damage1, damage2);
+	}
 }
 
 void ColonyEngine::meEat() {


Commit: 21e8f031566e6224154550d5a77fbc4ef3a3d16e
    https://github.com/scummvm/scummvm/commit/21e8f031566e6224154550d5a77fbc4ef3a3d16e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:39+02:00

Commit Message:
COLONY: unlock airlocks when using debug console

Changed paths:
    engines/colony/animation.cpp
    engines/colony/debugger.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 99918c91727..0fcb1edd244 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -1201,6 +1201,13 @@ void ColonyEngine::handleAirlockClick(int item) {
 	if (closeStart < 1)
 		closeStart = 1;
 
+	debugC(1, kColonyDebugAnimation,
+		"Airlock click: item=%d locked=%d unlocked=%d core=%d levelCount=%d wall=(%d,%d,%d) dest=[%d,%d,%d]",
+		item, locked ? 1 : 0, _unlocked ? 1 : 0, _corePower[_coreIndex],
+		(_level >= 1 && _level <= 8) ? _levelData[_level - 1].count : -1,
+		_airlockX, _airlockY, _airlockDirection,
+		airlock ? airlock[2] : -1, airlock ? airlock[3] : -1, airlock ? airlock[4] : -1);
+
 	// Original DoAirLock() also differs by platform:
 	// DOS uses sprite 2 as the 3-state door, Mac uses sprite 3 as the 5-state door.
 	if ((item == exitItem || (!isMac && item == 101)) && !locked) {
@@ -1236,6 +1243,10 @@ void ColonyEngine::handleAirlockClick(int item) {
 				return;
 			}
 		}
+	} else if (item == toggleItem) {
+		debugC(1, kColonyDebugAnimation,
+			"Airlock toggle ignored: unlocked=%d core=%d airlockPtr=%d",
+			_unlocked ? 1 : 0, _corePower[_coreIndex], airlock ? 1 : 0);
 	} else if (item == 101) {
 		_animationRunning = false;
 	}
diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
index 333a306bc95..49b6d558608 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/debugger.cpp
@@ -375,6 +375,7 @@ bool Debugger::cmdColony(int argc, const char **argv) {
 		_vm->_weapons = 3;
 		_vm->_armor = 3;
 		_vm->_hasKeycard = true;
+		_vm->_unlocked = true;
 		_vm->_orbit = 0;
 		_vm->_projon = false;
 		_vm->_pcount = 0;
@@ -392,6 +393,7 @@ bool Debugger::cmdColony(int argc, const char **argv) {
 	debugPrintf("Suit: power=[%d,%d,%d] weapons=%d armor=%d\n",
 	            (int)_vm->_me.power[0], (int)_vm->_me.power[1],
 	            (int)_vm->_me.power[2], _vm->_weapons, _vm->_armor);
+	debugPrintf("Security state forced to unlocked for colony debug entry\n");
 	return false;
 }
 


Commit: eb9558aa6989260dd8e39554e14515567be68510
    https://github.com/scummvm/scummvm/commit/eb9558aa6989260dd8e39554e14515567be68510
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:39+02:00

Commit Message:
COLONY: improve proportional letter spacing in text rendering

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 1a57ca55dd1..d0e4785daa9 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -1843,22 +1843,66 @@ void ColonyEngine::wallPolygon(const float corners[4][3], const float *u, const
 	_gfx->draw3DPolygon(px, py, pz, count, color);
 }
 
-static float signedArea2D(const float *x, const float *y, int count) {
-	float area = 0.0f;
+static bool nearlyEqual(float a, float b, float eps = 0.0001f) {
+	return fabsf(a - b) <= eps;
+}
+
+static void addSortedUniqueFloat(float *values, int &count, float value, float eps = 0.0001f) {
 	for (int i = 0; i < count; ++i) {
-		const int next = (i + 1) % count;
-		area += x[i] * y[next] - x[next] * y[i];
+		if (nearlyEqual(values[i], value, eps))
+			return;
+		if (value < values[i]) {
+			for (int j = count; j > i; --j)
+				values[j] = values[j - 1];
+			values[i] = value;
+			++count;
+			return;
+		}
 	}
-	return area * 0.5f;
+
+	values[count++] = value;
+}
+
+static bool segmentIntersection2D(float ax, float ay, float bx, float by,
+		float cx, float cy, float dx, float dy, float &ix, float &iy) {
+	const float den = (ax - bx) * (cy - dy) - (ay - by) * (cx - dx);
+	if (fabsf(den) < 0.0001f)
+		return false;
+
+	const float t = ((ax - cx) * (cy - dy) - (ay - cy) * (cx - dx)) / den;
+	const float u = ((ax - cx) * (ay - by) - (ay - cy) * (ax - bx)) / den;
+	if (t <= 0.0001f || t >= 0.9999f || u <= 0.0001f || u >= 0.9999f)
+		return false;
+
+	ix = ax + t * (bx - ax);
+	iy = ay + t * (by - ay);
+	return true;
 }
 
-static bool pointInTriangle2D(float px, float py, float ax, float ay, float bx, float by, float cx, float cy) {
-	const float d1 = (px - bx) * (ay - by) - (ax - bx) * (py - by);
-	const float d2 = (px - cx) * (by - cy) - (bx - cx) * (py - cy);
-	const float d3 = (px - ax) * (cy - ay) - (cx - ax) * (py - ay);
-	const bool hasNeg = (d1 < 0.0f) || (d2 < 0.0f) || (d3 < 0.0f);
-	const bool hasPos = (d1 > 0.0f) || (d2 > 0.0f) || (d3 > 0.0f);
-	return !(hasNeg && hasPos);
+static float edgeXAtY(float x0, float y0, float x1, float y1, float y) {
+	if (nearlyEqual(y0, y1))
+		return x0;
+
+	const float t = (y - y0) / (y1 - y0);
+	return x0 + (x1 - x0) * t;
+}
+
+struct WallCharSpan {
+	float xMid;
+	float xTop;
+	float xBottom;
+};
+
+static void sortWallCharSpans(WallCharSpan *spans, int count) {
+	for (int i = 1; i < count; ++i) {
+		WallCharSpan span = spans[i];
+		int j = i - 1;
+		while (j >= 0 && spans[j].xMid > span.xMid) {
+			spans[j + 1] = spans[j];
+			--j;
+		}
+		spans[j + 1] = span;
+	}
 }
 
 static const char *const kWallCharData[] = {
@@ -1951,67 +1995,70 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 		if (!macMode || count < 3)
 			return;
 
-		const float area = signedArea2D(u, v, count);
-		if (fabsf(area) < 0.0001f)
-			return;
-
-		int indices[32];
+		float cuts[96];
+		int cutCount = 0;
 		for (int i = 0; i < count; ++i)
-			indices[i] = i;
-
-		const bool ccw = area > 0.0f;
-		int remaining = count;
-		int guard = 0;
-
-		while (remaining > 3 && guard++ < 64) {
-			bool earFound = false;
-			for (int i = 0; i < remaining; ++i) {
-				const int prev = indices[(i + remaining - 1) % remaining];
-				const int curr = indices[i];
-				const int next = indices[(i + 1) % remaining];
-				const float cross = (u[curr] - u[prev]) * (v[next] - v[prev]) -
-					(v[curr] - v[prev]) * (u[next] - u[prev]);
-				if (ccw ? (cross <= 0.0001f) : (cross >= -0.0001f))
-					continue;
+			addSortedUniqueFloat(cuts, cutCount, v[i]);
 
-				bool containsPoint = false;
-				for (int j = 0; j < remaining; ++j) {
-					const int test = indices[j];
-					if (test == prev || test == curr || test == next)
-						continue;
-					if (pointInTriangle2D(u[test], v[test], u[prev], v[prev], u[curr], v[curr], u[next], v[next])) {
-						containsPoint = true;
-						break;
-					}
-				}
-				if (containsPoint)
+		for (int i = 0; i < count; ++i) {
+			const int nextI = (i + 1) % count;
+			for (int j = i + 1; j < count; ++j) {
+				const int nextJ = (j + 1) % count;
+				if (j == i || j == nextI || nextJ == i)
+					continue;
+				if (i == 0 && nextJ == 0)
+					continue;
+				if ((nearlyEqual(u[i], u[j]) && nearlyEqual(v[i], v[j])) ||
+				    (nearlyEqual(u[i], u[nextJ]) && nearlyEqual(v[i], v[nextJ])) ||
+				    (nearlyEqual(u[nextI], u[j]) && nearlyEqual(v[nextI], v[j])) ||
+				    (nearlyEqual(u[nextI], u[nextJ]) && nearlyEqual(v[nextI], v[nextJ])))
 					continue;
 
-				const float triU[3] = {u[prev], u[curr], u[next]};
-				const float triV[3] = {v[prev], v[curr], v[next]};
-				wallPolygon(corners, triU, triV, 3, fillColor);
-
-				for (int j = i; j < remaining - 1; ++j)
-					indices[j] = indices[j + 1];
-				--remaining;
-				earFound = true;
-				break;
+				float ix, iy;
+				if (segmentIntersection2D(u[i], v[i], u[nextI], v[nextI], u[j], v[j], u[nextJ], v[nextJ], ix, iy))
+					addSortedUniqueFloat(cuts, cutCount, iy);
 			}
+		}
 
-			if (!earFound) {
-				for (int i = 1; i < remaining - 1; ++i) {
-					const float triU[3] = {u[indices[0]], u[indices[i]], u[indices[i + 1]]};
-					const float triV[3] = {v[indices[0]], v[indices[i]], v[indices[i + 1]]};
-					wallPolygon(corners, triU, triV, 3, fillColor);
-				}
-				return;
+		for (int band = 0; band < cutCount - 1; ++band) {
+			const float y0 = cuts[band];
+			const float y1 = cuts[band + 1];
+			if (y1 - y0 <= 0.0001f)
+				continue;
+
+			const float yMid = (y0 + y1) * 0.5f;
+			const float yTopSample = y0 + (y1 - y0) * 0.001f;
+			const float yBottomSample = y1 - (y1 - y0) * 0.001f;
+			WallCharSpan spans[32];
+			int spanCount = 0;
+
+			for (int i = 0; i < count; ++i) {
+				const int next = (i + 1) % count;
+				const float yA = v[i];
+				const float yB = v[next];
+				if (nearlyEqual(yA, yB))
+					continue;
+				if (!((yA <= yMid && yMid < yB) || (yB <= yMid && yMid < yA)))
+					continue;
+
+				spans[spanCount].xMid = edgeXAtY(u[i], yA, u[next], yB, yMid);
+				spans[spanCount].xTop = edgeXAtY(u[i], yA, u[next], yB, yTopSample);
+				spans[spanCount].xBottom = edgeXAtY(u[i], yA, u[next], yB, yBottomSample);
+				++spanCount;
 			}
-		}
 
-		if (remaining == 3) {
-			const float triU[3] = {u[indices[0]], u[indices[1]], u[indices[2]]};
-			const float triV[3] = {v[indices[0]], v[indices[1]], v[indices[2]]};
-			wallPolygon(corners, triU, triV, 3, fillColor);
+			if (spanCount < 2)
+				continue;
+
+			sortWallCharSpans(spans, spanCount);
+			for (int i = 0; i + 1 < spanCount; i += 2) {
+				if (spans[i + 1].xMid - spans[i].xMid <= 0.0001f)
+					continue;
+
+				const float quadU[4] = {spans[i].xTop, spans[i + 1].xTop, spans[i + 1].xBottom, spans[i].xBottom};
+				const float quadV[4] = {y0, y0, y1, y1};
+				wallPolygon(corners, quadU, quadV, 4, fillColor);
+			}
 		}
 	};
 


Commit: 90a41977ed240b65f2932b6edbcd032e75a73eb0
    https://github.com/scummvm/scummvm/commit/90a41977ed240b65f2932b6edbcd032e75a73eb0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:39+02:00

Commit Message:
COLONY: save/load fixes

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/metaengine.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 55dc53a4ff4..198a2853391 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -164,6 +164,10 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 
 ColonyEngine::~ColonyEngine() {
 	deleteAnimation();
+	if (_savedScreen) {
+		_savedScreen->free();
+		delete _savedScreen;
+	}
 	if (_pictPower) {
 		_pictPower->free();
 		delete _pictPower;
@@ -185,6 +189,21 @@ ColonyEngine::~ColonyEngine() {
 	delete _wm;
 }
 
+void ColonyEngine::pauseEngineIntern(bool pause) {
+	if (pause && _gfx && _level >= 1 && _level <= 7) {
+		if (_savedScreen) {
+			_savedScreen->free();
+			delete _savedScreen;
+		}
+		_savedScreen = _gfx->getScreenshot();
+	}
+
+	Engine::pauseEngineIntern(pause);
+
+	if (_frameLimiter)
+		_frameLimiter->pause(pause);
+}
+
 void ColonyEngine::loadMacColors() {
 	_hasMacColors = false;
 	Common::SeekableReadStream *file = nullptr;
@@ -565,9 +584,16 @@ Common::Error ColonyEngine::run() {
 	// Frame limiter: target 60fps, like Freescape engine
 	_frameLimiter = new Graphics::FrameLimiter(_system, 60);
 
-	playIntro();
+	const int startupLoadSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
+	if (startupLoadSlot >= 0) {
+		Common::Error status = loadGameState(startupLoadSlot);
+		if (status.getCode() != Common::kNoError)
+			return status;
+	} else {
+		playIntro();
+		loadMap(1); // Normal startup path begins on the first map after the intro
+	}
 
-	loadMap(1); // Try to load the first map
 	_mouseLocked = true;
 	updateMouseCapture(true);
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index cc08bd4df50..a26169516a3 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -364,8 +364,10 @@ public:
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
 	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
 	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
+	void pauseEngineIntern(bool pause) override;
 	Common::Platform getPlatform() const { return _gameDescription->platform; }
 	bool isSoundEnabled() const { return _soundOn; }
+	const Graphics::Surface *getSavedScreen() const { return _savedScreen; }
 
 	void initTrig();
 	void loadMacColors();
@@ -406,6 +408,7 @@ private:
 	Sound *_sound;
 	Graphics::FrameLimiter *_frameLimiter;
 	Common::RenderMode _renderMode;
+	Graphics::Surface *_savedScreen = nullptr;
 
 
 	int _tsin, _tcos;
diff --git a/engines/colony/metaengine.cpp b/engines/colony/metaengine.cpp
index 492f2b3de3f..d6d183bccf8 100644
--- a/engines/colony/metaengine.cpp
+++ b/engines/colony/metaengine.cpp
@@ -27,6 +27,8 @@
 #include "backends/keymapper/action.h"
 #include "backends/keymapper/keymap.h"
 #include "backends/keymapper/standard-actions.h"
+#include "graphics/scaler.h"
+#include "graphics/thumbnail.h"
 
 namespace Colony {
 
@@ -61,9 +63,24 @@ public:
 		return Common::kNoError;
 	}
 
+	void getSavegameThumbnail(Graphics::Surface &thumb) override;
 	Common::KeymapArray initKeymaps(const char *target) const override;
 };
 
+void ColonyMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
+	ColonyEngine *engine = (ColonyEngine *)g_engine;
+	if (!engine || !engine->getSavedScreen())
+		return;
+
+	Graphics::Surface *scaledSavedScreen = scale(*engine->getSavedScreen(), kThumbnailWidth, kThumbnailHeight2);
+	if (!scaledSavedScreen)
+		return;
+
+	thumb.copyFrom(*scaledSavedScreen);
+	scaledSavedScreen->free();
+	delete scaledSavedScreen;
+}
+
 Common::KeymapArray ColonyMetaEngine::initKeymaps(const char *target) const {
 	Common::Keymap *engineKeyMap = new Common::Keymap(Common::Keymap::kKeymapTypeGame, "colony", "The Colony");
 	Common::Action *act;
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index b039d477d1e..a370b008356 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -69,6 +69,7 @@ public:
 
 	// Overlay a RGBA software surface onto the GL framebuffer (for Mac menu bar).
 	virtual void drawSurface(const Graphics::Surface *surf, int x, int y) {}
+	virtual Graphics::Surface *getScreenshot() { return nullptr; }
 
 	// Convenience color accessors
 	uint32 white() const { return 255; }
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 87bc89e68c8..3bdee5b012e 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -90,6 +90,7 @@ public:
 	}
 	void computeScreenViewport() override;
 	void drawSurface(const Graphics::Surface *surf, int x, int y) override;
+	Graphics::Surface *getScreenshot() override;
 
 private:
 	void useColor(uint32 color);
@@ -703,6 +704,18 @@ void OpenGLRenderer::copyToScreen() {
 	_system->updateScreen();
 }
 
+Graphics::Surface *OpenGLRenderer::getScreenshot() {
+	Graphics::Surface *surface = new Graphics::Surface();
+	surface->create(_screenViewport.width(), _screenViewport.height(),
+	                Graphics::PixelFormat::createFormatRGBA32());
+
+	glReadPixels(_screenViewport.left, _system->getHeight() - _screenViewport.bottom,
+	             _screenViewport.width(), _screenViewport.height(),
+	             GL_RGBA, GL_UNSIGNED_BYTE, surface->getPixels());
+	surface->flipVertical(Common::Rect(surface->w, surface->h));
+	return surface;
+}
+
 Renderer *createOpenGLRenderer(OSystem *system, int width, int height) {
 	return new OpenGLRenderer(system, width, height);
 }


Commit: 42564fea389825527668a3915d4317515f4d6ec1
    https://github.com/scummvm/scummvm/commit/42564fea389825527668a3915d4317515f4d6ec1
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:40+02:00

Commit Message:
COLONY: improved battle mode rendering

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


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 2054bfd11c6..3d94bb7bf87 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -97,16 +97,6 @@ static int wrapBattleCoord(int coord) {
 	return coord;
 }
 
-static bool battleOriginVisible(long relX, long relY, uint8 look, const int *sint, const int *cost,
-                                int &side, int &forward) {
-	forward = (int)((relX * cost[look] + relY * sint[look]) >> 7);
-	if (forward <= 0)
-		return false;
-
-	side = (int)((relX * sint[look] - relY * cost[look]) >> 7);
-	return forward > ABS(side);
-}
-
 static bool battleProjectPoint(const Common::Rect &screenR, uint8 look, int8 lookY, const int *sint,
                                const int *cost, int camX, int camY,
                                float worldX, float worldY, float worldZ,
@@ -606,24 +596,23 @@ void ColonyEngine::battleDrawPyramids() {
 		if (qyi < 0) { qyi = 3; dy = 0x7FFF; }
 		if (qyi > 3) { qyi = 0; dy = 0x7FFF; }
 
+		// Original BattleQuadrant relied on 16-bit signed wrap here.
+		const int wrappedCamX = battleNormalizeCoord(xloc + dx);
+		const int wrappedCamY = battleNormalizeCoord(yloc + dy);
+
 		for (int i = 0; i < kMaxQuad; i++) {
-			long relX = _pyramids[qxi][qyi][i].xloc - (xloc + dx);
-			long relY = _pyramids[qxi][qyi][i].yloc - (yloc + dy);
+			const int relX = battleNormalizeCoord(_pyramids[qxi][qyi][i].xloc - wrappedCamX);
+			const int relY = battleNormalizeCoord(_pyramids[qxi][qyi][i].yloc - wrappedCamY);
 			if (ABS(relX) + ABS(relY) >= 8000)
 				continue;
 
-			int side = 0;
-			int forward = 0;
-			if (!battleOriginVisible(relX, relY, _me.look, _sint, _cost, side, forward))
-				continue;
+			const int forward = (int)((relX * _cost[_me.look] + relY * _sint[_me.look]) >> 7);
 
 			Locate &pyr = _pyramids[qxi][qyi][i];
 			battleResetBounds(_screenR, pyr);
 
-			int wx = pyr.xloc;
-			int wy = pyr.yloc;
-			if (dx) wx -= dx;
-			if (dy) wy -= dy;
+			const int wx = battleNormalizeCoord(_me.xloc + relX);
+			const int wy = battleNormalizeCoord(_me.yloc + relY);
 
 			battleAccumulateBounds(_screenR, kRockDef, pyr, wx, wy, pyr.ang, -kFloor,
 			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
@@ -686,10 +675,7 @@ void ColonyEngine::battleDrawTanks() {
 		if (ABS(relX) + ABS(relY) >= 8000)
 			continue;
 
-		int side = 0;
-		int forward = 0;
-		if (!battleOriginVisible(relX, relY, _me.look, _sint, _cost, side, forward))
-			continue;
+		const int forward = (int)((relX * _cost[_me.look] + relY * _sint[_me.look]) >> 7);
 
 		Locate &drone = _bfight[i];
 		battleResetBounds(_screenR, drone);
@@ -763,20 +749,17 @@ void ColonyEngine::battleDrawTanks() {
 		long relX = _battleProj.xloc - _me.xloc;
 		long relY = _battleProj.yloc - _me.yloc;
 		if (ABS(relX) + ABS(relY) < 20000) {
-			int side = 0;
-			int forward = 0;
-			if (battleOriginVisible(relX, relY, _me.look, _sint, _cost, side, forward)) {
-				battleResetBounds(_screenR, _battleProj);
-				battleAccumulateBounds(_screenR, kProjDef, _battleProj,
-				                       _battleProj.xloc, _battleProj.yloc, _battleProj.ang, -kFloor,
-				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
-				_battleProj.dist = forward;
-				draw3DBattlePrism(kProjDef, _battleProj.xloc, _battleProj.yloc,
-				                  _battleProj.ang, -kFloor);
-				if (_battleMaxP < 100) {
-					_battlePwh[_battleMaxP] = &_battleProj;
-					_battleMaxP++;
-				}
+			const int forward = (int)((relX * _cost[_me.look] + relY * _sint[_me.look]) >> 7);
+			battleResetBounds(_screenR, _battleProj);
+			battleAccumulateBounds(_screenR, kProjDef, _battleProj,
+			                       _battleProj.xloc, _battleProj.yloc, _battleProj.ang, -kFloor,
+			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			_battleProj.dist = forward;
+			draw3DBattlePrism(kProjDef, _battleProj.xloc, _battleProj.yloc,
+			                  _battleProj.ang, -kFloor);
+			if (_battleMaxP < 100) {
+				_battlePwh[_battleMaxP] = &_battleProj;
+				_battleMaxP++;
 			}
 		}
 	}
@@ -786,25 +769,22 @@ void ColonyEngine::battleDrawTanks() {
 		long relX = _battleEnter.xloc - _me.xloc;
 		long relY = _battleEnter.yloc - _me.yloc;
 		if (ABS(relX) + ABS(relY) < 20000) {
-			int side = 0;
-			int forward = 0;
-			if (battleOriginVisible(relX, relY, _me.look, _sint, _cost, side, forward)) {
-				battleResetBounds(_screenR, _battleEnter);
-				battleAccumulateBounds(_screenR, kEntDef, _battleEnter,
-				                       _battleEnter.xloc, _battleEnter.yloc, _battleEnter.ang, -kFloor,
-				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
-				battleAccumulateBounds(_screenR, kEntDoorDef, _battleEnter,
-				                       _battleEnter.xloc, _battleEnter.yloc, _battleEnter.ang, -kFloor,
-				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
-				_battleEnter.dist = forward;
-				draw3DBattlePrism(kEntDef, _battleEnter.xloc, _battleEnter.yloc,
-				                  _battleEnter.ang, -kFloor);
-				draw3DBattlePrism(kEntDoorDef, _battleEnter.xloc, _battleEnter.yloc,
-				                  _battleEnter.ang, -kFloor);
-				if (_battleMaxP < 100) {
-					_battlePwh[_battleMaxP] = &_battleEnter;
-					_battleMaxP++;
-				}
+			const int forward = (int)((relX * _cost[_me.look] + relY * _sint[_me.look]) >> 7);
+			battleResetBounds(_screenR, _battleEnter);
+			battleAccumulateBounds(_screenR, kEntDef, _battleEnter,
+			                       _battleEnter.xloc, _battleEnter.yloc, _battleEnter.ang, -kFloor,
+			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			battleAccumulateBounds(_screenR, kEntDoorDef, _battleEnter,
+			                       _battleEnter.xloc, _battleEnter.yloc, _battleEnter.ang, -kFloor,
+			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			_battleEnter.dist = forward;
+			draw3DBattlePrism(kEntDef, _battleEnter.xloc, _battleEnter.yloc,
+			                  _battleEnter.ang, -kFloor);
+			draw3DBattlePrism(kEntDoorDef, _battleEnter.xloc, _battleEnter.yloc,
+			                  _battleEnter.ang, -kFloor);
+			if (_battleMaxP < 100) {
+				_battlePwh[_battleMaxP] = &_battleEnter;
+				_battleMaxP++;
 			}
 		}
 	}
@@ -814,51 +794,48 @@ void ColonyEngine::battleDrawTanks() {
 		long relX = _battleShip.xloc - _me.xloc;
 		long relY = _battleShip.yloc - _me.yloc;
 		if (ABS(relX) + ABS(relY) < 20000) {
-			int side = 0;
-			int forward = 0;
-			if (battleOriginVisible(relX, relY, _me.look, _sint, _cost, side, forward)) {
-				battleResetBounds(_screenR, _battleShip);
-				battleAccumulateBounds(_screenR, kSBodyDef, _battleShip,
-				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
-				battleAccumulateBounds(_screenR, kSFrontDef, _battleShip,
-				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
-				battleAccumulateBounds(_screenR, kSBackDef, _battleShip,
-				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
-				battleAccumulateBounds(_screenR, kFTopDef, _battleShip,
-				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
-				battleAccumulateBounds(_screenR, kFLeftDef, _battleShip,
-				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
-				battleAccumulateBounds(_screenR, kFRightDef, _battleShip,
-				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
-				battleAccumulateBounds(_screenR, kSDoorDef, _battleShip,
-				                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-				                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
-				_battleShip.dist = forward;
-				draw3DBattlePrism(kSBodyDef, _battleShip.xloc, _battleShip.yloc,
-				                  _battleShip.ang, -kFloor);
-				draw3DBattlePrism(kSFrontDef, _battleShip.xloc, _battleShip.yloc,
-				                  _battleShip.ang, -kFloor);
-				draw3DBattlePrism(kSBackDef, _battleShip.xloc, _battleShip.yloc,
-				                  _battleShip.ang, -kFloor);
-				// Fins: force-draw (no backface cull) — single-sided surfaces
-				draw3DBattlePrism(kFTopDef, _battleShip.xloc, _battleShip.yloc,
-				                  _battleShip.ang, -kFloor);
-				draw3DBattlePrism(kFLeftDef, _battleShip.xloc, _battleShip.yloc,
-				                  _battleShip.ang, -kFloor);
-				draw3DBattlePrism(kFRightDef, _battleShip.xloc, _battleShip.yloc,
-				                  _battleShip.ang, -kFloor);
-				draw3DBattlePrism(kSDoorDef, _battleShip.xloc, _battleShip.yloc,
-				                  _battleShip.ang, -kFloor);
-				if (_battleMaxP < 100) {
-					_battlePwh[_battleMaxP] = &_battleShip;
-					_battleMaxP++;
-				}
+			const int forward = (int)((relX * _cost[_me.look] + relY * _sint[_me.look]) >> 7);
+			battleResetBounds(_screenR, _battleShip);
+			battleAccumulateBounds(_screenR, kSBodyDef, _battleShip,
+			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			battleAccumulateBounds(_screenR, kSFrontDef, _battleShip,
+			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			battleAccumulateBounds(_screenR, kSBackDef, _battleShip,
+			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			battleAccumulateBounds(_screenR, kFTopDef, _battleShip,
+			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			battleAccumulateBounds(_screenR, kFLeftDef, _battleShip,
+			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			battleAccumulateBounds(_screenR, kFRightDef, _battleShip,
+			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			battleAccumulateBounds(_screenR, kSDoorDef, _battleShip,
+			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			_battleShip.dist = forward;
+			draw3DBattlePrism(kSBodyDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			draw3DBattlePrism(kSFrontDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			draw3DBattlePrism(kSBackDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			// Fins: force-draw (no backface cull) — single-sided surfaces
+			draw3DBattlePrism(kFTopDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			draw3DBattlePrism(kFLeftDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			draw3DBattlePrism(kFRightDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			draw3DBattlePrism(kSDoorDef, _battleShip.xloc, _battleShip.yloc,
+			                  _battleShip.ang, -kFloor);
+			if (_battleMaxP < 100) {
+				_battlePwh[_battleMaxP] = &_battleShip;
+				_battleMaxP++;
 			}
 		}
 	}
@@ -996,6 +973,7 @@ void ColonyEngine::battleCommand(int xnew, int ynew) {
 	}
 
 	auto enterColony = [&](int mapNum, int xloc, int yloc) {
+		playTunnelAirlockEffect();
 		_gameMode = kModeColony;
 		_projon = false;
 		_pcount = 0;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index a26169516a3..29db499615f 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -583,6 +583,7 @@ private:
 	int openAdjacentDoors(int x, int y);
 	int goToDestination(const uint8 *map, Locate *pobject);
 	int tryPassThroughFeature(int fromX, int fromY, int direction, Locate *pobject);
+	void playTunnelAirlockEffect();
 	void syncMacMenuChecks();
 	void updateMouseCapture(bool recenter = true);
 	Common::Point getAimPoint() const;
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 47aae8bc826..676a41e6a84 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -54,6 +54,8 @@ static const int kTunnelST[] = {
 	2, 2, 2, 2, 2, 2, 2, 2, 2, 2
 };
 
+static const int kTunnelStraight[60] = {0};
+
 static uint32 packTunnelMacColor(const uint16 rgb[3]) {
 	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
 	       ((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
@@ -504,6 +506,8 @@ int ColonyEngine::goToDestination(const uint8 *map, Locate *pobject) {
 		if (pobject != &_me)
 			return 0;
 
+		playTunnelAirlockEffect();
+
 		if (_orbit || !(_armor || _fl)) {
 			terminateGame(false);
 			return 0;
@@ -702,6 +706,101 @@ 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 int tunnelColor = 24; // c_tunnel
+	const uint32 fillFg = macColor ? packTunnelMacColor(_macColors[tunnelColor].fg) : 0;
+	const uint32 fillBg = macColor ? packTunnelMacColor(_macColors[tunnelColor].bg) : 0;
+	const uint32 lineColor = macColor ? 0xFF000000 : 15;
+	int troy = 180;
+	int counter = 4;
+	int spd = 180 / counter;
+	int remaining = 5;
+
+	while (remaining > 0 && !shouldQuit()) {
+		if (macColor) {
+			fillTunnelPattern(_gfx, effectRect, fillFg, fillBg, _macColors[tunnelColor].pattern);
+		} else {
+			_gfx->fillRect(effectRect, 0);
+			_gfx->drawRect(effectRect, 15);
+		}
+
+		Common::Rect clipRect = effectRect;
+		int prevDL[3] = {0, 0, 0};
+		int prevDR[3] = {0, 0, 0};
+		int prevLT = 0;
+		int prevRT = 0;
+		int rox = -150;
+		int roy = troy;
+
+		for (int i = 0; i < remaining; ++i) {
+			int dl[2];
+			int dr[2];
+			projectTunnelPoint(effectRect, dl, rox, roy);
+			projectTunnelPoint(effectRect, dr, rox + 200, roy);
+
+			int left[3] = {
+				dl[0],
+				dl[1],
+				effectRect.bottom - 1 - (dl[1] - effectRect.top)
+			};
+			int right[3] = {
+				dr[0],
+				dr[1],
+				effectRect.bottom - 1 - (dr[1] - effectRect.top)
+			};
+
+			drawTunnelLine(_gfx, clipRect, left[0], left[2], left[0], left[1], lineColor);
+			drawTunnelLine(_gfx, clipRect, left[0], left[1], right[0], right[1], lineColor);
+			drawTunnelLine(_gfx, clipRect, right[0], right[1], right[0], right[2], lineColor);
+
+			const int center = (left[0] + right[0]) >> 1;
+			const int lt = (left[0] + center) >> 1;
+			const int rt = (right[0] + center) >> 1;
+
+			if (i > 0) {
+				drawTunnelLine(_gfx, clipRect, left[0], left[1], prevDL[0], prevDL[1], lineColor);
+				drawTunnelLine(_gfx, clipRect, right[0], right[1], prevDR[0], prevDR[1], lineColor);
+				drawTunnelLine(_gfx, clipRect, right[0], right[2], prevDR[0], prevDR[2], lineColor);
+				drawTunnelLine(_gfx, clipRect, left[0], left[2], prevDL[0], prevDL[2], lineColor);
+				drawTunnelLine(_gfx, clipRect, prevLT, prevDL[2], lt, left[2], lineColor);
+				drawTunnelLine(_gfx, clipRect, lt, left[2], rt, right[2], lineColor);
+				drawTunnelLine(_gfx, clipRect, rt, right[2], prevRT, prevDR[2], lineColor);
+			}
+
+			clipRect.left = MAX<int>(clipRect.left, left[0]);
+			clipRect.right = MIN<int>(clipRect.right, right[0]);
+			clipRect.top = MAX<int>(clipRect.top, left[1]);
+			clipRect.bottom = MIN<int>(clipRect.bottom, right[2]);
+			if (clipRect.bottom <= clipRect.top || clipRect.left >= clipRect.right)
+				break;
+
+			prevDL[0] = left[0];
+			prevDL[1] = left[1];
+			prevDL[2] = left[2];
+			prevDR[0] = right[0];
+			prevDR[1] = right[1];
+			prevDR[2] = right[2];
+			prevLT = lt;
+			prevRT = rt;
+			roy += 256;
+			rox += kTunnelStraight[i] << 2;
+		}
+
+		_gfx->copyToScreen();
+		_system->delayMillis(50);
+
+		troy -= spd;
+		counter--;
+		if (counter == 0) {
+			troy = 180;
+			counter = 4;
+			remaining--;
+		}
+	}
+}
+
 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.


Commit: f7352da5cf4ef79c29814da52f15d1264c8068ae
    https://github.com/scummvm/scummvm/commit/f7352da5cf4ef79c29814da52f15d1264c8068ae
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:40+02:00

Commit Message:
COLONY: split render.cpp into render_features.cpp and render_objects.cpp

Changed paths:
  A engines/colony/render_features.cpp
  A engines/colony/render_internal.h
  A engines/colony/render_objects.cpp
    engines/colony/module.mk
    engines/colony/render.cpp


diff --git a/engines/colony/module.mk b/engines/colony/module.mk
index 3135abfa9bd..0a3606dfef6 100644
--- a/engines/colony/module.mk
+++ b/engines/colony/module.mk
@@ -12,6 +12,8 @@ MODULE_OBJS := \
 	metaengine.o \
 	movement.o \
 	render.o \
+	render_features.o \
+	render_objects.o \
 	renderer_opengl.o \
 	savegame.o \
 	sound.o \
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index d0e4785daa9..cc155a79374 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "colony/colony.h"
+#include "colony/render_internal.h"
 #include "colony/gfx.h"
 #include "common/system.h"
 #include "common/util.h"
@@ -164,73 +165,6 @@ static const DOSColorEntry &lookupDOSColor(int colorIdx) {
 	}
 }
 
-// Mac Classic dither patterns (from colorize.c cColor[].pattern).
-// Mac Classic was a 1-bit B&W display. QuickDraw used 8x8 dither patterns
-// to simulate grayscale: WHITE, LTGRAY, GRAY, DKGRAY, BLACK, CLEAR.
-// Rendered via GL_POLYGON_STIPPLE: two-pass fill (white bg + black fg with stipple mask).
-enum MacPattern {
-	kPatternWhite  = 0, // Solid white (all background)
-	kPatternLtGray = 1, // 25% foreground (sparse black dots on white)
-	kPatternGray   = 2, // 50% foreground (checkerboard)
-	kPatternDkGray = 3, // 75% foreground (dense black, sparse white)
-	kPatternBlack  = 4, // Solid black (all foreground)
-	kPatternClear  = 5  // Outline only (no fill)
-};
-
-// Internal-only surface color for Mac wall helper objects (FWALL/CWALL).
-// In the original Mac renderer these use c_lwall/c_dwall, which are
-// special corridor-wall colors rather than normal material palette entries.
-static const int kColorCorridorWall = 1000;
-
-// GL_POLYGON_STIPPLE patterns: 32x32 bit arrays (128 bytes), tiled from 8x8 Mac patterns.
-// Bit=1 → fragment drawn (black foreground), bit=0 → fragment discarded (white background shows).
-// Mac QuickDraw convention: bit=1 = foreground (BLACK), bit=0 = background (WHITE).
-static const byte kStippleLtGray[128] = {
-	// 0x88,0x22 alternating rows = 25% coverage (sparse dots)
-	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
-	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
-	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
-	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
-	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
-	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
-	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
-	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
-};
-
-static const byte kStippleGray[128] = {
-	// 0xAA,0x55 alternating rows = 50% coverage (checkerboard)
-	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
-	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
-	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
-	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
-	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
-	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
-	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
-	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
-};
-
-static const byte kStippleDkGray[128] = {
-	// 0x77,0xDD alternating rows = 75% coverage (dense dots)
-	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
-	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
-	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
-	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
-	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
-	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
-	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
-	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
-};
-
-// Lookup: MacPattern enum → GL stipple data pointer (null for solid/clear patterns)
-static const byte *kMacStippleData[] = {
-	nullptr,        // kPatternWhite  - solid white, no stipple
-	kStippleLtGray, // kPatternLtGray - 25% black dots
-	kStippleGray,   // kPatternGray   - 50% checkerboard
-	kStippleDkGray, // kPatternDkGray - 75% black
-	nullptr,        // kPatternBlack  - solid black, no stipple
-	nullptr         // kPatternClear  - outline only
-};
-
 // Map ObjColor → Mac B&W dither pattern (from ROBOCOLR.C MONOCHROME field).
 // The monochrome field in the DOS table matches MacPattern enum values directly:
 // WHITE=0, LTGRAY=1, GRAY=2, DKGRAY=3, BLACK=4, CLEAR=5.
@@ -238,11 +172,6 @@ static int lookupMacPattern(int colorIdx) {
 	return lookupDOSColor(colorIdx).monochrome;
 }
 
-// Pack Mac 16-bit RGB into 32-bit ARGB with 0xFF000000 marker for direct RGB.
-static uint32 packMacColor(const uint16 rgb[3]) {
-	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
-}
-
 // Map ObjColor constant → Mac Color256 index (cColor[] from colordef.h).
 static int mapObjColorToMacColor(int colorIdx) {
 	switch (colorIdx) {
@@ -314,958 +243,6 @@ static int mapObjColorToMacColor(int colorIdx) {
 	}
 }
 
-// Helper: set up Mac color stipple rendering for a given cColor[] pattern.
-// Configures setMacColors/setStippleData/setWireframe for the pattern type.
-// Returns the stipple pointer (null for solid patterns)  caller must clear with setStippleData(nullptr).
-static const byte *setupMacPattern(Renderer *gfx, int pattern, uint32 fg, uint32 bg) {
-	const byte *stipple = (pattern >= 1 && pattern <= 3) ? kMacStippleData[pattern] : nullptr;
-	if (stipple) {
-		gfx->setMacColors(fg, bg);
-		gfx->setStippleData(stipple);
-		gfx->setWireframe(true, bg);
-	} else if (pattern == 4) {
-		// BLACK: solid fg fill
-		gfx->setWireframe(true, fg);
-	} else {
-		// WHITE (0): solid bg fill + fg outline
-		gfx->setWireframe(true, bg);
-	}
-	return stipple;
-}
-
-// DOS object geometry constants
-static const int kScreenPts[8][3] = {
-	{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
-	{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
-};
-static const int kScreenSurf[4][8] = {
-	{kColorBlack, 4, 0, 3, 7, 4, 0, 0}, {kColorBlack, 4, 3, 2, 6, 7, 0, 0},
-	{kColorBlack, 4, 1, 0, 4, 5, 0, 0}, {kColorBlack, 4, 2, 1, 5, 6, 0, 0}
-};
-static const int kTableTopPts[4][3] = {
-	{-128, 128, 100}, {128, 128, 100}, {128, -128, 100}, {-128, -128, 100}
-};
-static const int kTableTopSurf[1][8] = {{kColorTable, 4, 3, 2, 1, 0, 0, 0}};
-static const int kTableBasePts[8][3] = {
-	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-	{-5, 5, 100}, {5, 5, 100}, {5, -5, 100}, {-5, -5, 100}
-};
-static const int kTableBaseSurf[4][8] = {
-	{kColorTableBase, 4, 0, 3, 7, 4, 0, 0}, {kColorTableBase, 4, 3, 2, 6, 7, 0, 0},
-	{kColorTableBase, 4, 1, 0, 4, 5, 0, 0}, {kColorTableBase, 4, 2, 1, 5, 6, 0, 0}
-};
-static const int kBedPostPts[4][3] = {
-	{-82, 128, 100}, {82, 128, 100}, {82, 128, 0}, {-82, 128, 0}
-};
-static const int kBedPostSurf[1][8] = {{kColorBed, 4, 3, 2, 1, 0, 0, 0}};
-static const int kBedBlanketPts[8][3] = {
-	{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
-	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
-};
-static const int kBlanketSurf[4][8] = {
-	{kColorBlanket, 4, 0, 3, 7, 4, 0, 0}, {kColorBlanket, 4, 3, 2, 6, 7, 0, 0},
-	{kColorBlanket, 4, 2, 1, 5, 6, 0, 0}, {kColorBlanket, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kBedSheetPts[8][3] = {
-	{-80, 128, 30}, {80, 128, 30}, {80, 70, 30}, {-80, 70, 30},
-	{-80, 128, 60}, {80, 128, 60}, {80, 70, 60}, {-80, 70, 60}
-};
-static const int kSheetSurf[3][8] = {
-	{kColorSheet, 4, 0, 3, 7, 4, 0, 0}, {kColorSheet, 4, 2, 1, 5, 6, 0, 0},
-	{kColorSheet, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kBBedBlanketPts[8][3] = {
-	{-128, 70, 0}, {128, 70, 0}, {128, -175, 0}, {-128, -175, 0},
-	{-128, 70, 60}, {128, 70, 60}, {128, -175, 60}, {-128, -175, 60}
-};
-static const int kBBedSheetPts[8][3] = {
-	{-128, 128, 30}, {128, 128, 30}, {128, 70, 30}, {-128, 70, 30},
-	{-128, 128, 60}, {128, 128, 60}, {128, 70, 60}, {-128, 70, 60}
-};
-static const int kBBedPostPts[4][3] = {
-	{-130, 128, 100}, {130, 128, 100}, {130, 128, 0}, {-130, 128, 0}
-};
-static const int kDeskTopPts[4][3] = {
-	{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
-};
-static const int kDeskTopSurf[1][8] = {{kColorDeskTop, 4, 3, 2, 1, 0, 0, 0}};
-static const int kDeskLeftPts[8][3] = {
-	{-135, 95, 0}, {-55, 95, 0}, {-55, -95, 0}, {-135, -95, 0},
-	{-135, 95, 100}, {-55, 95, 100}, {-55, -95, 100}, {-135, -95, 100}
-};
-static const int kDeskRightPts[8][3] = {
-	{55, 95, 0}, {135, 95, 0}, {135, -95, 0}, {55, -95, 0},
-	{55, 95, 100}, {135, 95, 100}, {135, -95, 100}, {55, -95, 100}
-};
-static const int kDeskCabSurf[4][8] = {
-	{kColorDesk, 4, 0, 3, 7, 4, 0, 0}, {kColorDesk, 4, 3, 2, 6, 7, 0, 0},
-	{kColorDesk, 4, 1, 0, 4, 5, 0, 0}, {kColorDesk, 4, 2, 1, 5, 6, 0, 0}
-};
-static const int kSeatPts[4][3] = {
-	{-40, 210, 60}, {40, 210, 60}, {40, 115, 60}, {-40, 115, 60}
-};
-static const int kSeatSurf[1][8] = {{kColorDeskChair, 4, 3, 2, 1, 0, 0, 0}};
-static const int kArmLeftPts[4][3] = {
-	{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
-};
-static const int kArmRightPts[4][3] = {
-	{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
-};
-static const int kArmSurf[2][8] = {
-	{kColorClear, 4, 3, 2, 1, 0, 0, 0}, {kColorClear, 4, 0, 1, 2, 3, 0, 0}
-};
-static const int kBackPts[4][3] = {
-	{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
-};
-static const int kBackSurf[2][8] = {
-	{kColorDeskChair, 4, 3, 2, 1, 0, 0, 0}, {kColorDeskChair, 4, 0, 1, 2, 3, 0, 0}
-};
-static const int kComputerPts[8][3] = {
-	{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
-	{70, 25, 120}, {120, 25, 120}, {120, -25, 120}, {70, -25, 120}
-};
-static const int kMonitorPts[8][3] = {
-	{75, 20, 120}, {115, 20, 120}, {115, -20, 120}, {75, -20, 120},
-	{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
-};
-static const int kComputerSurf[5][8] = {
-	{kColorMac, 4, 7, 6, 5, 4, 0, 0}, {kColorMac, 4, 0, 3, 7, 4, 0, 0},
-	{kColorMac, 4, 3, 2, 6, 7, 0, 0}, {kColorMac, 4, 1, 0, 4, 5, 0, 0},
-	{kColorMac, 4, 2, 1, 5, 6, 0, 0}
-};
-static const int kDeskScreenPts[4][3] = {
-	{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
-};
-static const int kDeskScreenSurf[1][8] = {{kColorMacScreen, 4, 3, 2, 1, 0, 0, 0}};
-static const int kCSeatPts[4][3] = {
-	{-40, 40, 60}, {40, 40, 60}, {40, -40, 60}, {-40, -40, 60}
-};
-static const int kCSeatSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
-static const int kCArmLeftPts[4][3] = {
-	{-50, 40, 90}, {-40, 40, 60}, {-40, -40, 60}, {-50, -40, 90}
-};
-static const int kCArmLeftSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
-static const int kCArmRightPts[4][3] = {
-	{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
-};
-static const int kCArmRightSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
-static const int kCBackPts[4][3] = {
-	{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
-};
-static const int kCBackSurf[2][8] = {
-	{kColorChair, 4, 3, 2, 1, 0, 0, 0}, {kColorChair, 4, 0, 1, 2, 3, 0, 0}
-};
-static const int kCBasePts[8][3] = {
-	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-	{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
-};
-static const int kCBaseSurf[4][8] = {
-	{kColorChairBase, 4, 0, 3, 7, 4, 0, 0}, {kColorChairBase, 4, 3, 2, 6, 7, 0, 0},
-	{kColorChairBase, 4, 1, 0, 4, 5, 0, 0}, {kColorChairBase, 4, 2, 1, 5, 6, 0, 0}
-};
-static const int kConsolePts[8][3] = {
-	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
-	{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
-};
-static const int kConsoleSurf[5][8] = {
-	{kColorConsole, 4, 4, 0, 3, 7, 0, 0}, {kColorConsole, 4, 7, 3, 2, 6, 0, 0},
-	{kColorConsole, 4, 5, 1, 0, 4, 0, 0}, {kColorConsole, 4, 6, 2, 1, 5, 0, 0},
-	{kColorBlack, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kCouchSurf[5][8] = {
-	{kColorCouch, 4, 0, 3, 7, 4, 0, 0}, {kColorCouch, 4, 3, 2, 6, 7, 0, 0},
-	{kColorCouch, 4, 1, 0, 4, 5, 0, 0}, {kColorCouch, 4, 2, 1, 5, 6, 0, 0},
-	{kColorCouch, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kACouchPts[8][3] = {
-	{-50, 150, 0}, {50, 150, 0}, {50, -150, 0}, {-50, -150, 0},
-	{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50}
-};
-static const int kBCouchPts[8][3] = {
-	{-80, 150, 0}, {-45, 150, 0}, {-45, -150, 0}, {-80, -150, 0},
-	{-80, 150, 120}, {-55, 150, 120}, {-55, -150, 120}, {-80, -150, 120}
-};
-static const int kCCouchPts[8][3] = {
-	{-70, 170, 0}, {50, 170, 0}, {50, 150, 0}, {-70, 150, 0},
-	{-70, 170, 80}, {50, 170, 80}, {50, 150, 80}, {-70, 150, 80}
-};
-static const int kDCouchPts[8][3] = {
-	{-70, -150, 0}, {50, -150, 0}, {50, -170, 0}, {-70, -170, 0},
-	{-70, -150, 80}, {50, -150, 80}, {50, -170, 80}, {-70, -170, 80}
-};
-static const int kAChairPts[8][3] = {
-	{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
-	{-50, 50, 50}, {50, 50, 50}, {50, -50, 50}, {-50, -50, 50}
-};
-static const int kBChairPts[8][3] = {
-	{-80, 50, 0}, {-45, 50, 0}, {-45, -50, 0}, {-80, -50, 0},
-	{-80, 50, 120}, {-55, 50, 120}, {-55, -50, 120}, {-80, -50, 120}
-};
-static const int kCChairPts2[8][3] = {
-	{-70, 70, 0}, {50, 70, 0}, {50, 50, 0}, {-70, 50, 0},
-	{-70, 70, 80}, {50, 70, 80}, {50, 50, 80}, {-70, 50, 80}
-};
-static const int kDChairPts[8][3] = {
-	{-70, -50, 0}, {50, -50, 0}, {50, -70, 0}, {-70, -70, 0},
-	{-70, -50, 80}, {50, -50, 80}, {50, -70, 80}, {-70, -70, 80}
-};
-static const int kTVBodyPts[8][3] = {
-	{-30, 60, 0}, {30, 60, 0}, {30, -60, 0}, {-30, -60, 0},
-	{-30, 60, 120}, {30, 60, 120}, {30, -60, 120}, {-30, -60, 120}
-};
-static const int kTVBodySurf[5][8] = {
-	{kColorTV, 4, 0, 3, 7, 4, 0, 0}, {kColorTV, 4, 3, 2, 6, 7, 0, 0},
-	{kColorTV, 4, 1, 0, 4, 5, 0, 0}, {kColorTV, 4, 2, 1, 5, 6, 0, 0},
-	{kColorTV, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kTVScreenPts[4][3] = {
-	{30, 50, 10}, {30, -50, 10}, {30, 50, 110}, {30, -50, 110}
-};
-static const int kTVScreenSurf[1][8] = {{kColorTVScreen, 4, 1, 0, 2, 3, 0, 0}};
-static const int kDrawerPts[8][3] = {
-	{-80, 70, 0}, {0, 70, 0}, {0, -70, 0}, {-80, -70, 0},
-	{-80, 70, 100}, {0, 70, 100}, {0, -70, 100}, {-80, -70, 100}
-};
-static const int kDrawerSurf[5][8] = {
-	{kColorDrawer, 4, 0, 3, 7, 4, 0, 0}, {kColorDrawer, 4, 3, 2, 6, 7, 0, 0},
-	{kColorDrawer, 4, 1, 0, 4, 5, 0, 0}, {kColorDrawer, 4, 2, 1, 5, 6, 0, 0},
-	{kColorDrawer, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kMirrorPts[4][3] = {
-	{-80, 65, 100}, {-80, -65, 100}, {-80, 65, 210}, {-80, -65, 210}
-};
-static const int kMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
-
-// Bathtub geometry
-static const int kTubPts[8][3] = {
-	{-128, 128,  0}, {   0, 128,  0}, {   0,-128,  0}, {-128,-128,  0},
-	{-128, 128, 70}, {   0, 128, 70}, {   0,-128, 70}, {-128,-128, 70}
-};
-static const int kTubSurf[5][8] = {
-	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
-	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
-	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kDTubPts[6][3] = {
-	{-16, 112, 70}, {-8, 0, 70}, {-16, -112, 70}, {-112, -112, 70}, {-120, 0, 70}, {-112, 112, 70}
-};
-static const int kDTubSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
-
-// Toilet geometry
-static const int kAToiletPts[8][3] = {
-	{-128, 45, 30}, {-100, 45, 30}, {-100, -45, 30}, {-128, -45, 30},
-	{-128, 45, 100}, {-100, 45, 100}, {-100, -45, 100}, {-128, -45, 100}
-};
-static const int kAToiletSurf[5][8] = {
-	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
-	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
-	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kBToiletPts[12][3] = {
-	{-100, 20, 50}, {-60, 40, 50}, {-20, 20, 50}, {-20, -20, 50}, {-60, -40, 50}, {-100, -20, 50},
-	{-80, 10,  0}, {-60, 20,  0}, {-40, 10,  0}, {-40, -10,  0}, {-60, -20,  0}, {-80, -10,  0}
-};
-static const int kBToiletSurf[7][8] = {
-	{kColorBath, 4, 0, 1, 7, 6, 0, 0}, {kColorBath, 4, 1, 2, 8, 7, 0, 0}, {kColorBath, 4, 2, 3, 9, 8, 0, 0},
-	{kColorBath, 4, 3, 4, 10, 9, 0, 0}, {kColorBath, 4, 4, 5, 11, 10, 0, 0}, {kColorBath, 4, 5, 0, 6, 11, 0, 0},
-	{kColorBath, 6, 5, 4, 3, 2, 1, 0}
-};
-static const int kCToiletPts[6][3] = {
-	{-95, 15, 50}, {-60, 35, 50}, {-25, 15, 50}, {-25, -15, 50}, {-60, -35, 50}, {-95, -15, 50}
-};
-static const int kCToiletSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
-static const int kDToiletPts[6][3] = {
-	{-100, 20, 50}, {-100, 40, 90}, {-100, 20, 130}, {-100, -20, 130}, {-100, -40, 90}, {-100, -20, 50}
-};
-static const int kDToiletSurf[1][8] = {{kColorLtGreen, 6, 5, 4, 3, 2, 1, 0}};
-static const int kEToiletPts[4][3] = {
-	{-128,-128, 20}, {-128,-128, 200}, { 128,-128, 200}, { 128,-128, 20}
-};
-static const int kEToiletSurf[1][8] = {{kColorDkGray, 4, 0, 1, 2, 3, 0, 0}};
-
-// Sink geometry
-static const int kSinkPts[8][3] = {
-	{-128, 50, 70}, {-50, 50, 90}, {-50,-50, 90}, {-128,-50, 70},
-	{-128, 50, 110}, {-50, 50, 110}, {-50,-50, 110}, {-128,-50, 110}
-};
-static const int kSinkSurf[5][8] = {
-	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
-	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
-	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kDSinkPts[6][3] = {
-	{-55, 0, 110}, {-60, -45, 110}, {-118, -45, 110}, {-123, 0, 110}, {-118, 45, 110}, {-60, 45, 110}
-};
-static const int kDSinkSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
-static const int kSinkMirrorPts[4][3] = {
-	{-128, 65, 130}, {-128, -65, 130}, {-128, 65, 250}, {-128, -65, 250}
-};
-static const int kSinkMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
-
-static const Colony::ColonyEngine::PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
-static const Colony::ColonyEngine::PrismPartDef kTableParts[2] = {
-	{4, kTableTopPts, 1, kTableTopSurf},
-	{8, kTableBasePts, 4, kTableBaseSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kBedParts[3] = {
-	{4, kBedPostPts, 1, kBedPostSurf},
-	{8, kBedBlanketPts, 4, kBlanketSurf},
-	{8, kBedSheetPts, 3, kSheetSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kBBedParts[3] = {
-	{4, kBBedPostPts, 1, kBedPostSurf},
-	{8, kBBedBlanketPts, 4, kBlanketSurf},
-	{8, kBBedSheetPts, 3, kSheetSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kDeskParts[10] = {
-	{4, kDeskTopPts, 1, kDeskTopSurf},
-	{8, kDeskLeftPts, 4, kDeskCabSurf},
-	{8, kDeskRightPts, 4, kDeskCabSurf},
-	{4, kSeatPts, 1, kSeatSurf},
-	{4, kArmLeftPts, 2, kArmSurf},
-	{4, kArmRightPts, 2, kArmSurf},
-	{4, kBackPts, 2, kBackSurf},
-	{8, kComputerPts, 5, kComputerSurf},
-	{8, kMonitorPts, 5, kComputerSurf},
-	{4, kDeskScreenPts, 1, kDeskScreenSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kCChairParts[5] = {
-	{4, kCSeatPts, 1, kCSeatSurf},
-	{4, kCArmLeftPts, 1, kCArmLeftSurf},
-	{4, kCArmRightPts, 1, kCArmRightSurf},
-	{4, kCBackPts, 1, kCBackSurf},
-	{8, kCBasePts, 4, kCBaseSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kConsolePart = {8, kConsolePts, 5, kConsoleSurf};
-static const Colony::ColonyEngine::PrismPartDef kCouchParts[4] = {
-	{8, kACouchPts, 5, kCouchSurf},
-	{8, kBCouchPts, 5, kCouchSurf},
-	{8, kCCouchPts, 5, kCouchSurf},
-	{8, kDCouchPts, 5, kCouchSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kChairParts[4] = {
-	{8, kAChairPts, 5, kCouchSurf},
-	{8, kBChairPts, 5, kCouchSurf},
-	{8, kCChairPts2, 5, kCouchSurf},
-	{8, kDChairPts, 5, kCouchSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kTVParts[2] = {
-	{8, kTVBodyPts, 5, kTVBodySurf},
-	{4, kTVScreenPts, 1, kTVScreenSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kDrawerParts[2] = {
-	{8, kDrawerPts, 5, kDrawerSurf},
-	{4, kMirrorPts, 1, kMirrorSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kTubParts[2] = {
-	{8, kTubPts, 5, kTubSurf},
-	{6, kDTubPts, 1, kDTubSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kSinkParts[3] = {
-	{8, kSinkPts, 5, kSinkSurf},
-	{6, kDSinkPts, 1, kDSinkSurf},
-	{4, kSinkMirrorPts, 1, kSinkMirrorSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kToiletParts[4] = {
-	{8, kAToiletPts, 5, kAToiletSurf},
-	{12, kBToiletPts, 7, kBToiletSurf},
-	{6, kCToiletPts, 1, kCToiletSurf},
-	{6, kDToiletPts, 1, kDToiletSurf}
-};
-static const Colony::ColonyEngine::PrismPartDef kPToiletParts[5] = {
-	{8, kAToiletPts, 5, kAToiletSurf},
-	{12, kBToiletPts, 7, kBToiletSurf},
-	{6, kCToiletPts, 1, kCToiletSurf},
-	{6, kDToiletPts, 1, kDToiletSurf},
-	{4, kEToiletPts, 1, kEToiletSurf}
-};
-
-static const int kFWallPts[4][3] = {
-	{-128, 128, 0}, {128, -128, 0}, {-128, 128, 320}, {128, -128, 320}
-};
-static const int kFWallSurf[1][8] = {{kColorWall, 4, 2, 3, 1, 0, 0, 0}};
-static const Colony::ColonyEngine::PrismPartDef kFWallPart = {4, kFWallPts, 1, kFWallSurf};
-
-static const int kCWallPts[8][3] = {
-	{-128, 128, 0}, {0, 112, 0}, {112, 0, 0}, {128, -128, 0},
-	{-128, 128, 320},  {0, 112, 320},  {112, 0, 320},  {128, -128, 320}
-};
-static const int kCWallSurf[3][8] = {
-	{kColorWall, 4, 1, 0, 4, 5, 0, 0}, {kColorWall, 4, 2, 1, 5, 6, 0, 0}, {kColorWall, 4, 3, 2, 6, 7, 0, 0}
-};
-static const Colony::ColonyEngine::PrismPartDef kCWallPart = {8, kCWallPts, 3, kCWallSurf};
-
-static const int kPlantPotPts[12][3] = {
-	{10, 17, 40}, {20, 0, 40}, {10, -17, 40}, {-10, -17, 40}, {-20, 0, 40}, {-10, 17, 40},
-	{8, 12, 0}, {15, 0, 0}, {8, -12, 0}, {-8, -12, 0}, {-15, 0, 0}, {-8, 12, 0}
-};
-static const int kPlantPotSurf[6][8] = {
-	{kColorPot, 4, 0, 1, 7, 6, 0, 0}, {kColorPot, 4, 1, 2, 8, 7, 0, 0}, {kColorPot, 4, 2, 3, 9, 8, 0, 0},
-	{kColorPot, 4, 3, 4, 10, 9, 0, 0}, {kColorPot, 4, 4, 5, 11, 10, 0, 0}, {kColorPot, 4, 5, 0, 6, 11, 0, 0}
-};
-static const int kPlantTopPotPts[6][3] = {
-	{10, 17, 40}, {20, 0, 40}, {10, -17, 40}, {-10, -17, 40}, {-20, 0, 40}, {-10, 17, 40}
-};
-static const int kPlantTopPotSurf[1][8] = {{kColorDkGray, 6, 5, 4, 3, 2, 1, 0}};
-
-static const int kPlantLeaf0Pts[3][3] = {{0, 0, 0}, {20, 20, 150}, {70, 70, 100}};
-static const int kPlantLeaf1Pts[3][3] = {{0, 0, 0}, {-20, 30, 100}, {-60, 50, 50}};
-static const int kPlantLeaf2Pts[3][3] = {{0, 0, 0}, {-20, -10, 70}, {-90, -70, 50}};
-static const int kPlantLeaf3Pts[3][3] = {{0, 0, 0}, {20, -10, 50}, {90, -70, 70}};
-static const int kPlantLeaf4Pts[3][3] = {{0, 0, 0}, {20, -30, 190}, {40, -60, 150}};
-static const int kPlantLeaf5Pts[3][3] = {{0, 0, 0}, {20, -10, 130}, {50, -80, 200}};
-
-static const int kPlantLeafSurf[2][8] = {{kColorClear, 3, 0, 1, 2, 0, 0, 0}, {kColorClear, 3, 2, 1, 0, 0, 0, 0}};
-
-static const Colony::ColonyEngine::PrismPartDef kPlantParts[8] = {
-	{12, kPlantPotPts, 6, kPlantPotSurf},
-	{6, kPlantTopPotPts, 1, kPlantTopPotSurf},
-	{3, kPlantLeaf0Pts, 2, kPlantLeafSurf},
-	{3, kPlantLeaf1Pts, 2, kPlantLeafSurf},
-	{3, kPlantLeaf2Pts, 2, kPlantLeafSurf},
-	{3, kPlantLeaf3Pts, 2, kPlantLeafSurf},
-	{3, kPlantLeaf4Pts, 2, kPlantLeafSurf},
-	{3, kPlantLeaf5Pts, 2, kPlantLeafSurf}
-};
-
-static const int kBox1Pts[8][3] = {
-	{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
-	{-50, 50, 100}, {50, 50, 100}, {50, -50, 100}, {-50, -50, 100}
-};
-static const int kBox1Surf[5][8] = {
-	{kColorBox, 4, 0, 3, 7, 4, 0, 0}, {kColorBox, 4, 3, 2, 6, 7, 0, 0},
-	{kColorBox, 4, 1, 0, 4, 5, 0, 0}, {kColorBox, 4, 2, 1, 5, 6, 0, 0}, {kColorBox, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kBox2Pts[8][3] = {
-	{-50, 50, 100}, {50, 50, 100}, {50, -50, 100}, {-50, -50, 100},
-	{-50, 50, 200}, {50, 50, 200}, {50, -50, 200}, {-50, -50, 200}
-};
-
-static const int kReactorCorePts[12][3] = {
-	{-40, 20, 288}, {0, 40, 288}, {40, 20, 288}, {40, -20, 288}, {0, -40, 288}, {-40, -20, 288},
-	{-40, 20, 32}, {0, 40, 32}, {40, 20, 32}, {40, -20, 32}, {0, -40, 32}, {-40, -20, 32}
-};
-static const int kReactorCoreSurf[7][8] = {
-	{kColorCCore, 4, 0, 1, 7, 6, 0, 0}, {kColorCCore, 4, 1, 2, 8, 7, 0, 0}, {kColorCCore, 4, 2, 3, 9, 8, 0, 0},
-	{kColorCCore, 4, 3, 4, 10, 9, 0, 0}, {kColorCCore, 4, 4, 5, 11, 10, 0, 0}, {kColorCCore, 4, 5, 0, 6, 11, 0, 0},
-	{kColorCCore, 6, 5, 4, 3, 2, 1, 0}
-};
-static const int kReactorBasePts[8][3] = {
-	{-128, 128, 0}, {128, 128, 0}, {128, -128, 0}, {-128, -128, 0},
-	{-128, 128, 32}, {128, 128, 32}, {128, -128, 32}, {-128, -128, 32}
-};
-static const int kReactorBaseSurf[6][8] = {
-	{kColorReactor, 4, 0, 3, 7, 4, 0, 0}, {kColorReactor, 4, 3, 2, 6, 7, 0, 0}, {kColorReactor, 4, 1, 0, 4, 5, 0, 0},
-	{kColorReactor, 4, 2, 1, 5, 6, 0, 0}, {kColorReactor, 4, 7, 6, 5, 4, 0, 0}, {kColorReactor, 4, 0, 1, 2, 3, 0, 0}
-};
-static const int kReactorTopPts[8][3] = {
-	{-128, 128, 288}, {128, 128, 288}, {128, -128, 288}, {-128, -128, 288},
-	{-128, 128, 320}, {128, 128, 320}, {128, -128, 320}, {-128, -128, 320}
-};
-
-static const Colony::ColonyEngine::PrismPartDef kBox1Part = {8, kBox1Pts, 5, kBox1Surf};
-static const Colony::ColonyEngine::PrismPartDef kBox2Parts[2] = {
-	{8, kBox2Pts, 4, kBox1Surf}, // Body (stacked on box1)
-	{8, kBox1Pts, 5, kBox1Surf}  // Lid (same geometry as box1 base)
-};
-// Bench: simple box (1 part). DOS INITOBJ.C InitBench.
-static const int kBenchPts[8][3] = {
-	{-60, 128, 0}, {60, 128, 0}, {60, -128, 0}, {-60, -128, 0},
-	{-60, 128, 120}, {60, 128, 120}, {60, -128, 120}, {-60, -128, 120}
-};
-static const int kBenchSurf[5][8] = {
-	{kColorBench, 4, 0, 3, 7, 4, 0, 0}, {kColorBench, 4, 3, 2, 6, 7, 0, 0},
-	{kColorBench, 4, 1, 0, 4, 5, 0, 0}, {kColorBench, 4, 2, 1, 5, 6, 0, 0},
-	{kColorBlack, 4, 7, 6, 5, 4, 0, 0}
-};
-static const Colony::ColonyEngine::PrismPartDef kBenchPart = {8, kBenchPts, 5, kBenchSurf};
-
-// CBench: L-shaped corner bench (2 parts). DOS INITOBJ.C InitCBench.
-// Part 0: slanted front face (front-left Y=60, front-right Y=-60)
-static const int kCBenchPts[8][3] = {
-	{-60, 60, 0}, {60, -60, 0}, {60, -128, 0}, {-60, -128, 0},
-	{-60, 60, 120}, {60, -60, 120}, {60, -128, 120}, {-60, -128, 120}
-};
-// Part 1: wider perpendicular section
-static const int kDBenchPts[8][3] = {
-	{-60, 60, 0}, {128, 60, 0}, {128, -60, 0}, {60, -60, 0},
-	{-60, 60, 120}, {128, 60, 120}, {128, -60, 120}, {60, -60, 120}
-};
-static const Colony::ColonyEngine::PrismPartDef kCBenchParts[2] = {
-	{8, kCBenchPts, 5, kBenchSurf},
-	{8, kDBenchPts, 5, kBenchSurf}
-};
-
-// Power Suit: triangular prism body + small rectangular pedestal + flat table + hexagonal power source
-// DOS INITOBJ.C: 5 prism parts. Floor=160, so 2*Floor=320.
-static const int kPowerTopPts[3][3] = {{-150, 120, 320}, {150, 120, 320}, {0, -150, 320}};
-static const int kPowerTopSurf[1][8] = {{kColorPower, 3, 0, 1, 2, 0, 0, 0}};
-
-static const int kPowerBottomPts[3][3] = {{-150, 120, 0}, {150, 120, 0}, {0, -150, 0}};
-static const int kPowerBottomSurf[1][8] = {{kColorPower, 3, 2, 1, 0, 0, 0, 0}};
-
-static const int kPowerBasePts[8][3] = {
-	{-5, 100, 0}, {5, 100, 0}, {5, 90, 0}, {-5, 90, 0},
-	{-5, 100, 100}, {5, 100, 100}, {5, 90, 100}, {-5, 90, 100}
-};
-static const int kPowerBaseSurf[4][8] = {
-	{kColorPBase, 4, 0, 3, 7, 4, 0, 0}, {kColorPBase, 4, 3, 2, 6, 7, 0, 0},
-	{kColorPBase, 4, 1, 0, 4, 5, 0, 0}, {kColorPBase, 4, 2, 1, 5, 6, 0, 0}
-};
-
-static const int kPowerTablePts[4][3] = {{-50, 135, 100}, {50, 135, 100}, {50, 55, 100}, {-50, 55, 100}};
-static const int kPowerTableSurf[1][8] = {{kColorBlack, 4, 3, 2, 1, 0, 0, 0}};
-
-static const int kPowerSourcePts[12][3] = {
-	{-75, 0, 290}, {-35, 60, 290}, {35, 60, 290}, {75, 0, 290}, {35, -60, 290}, {-35, -60, 290},
-	{-75, 0, 320}, {-35, 60, 320}, {35, 60, 320}, {75, 0, 320}, {35, -60, 320}, {-35, -60, 320}
-};
-static const int kPowerSourceSurf[7][8] = {
-	{kColorRainbow1, 6, 0, 1, 2, 3, 4, 5},
-	{kColorPSource, 4, 0, 6, 7, 1, 0, 0}, {kColorPSource, 4, 1, 7, 8, 2, 0, 0},
-	{kColorPSource, 4, 2, 8, 9, 3, 0, 0}, {kColorPSource, 4, 3, 9, 10, 4, 0, 0},
-	{kColorPSource, 4, 4, 10, 11, 5, 0, 0}, {kColorPSource, 4, 5, 11, 6, 0, 0, 0}
-};
-
-static const Colony::ColonyEngine::PrismPartDef kPowerSuitParts[5] = {
-	{3, kPowerTopPts, 1, kPowerTopSurf},
-	{3, kPowerBottomPts, 1, kPowerBottomSurf},
-	{8, kPowerBasePts, 4, kPowerBaseSurf},
-	{4, kPowerTablePts, 1, kPowerTableSurf},
-	{12, kPowerSourcePts, 7, kPowerSourceSurf}
-};
-
-// Cryo tube: top (coffin-shaped lid) + base
-static const int kCryoTopPts[16][3] = {
-	{-130,  50,  80}, { 130,  50,  80}, { 130, -50,  80}, {-130, -50,  80},
-	{-130,  50, 140}, { 130,  50, 140}, { 130, -50, 140}, {-130, -50, 140},
-	{   0,  50, 140}, {   0, -50, 140},
-	{-140,  70, 110}, { 140,  70, 110}, { 140, -70, 110}, {-140, -70, 110},
-	{   0,  70, 110}, {   0, -70, 110}
-};
-static const int kCryoTopSurf[12][8] = {
-	{kColorCryo,      4, 7, 9, 8, 4, 0, 0},
-	{kColorCryoGlass, 4, 9, 6, 5, 8, 0, 0},
-	{kColorCryo,      4, 0, 10, 11, 1, 0, 0},
-	{kColorCryo,      4, 1, 11, 12, 2, 0, 0},
-	{kColorCryo,      4, 2, 12, 13, 3, 0, 0},
-	{kColorCryo,      4, 3, 13, 10, 0, 0, 0},
-	{kColorCryo,      4, 7, 13, 15, 9, 0, 0},
-	{kColorCryo,      4, 4, 10, 13, 7, 0, 0},
-	{kColorCryo,      4, 14, 10, 4, 8, 0, 0},
-	{kColorSilver,    4, 5, 11, 14, 8, 0, 0},
-	{kColorSilver,    4, 6, 12, 11, 5, 0, 0},
-	{kColorSilver,    4, 9, 15, 12, 6, 0, 0}
-};
-static const int kCryoBasePts[8][3] = {
-	{-130,  50,  0}, { 130,  50,  0}, { 130, -50,  0}, {-130, -50,  0},
-	{-130,  50, 80}, { 130,  50, 80}, { 130, -50, 80}, {-130, -50, 80}
-};
-static const int kCryoBaseSurf[5][8] = {
-	{kColorCryoBase, 4, 0, 3, 7, 4, 0, 0}, {kColorCryoBase, 4, 3, 2, 6, 7, 0, 0},
-	{kColorCryoBase, 4, 1, 0, 4, 5, 0, 0}, {kColorCryoBase, 4, 2, 1, 5, 6, 0, 0},
-	{kColorCryo,     4, 7, 6, 5, 4, 0, 0}
-};
-static const Colony::ColonyEngine::PrismPartDef kCryoParts[2] = {
-	{16, kCryoTopPts, 12, kCryoTopSurf},
-	{8, kCryoBasePts, 5, kCryoBaseSurf}
-};
-
-// Forklift: cab + treads + upper-left arm + lower-left fork + upper-right arm + lower-right fork
-static const int kFLCabPts[14][3] = {
-	{-35, 60, 40}, {35, 60, 40}, {35, -60, 40}, {-35, -60, 40},
-	{-15, 60, 260}, {15, 60, 260}, {15, -60, 260}, {-15, -60, 260},
-	{25, 60, 140}, {25, -60, 140},
-	{70, 35, 120}, {70, -35, 120},
-	{-70, 40, 80}, {-70, -40, 80}
-};
-static const int kFLCabSurf[12][8] = {
-	{kColorForklift, 4, 0, 3, 13, 12, 0, 0}, {kColorForklift, 4, 12, 13, 7, 4, 0, 0},
-	{kColorForklift, 3, 0, 12, 4, 0, 0, 0},  {kColorForklift, 3, 3, 7, 13, 0, 0, 0},
-	{kColorForklift, 4, 3, 2, 6, 7, 0, 0},   {kColorForklift, 4, 1, 0, 4, 5, 0, 0},
-	{kColorForklift, 3, 1, 8, 10, 0, 0, 0},  {kColorForklift, 3, 2, 11, 9, 0, 0, 0},
-	{kColorForklift, 4, 1, 10, 11, 2, 0, 0},
-	{kColorSilver,   3, 8, 5, 10, 0, 0, 0},  {kColorSilver, 3, 11, 6, 9, 0, 0, 0},
-	{kColorSilver,   4, 10, 5, 6, 11, 0, 0}
-};
-static const int kFLTreadPts[12][3] = {
-	{-60, 60, 20}, {60, 60, 20}, {60, -60, 20}, {-60, -60, 20},
-	{-35, 60, 40}, {35, 60, 40}, {35, -60, 40}, {-35, -60, 40},
-	{-35, 60, 0}, {35, 60, 0}, {35, -60, 0}, {-35, -60, 0}
-};
-static const int kFLTreadSurf[6][8] = {
-	{kColorTread1, 4, 0, 3, 7, 4, 0, 0},
-	{kColorTread2, 6, 3, 11, 10, 2, 6, 7},
-	{kColorTread2, 6, 0, 4, 5, 1, 9, 8},
-	{kColorTread1, 4, 2, 1, 5, 6, 0, 0},
-	{kColorTread1, 4, 0, 8, 11, 3, 0, 0},
-	{kColorTread1, 4, 10, 9, 1, 2, 0, 0}
-};
-static const int kFLULPts[8][3] = {
-	{-15, 70, 120}, {15, 70, 120}, {15, 60, 120}, {-15, 60, 120},
-	{-25, 70, 230}, {25, 70, 230}, {25, 60, 230}, {-25, 60, 230}
-};
-static const int kFLArmSurf[4][8] = {
-	{kColorForklift, 4, 0, 3, 7, 4, 0, 0}, {kColorForklift, 4, 3, 2, 6, 7, 0, 0},
-	{kColorForklift, 4, 1, 0, 4, 5, 0, 0}, {kColorForklift, 4, 2, 1, 5, 6, 0, 0}
-};
-static const int kFLLLPts[8][3] = {
-	{-15, 80, 120}, {100, 80, 125}, {100, 70, 125}, {-15, 70, 120},
-	{-15, 80, 150}, {100, 80, 140}, {100, 70, 140}, {-15, 70, 150}
-};
-static const int kFLForkSurf[6][8] = {
-	{kColorForklift, 4, 0, 3, 7, 4, 0, 0}, {kColorForklift, 4, 3, 2, 6, 7, 0, 0},
-	{kColorForklift, 4, 1, 0, 4, 5, 0, 0}, {kColorBlack,    4, 2, 1, 5, 6, 0, 0},
-	{kColorForklift, 4, 7, 6, 5, 4, 0, 0}, {kColorForklift, 4, 0, 1, 2, 3, 0, 0}
-};
-static const int kFLURPts[8][3] = {
-	{-15, -60, 120}, {15, -60, 120}, {15, -70, 120}, {-15, -70, 120},
-	{-25, -60, 230}, {25, -60, 230}, {25, -70, 230}, {-25, -70, 230}
-};
-static const int kFLLRPts[8][3] = {
-	{-15, -70, 120}, {100, -70, 125}, {100, -80, 125}, {-15, -80, 120},
-	{-15, -70, 150}, {100, -70, 140}, {100, -80, 140}, {-15, -80, 150}
-};
-static const Colony::ColonyEngine::PrismPartDef kForkliftParts[6] = {
-	{14, kFLCabPts, 12, kFLCabSurf},
-	{12, kFLTreadPts, 6, kFLTreadSurf},
-	{8, kFLULPts, 4, kFLArmSurf},
-	{8, kFLLLPts, 6, kFLForkSurf},
-	{8, kFLURPts, 4, kFLArmSurf},
-	{8, kFLLRPts, 6, kFLForkSurf}
-};
-
-// Teleport: octagonal booth with flared middle
-static const int kTelePts[24][3] = {
-	// Ring 0: outer flared ring at z=140
-	{   0, 175, 140}, { 125, 125, 140}, { 175,   0, 140}, { 125,-125, 140},
-	{   0,-175, 140}, {-125,-125, 140}, {-175,   0, 140}, {-125, 125, 140},
-	// Ring 1: inner ring at z=0 (bottom)
-	{  0,  80, 0}, { 65,  65, 0}, { 80,   0, 0}, { 65, -65, 0},
-	{  0, -80, 0}, {-65, -65, 0}, {-80,   0, 0}, {-65,  65, 0},
-	// Ring 2: inner ring at z=280 (top)
-	{  0,  80, 280}, { 65,  65, 280}, { 80,   0, 280}, { 65, -65, 280},
-	{  0, -80, 280}, {-65, -65, 280}, {-80,   0, 280}, {-65,  65, 280}
-};
-static const int kTeleSurf[16][8] = {
-	// Bottom 8 panels (outer mid to inner bottom)
-	{kColorTeleDoor, 4, 0, 1, 9, 8, 0, 0},
-	{kColorTele,     4, 1, 2, 10, 9, 0, 0}, {kColorTele, 4, 2, 3, 11, 10, 0, 0},
-	{kColorTele,     4, 3, 4, 12, 11, 0, 0}, {kColorTele, 4, 4, 5, 13, 12, 0, 0},
-	{kColorTele,     4, 5, 6, 14, 13, 0, 0}, {kColorTele, 4, 6, 7, 15, 14, 0, 0},
-	{kColorTele,     4, 7, 0, 8, 15, 0, 0},
-	// Top 8 panels (outer mid to inner top)
-	{kColorSilver,   4, 1, 0, 16, 17, 0, 0},
-	{kColorTele,     4, 2, 1, 17, 18, 0, 0}, {kColorTele, 4, 3, 2, 18, 19, 0, 0},
-	{kColorTele,     4, 4, 3, 19, 20, 0, 0}, {kColorTele, 4, 5, 4, 20, 21, 0, 0},
-	{kColorTele,     4, 6, 5, 21, 22, 0, 0}, {kColorTele, 4, 7, 6, 22, 23, 0, 0},
-	{kColorTele,     4, 0, 7, 23, 16, 0, 0}
-};
-static const Colony::ColonyEngine::PrismPartDef kTelePart = {24, kTelePts, 16, kTeleSurf};
-
-// Projector: body + stand + lens (sits on table)
-static const int kProjectorPts[8][3] = {
-	{-30, 30, 140}, {30, 30, 140}, {30, -30, 140}, {-30, -30, 140},
-	{-20, 30, 160}, {30, 30, 160}, {30, -30, 160}, {-20, -30, 160}
-};
-static const int kProjectorSurf[5][8] = {
-	{kColorProjector, 4, 0, 3, 7, 4, 0, 0}, {kColorProjector, 4, 3, 2, 6, 7, 0, 0},
-	{kColorProjector, 4, 1, 0, 4, 5, 0, 0}, {kColorProjector, 4, 2, 1, 5, 6, 0, 0},
-	{kColorProjector, 4, 7, 6, 5, 4, 0, 0}
-};
-static const int kPStandPts[4][3] = {
-	{-25, 50, 100}, {0, 10, 140}, {0, -10, 140}, {-25, -50, 100}
-};
-static const int kPStandSurf[1][8] = {{kColorPStand, 4, 0, 1, 2, 3, 0, 0}};
-static const int kPLensPts[12][3] = {
-	{30,  8, 154}, {30,  0, 158}, {30, -8, 154}, {30, -8, 146}, {30,  0, 142}, {30,  8, 146},
-	{55, 10, 155}, {55,  0, 160}, {55,-10, 155}, {55,-10, 145}, {55,  0, 140}, {55, 10, 145}
-};
-static const int kPLensSurf[7][8] = {
-	{kColorPLens, 4, 0, 1, 7, 6, 0, 0}, {kColorPLens, 4, 1, 2, 8, 7, 0, 0},
-	{kColorPLens, 4, 2, 3, 9, 8, 0, 0}, {kColorPLens, 4, 3, 4, 10, 9, 0, 0},
-	{kColorPLens, 4, 4, 5, 11, 10, 0, 0}, {kColorPLens, 4, 5, 0, 6, 11, 0, 0},
-	{kColorBlack, 6, 6, 7, 8, 9, 10, 11}
-};
-static const Colony::ColonyEngine::PrismPartDef kProjectorParts[3] = {
-	{8, kProjectorPts, 5, kProjectorSurf},
-	{4, kPStandPts, 1, kPStandSurf},
-	{12, kPLensPts, 7, kPLensSurf}
-};
-
-// ============================================================================
-// Robot geometry data (from DOS PYRAMID.H, CUBE.H, EYE.H, UPYRAMID.H,
-// QUEEN.H, DRONE.H, SNOOP.H)
-// ============================================================================
-
-// --- Pyramid (type 2) ---
-static const int kPyramidPts[5][3] = {
-	{-75, 75, 30}, {75, 75, 30}, {75, -75, 30}, {-75, -75, 30}, {0, 0, 200}
-};
-static const int kPyramidSurf[4][8] = {
-	{kColorPyramid, 3, 1, 0, 4, 1, 0, 0}, {kColorPyramid, 3, 2, 1, 4, 2, 0, 0},
-	{kColorPyramid, 3, 3, 2, 4, 3, 0, 0}, {kColorPyramid, 3, 0, 3, 4, 0, 0, 0}
-};
-static const int kPShadowPts[4][3] = {
-	{-75, 75, 0}, {75, 75, 0}, {75, -75, 0}, {-75, -75, 0}
-};
-static const int kPShadowSurf[1][8] = {{kColorShadow, 4, 3, 2, 1, 0, 3, 0}};
-// Pyramid eye (ball on top)
-static const int kPIrisPts[4][3] = {
-	{15, 0, 185}, {15, 15, 200}, {15, 0, 215}, {15, -15, 200}
-};
-static const int kPIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
-static const int kPPupilPts[4][3] = {
-	{16, 0, 194}, {16, 6, 200}, {16, 0, 206}, {16, -6, 200}
-};
-static const int kPPupilSurf[1][8] = {{kColorPupil, 4, 0, 1, 2, 3, 0, 0}};
-
-static const Colony::ColonyEngine::PrismPartDef kPyramidBodyDef = {5, kPyramidPts, 4, kPyramidSurf};
-static const Colony::ColonyEngine::PrismPartDef kPShadowDef = {4, kPShadowPts, 1, kPShadowSurf};
-static const Colony::ColonyEngine::PrismPartDef kPIrisDef = {4, kPIrisPts, 1, kPIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kPPupilDef = {4, kPPupilPts, 1, kPPupilSurf};
-
-// --- Cube (type 3) --- (octahedron)
-static const int kCubePts[6][3] = {
-	{0, 0, 200}, {100, 0, 100}, {0, 100, 100}, {-100, 0, 100}, {0, -100, 100}, {0, 0, 0}
-};
-static const int kCubeSurf[8][8] = {
-	{kColorCube, 3, 0, 1, 2, 0, 0, 0}, {kColorCube, 3, 0, 2, 3, 0, 0, 0},
-	{kColorCube, 3, 0, 3, 4, 0, 0, 0}, {kColorCube, 3, 0, 4, 1, 0, 0, 0},
-	{kColorCube, 3, 5, 2, 1, 5, 0, 0}, {kColorCube, 3, 5, 3, 2, 5, 0, 0},
-	{kColorCube, 3, 5, 4, 3, 5, 0, 0}, {kColorCube, 3, 5, 1, 4, 5, 0, 0}
-};
-static const Colony::ColonyEngine::PrismPartDef kCubeBodyDef = {6, kCubePts, 8, kCubeSurf};
-
-// --- UPyramid (type 4) --- (inverted pyramid)
-static const int kUPyramidPts[5][3] = {
-	{-75, 75, 190}, {75, 75, 190}, {75, -75, 190}, {-75, -75, 190}, {0, 0, 30}
-};
-static const int kUPyramidSurf[5][8] = {
-	{kColorUPyramid, 3, 0, 1, 4, 0, 0, 0}, {kColorUPyramid, 3, 1, 2, 4, 1, 0, 0},
-	{kColorUPyramid, 3, 2, 3, 4, 2, 0, 0}, {kColorUPyramid, 3, 3, 0, 4, 3, 0, 0},
-	{kColorUPyramid, 4, 3, 2, 1, 0, 3, 0}
-};
-static const int kUPShadowPts[4][3] = {
-	{-25, 25, 0}, {25, 25, 0}, {25, -25, 0}, {-25, -25, 0}
-};
-static const int kUPShadowSurf[1][8] = {{kColorShadow, 4, 3, 2, 1, 0, 3, 0}};
-
-static const Colony::ColonyEngine::PrismPartDef kUPyramidBodyDef = {5, kUPyramidPts, 5, kUPyramidSurf};
-static const Colony::ColonyEngine::PrismPartDef kUPShadowDef = {4, kUPShadowPts, 1, kUPShadowSurf};
-
-// --- Eye (type 1) ---
-// Ball is rendered by draw3DSphere(), not as a prism
-static const int kEyeIrisPts[4][3] = {
-	{60, 0, 140}, {60, 60, 200}, {60, 0, 260}, {60, -60, 200}
-};
-static const int kEyeIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
-static const int kEyePupilPts[4][3] = {
-	{66, 0, 175}, {66, 25, 200}, {66, 0, 225}, {66, -25, 200}
-};
-static const int kEyePupilSurf[1][8] = {{kColorBlack, 4, 0, 1, 2, 3, 0, 0}};
-
-static const Colony::ColonyEngine::PrismPartDef kEyeIrisDef = {4, kEyeIrisPts, 1, kEyeIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kEyePupilDef = {4, kEyePupilPts, 1, kEyePupilSurf};
-
-// --- Floating Pyramid (type 6) --- egg on ground
-static const int kFPyramidPts[5][3] = {
-	{-75, 75, 0}, {75, 75, 0}, {75, -75, 0}, {-75, -75, 0}, {0, 0, 170}
-};
-static const Colony::ColonyEngine::PrismPartDef kFPyramidBodyDef = {5, kFPyramidPts, 4, kPyramidSurf};
-
-// --- Small Pyramid (type 10) ---
-static const int kSPyramidPts[5][3] = {
-	{-40, 40, 0}, {40, 40, 0}, {40, -40, 0}, {-40, -40, 0}, {0, 0, 100}
-};
-static const Colony::ColonyEngine::PrismPartDef kSPyramidBodyDef = {5, kSPyramidPts, 4, kPyramidSurf};
-
-// --- Mini Pyramid (type 14) ---
-static const int kMPyramidPts[5][3] = {
-	{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0}, {0, 0, 50}
-};
-static const Colony::ColonyEngine::PrismPartDef kMPyramidBodyDef = {5, kMPyramidPts, 4, kPyramidSurf};
-
-// --- Floating Cube (type 7) ---
-static const int kFCubePts[6][3] = {
-	{0, 0, 150}, {75, 0, 75}, {0, 75, 75}, {-75, 0, 75}, {0, -75, 75}, {0, 0, 0}
-};
-static const Colony::ColonyEngine::PrismPartDef kFCubeBodyDef = {6, kFCubePts, 8, kCubeSurf};
-
-// --- Small Cube (type 11) ---
-static const int kSCubePts[6][3] = {
-	{0, 0, 100}, {50, 0, 50}, {0, 50, 50}, {-50, 0, 50}, {0, -50, 50}, {0, 0, 0}
-};
-static const Colony::ColonyEngine::PrismPartDef kSCubeBodyDef = {6, kSCubePts, 8, kCubeSurf};
-
-// --- Mini Cube (type 15) ---
-static const int kMCubePts[6][3] = {
-	{0, 0, 50}, {25, 0, 25}, {0, 25, 25}, {-25, 0, 25}, {0, -25, 25}, {0, 0, 0}
-};
-static const Colony::ColonyEngine::PrismPartDef kMCubeBodyDef = {6, kMCubePts, 8, kCubeSurf};
-
-// --- Floating UPyramid (type 8) ---
-static const int kFUPyramidPts[5][3] = {
-	{-75, 75, 170}, {75, 75, 170}, {75, -75, 170}, {-75, -75, 170}, {0, 0, 0}
-};
-static const Colony::ColonyEngine::PrismPartDef kFUPyramidBodyDef = {5, kFUPyramidPts, 5, kUPyramidSurf};
-
-// --- Small UPyramid (type 12) ---
-static const int kSUPyramidPts[5][3] = {
-	{-40, 40, 100}, {40, 40, 100}, {40, -40, 100}, {-40, -40, 100}, {0, 0, 0}
-};
-static const Colony::ColonyEngine::PrismPartDef kSUPyramidBodyDef = {5, kSUPyramidPts, 5, kUPyramidSurf};
-
-// --- Mini UPyramid (type 16) ---
-static const int kMUPyramidPts[5][3] = {
-	{-20, 20, 50}, {20, 20, 50}, {20, -20, 50}, {-20, -20, 50}, {0, 0, 0}
-};
-static const Colony::ColonyEngine::PrismPartDef kMUPyramidBodyDef = {5, kMUPyramidPts, 5, kUPyramidSurf};
-
-// --- Floating Eye (type 5) ---
-static const int kFEyeIrisPts[4][3] = {
-	{60, 0, 40}, {60, 60, 100}, {60, 0, 160}, {60, -60, 100}
-};
-static const int kFEyePupilPts[4][3] = {
-	{66, 0, 75}, {66, 25, 100}, {66, 0, 125}, {66, -25, 100}
-};
-static const Colony::ColonyEngine::PrismPartDef kFEyeIrisDef = {4, kFEyeIrisPts, 1, kEyeIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kFEyePupilDef = {4, kFEyePupilPts, 1, kEyePupilSurf};
-
-// --- Small Eye (type 9) ---
-static const int kSEyeIrisPts[4][3] = {
-	{30, 0, 20}, {30, 30, 50}, {30, 0, 80}, {30, -30, 50}
-};
-static const int kSEyePupilPts[4][3] = {
-	{33, 0, 38}, {33, 13, 50}, {33, 0, 63}, {33, -13, 50}
-};
-static const Colony::ColonyEngine::PrismPartDef kSEyeIrisDef = {4, kSEyeIrisPts, 1, kEyeIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kSEyePupilDef = {4, kSEyePupilPts, 1, kEyePupilSurf};
-
-// --- Mini Eye (type 13) ---
-static const int kMEyeIrisPts[4][3] = {
-	{15, 0, 10}, {15, 15, 25}, {15, 0, 40}, {15, -15, 25}
-};
-static const int kMEyePupilPts[4][3] = {
-	{16, 0, 19}, {16, 6, 25}, {16, 0, 31}, {16, -6, 25}
-};
-static const Colony::ColonyEngine::PrismPartDef kMEyeIrisDef = {4, kMEyeIrisPts, 1, kEyeIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kMEyePupilDef = {4, kMEyePupilPts, 1, kEyePupilSurf};
-
-// --- Queen (type 17) ---
-// Queen eye (ball rendered by draw3DSphere)
-static const int kQIrisPts[4][3] = {
-	{15, 0, 140}, {15, 15, 155}, {15, 0, 170}, {15, -15, 155}
-};
-static const int kQIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
-static const int kQPupilPts[4][3] = {
-	{16, 0, 148}, {16, 6, 155}, {16, 0, 161}, {16, -6, 155}
-};
-static const int kQPupilSurf[1][8] = {{kColorPupil, 4, 0, 1, 2, 3, 0, 0}};
-// Queen abdomen
-static const int kQAbdomenPts[9][3] = {
-	{120, 0, 130}, {30, 0, 160}, {30, 0, 100}, {30, 50, 130}, {30, -50, 130},
-	{0, 0, 150}, {0, 0, 110}, {0, 25, 130}, {0, -25, 130}
-};
-static const int kQAbdomenSurf[9][8] = {
-	{kColorQueen, 3, 0, 3, 1, 0, 0, 0}, {kColorQueen, 3, 0, 1, 4, 0, 0, 0},
-	{kColorQueen, 3, 0, 2, 3, 0, 0, 0}, {kColorQueen, 3, 0, 4, 2, 0, 0, 0},
-	{kColorQueen, 4, 1, 5, 8, 4, 1, 0}, {kColorQueen, 4, 1, 3, 7, 5, 1, 0},
-	{kColorQueen, 4, 2, 4, 8, 6, 2, 0}, {kColorQueen, 4, 2, 6, 7, 3, 2, 0},
-	{kColorClear, 4, 5, 7, 6, 8, 5, 0}
-};
-// Queen thorax
-static const int kQThoraxPts[9][3] = {
-	{-120, 0, 130}, {-50, 0, 170}, {-50, 0, 90}, {-50, 60, 130}, {-50, -60, 130},
-	{0, 0, 150}, {0, 0, 110}, {0, 25, 130}, {0, -25, 130}
-};
-static const int kQThoraxSurf[8][8] = {
-	{kColorQueen, 3, 0, 1, 3, 0, 0, 0}, {kColorQueen, 3, 0, 4, 1, 0, 0, 0},
-	{kColorQueen, 3, 0, 3, 2, 0, 0, 0}, {kColorQueen, 3, 0, 2, 4, 0, 0, 0},
-	{kColorQueen, 4, 1, 4, 8, 5, 1, 0}, {kColorQueen, 4, 1, 5, 7, 3, 1, 0},
-	{kColorQueen, 4, 2, 6, 8, 4, 2, 0}, {kColorQueen, 4, 2, 3, 7, 6, 2, 0}
-};
-// Queen wings
-static const int kQLWingPts[4][3] = {
-	{80, 0, 140}, {-40, 10, 200}, {-120, 60, 170}, {-40, 120, 140}
-};
-static const int kQLWingSurf[1][8] = {{kColorClear, 4, 0, 1, 2, 3, 0, 0}};
-static const int kQRWingPts[4][3] = {
-	{80, 0, 140}, {-40, -10, 200}, {-120, -60, 170}, {-40, -120, 140}
-};
-static const int kQRWingSurf[1][8] = {{kColorClear, 4, 0, 1, 2, 3, 0, 0}};
-
-static const Colony::ColonyEngine::PrismPartDef kQIrisDef = {4, kQIrisPts, 1, kQIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kQPupilDef = {4, kQPupilPts, 1, kQPupilSurf};
-static const Colony::ColonyEngine::PrismPartDef kQAbdomenDef = {9, kQAbdomenPts, 9, kQAbdomenSurf};
-static const Colony::ColonyEngine::PrismPartDef kQThoraxDef = {9, kQThoraxPts, 8, kQThoraxSurf};
-static const Colony::ColonyEngine::PrismPartDef kQLWingDef = {4, kQLWingPts, 1, kQLWingSurf};
-static const Colony::ColonyEngine::PrismPartDef kQRWingDef = {4, kQRWingPts, 1, kQRWingSurf};
-
-// --- Drone / Soldier (types 18, 19) ---
-static const int kDAbdomenPts[6][3] = {
-	{0, 0, 170}, {120, 0, 130}, {0, 100, 130}, {-130, 0, 130}, {0, -100, 130}, {0, 0, 100}
-};
-static const int kDAbdomenSurf[8][8] = {
-	{kColorDrone, 3, 0, 1, 2, 0, 0, 0}, {kColorDrone, 3, 0, 2, 3, 0, 0, 0},
-	{kColorDrone, 3, 0, 3, 4, 0, 0, 0}, {kColorDrone, 3, 0, 4, 1, 0, 0, 0},
-	{kColorDrone, 3, 5, 2, 1, 5, 0, 0}, {kColorDrone, 3, 5, 3, 2, 5, 0, 0},
-	{kColorDrone, 3, 5, 4, 3, 5, 0, 0}, {kColorDrone, 3, 5, 1, 4, 5, 0, 0}
-};
-// Drone static pincers (llPincer/rrPincer)
-static const int kDLLPincerPts[4][3] = {
-	{0, 0, 130}, {50, -2, 130}, {35, -20, 140}, {35, -20, 120}
-};
-static const int kDLPincerSurf[4][8] = {
-	{kColorClaw1, 3, 0, 2, 1, 0, 0, 0}, {kColorClaw1, 3, 0, 1, 3, 0, 0, 0},
-	{kColorClaw2, 3, 0, 3, 2, 0, 0, 0}, {kColorClaw2, 3, 1, 2, 3, 1, 0, 0}
-};
-static const int kDRRPincerPts[4][3] = {
-	{0, 0, 130}, {50, 2, 130}, {35, 20, 140}, {35, 20, 120}
-};
-static const int kDRPincerSurf[4][8] = {
-	{kColorClaw1, 3, 0, 1, 2, 0, 0, 0}, {kColorClaw1, 3, 0, 3, 1, 0, 0, 0},
-	{kColorClaw2, 3, 0, 2, 3, 0, 0, 0}, {kColorClaw2, 3, 1, 3, 2, 1, 0, 0}
-};
-// Drone eyes
-static const int kDLEyePts[3][3] = {
-	{60, 0, 150}, {60, 50, 130}, {60, 25, 150}
-};
-static const int kDLEyeSurf[2][8] = {
-	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
-};
-static const int kDREyePts[3][3] = {
-	{60, 0, 150}, {60, -50, 130}, {60, -25, 150}
-};
-static const int kDREyeSurf[2][8] = {
-	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
-};
-
-static const Colony::ColonyEngine::PrismPartDef kDAbdomenDef = {6, kDAbdomenPts, 8, kDAbdomenSurf};
-static const Colony::ColonyEngine::PrismPartDef kDLLPincerDef = {4, kDLLPincerPts, 4, kDLPincerSurf};
-static const Colony::ColonyEngine::PrismPartDef kDRRPincerDef = {4, kDRRPincerPts, 4, kDRPincerSurf};
-static const Colony::ColonyEngine::PrismPartDef kDLEyeDef = {3, kDLEyePts, 2, kDLEyeSurf};
-static const Colony::ColonyEngine::PrismPartDef kDREyeDef = {3, kDREyePts, 2, kDREyeSurf};
-
-// --- Snoop (type 20) ---
-static const int kSnoopAbdomenPts[4][3] = {
-	{0, 100, 0}, {-180, 0, 0}, {0, -100, 0}, {0, 0, 70}
-};
-static const int kSnoopAbdomenSurf[2][8] = {
-	{kColorTopSnoop, 3, 0, 1, 3, 0, 0, 0}, {kColorTopSnoop, 3, 2, 3, 1, 2, 0, 0}
-};
-static const int kSnoopHeadPts[4][3] = {
-	{0, 100, 0}, {150, 0, 0}, {0, -100, 0}, {0, 0, 70}
-};
-static const int kSnoopHeadSurf[3][8] = {
-	{kColorTopSnoop, 3, 0, 3, 1, 0, 0, 0}, {kColorTopSnoop, 3, 2, 1, 3, 2, 0, 0},
-	{kColorBottomSnoop, 3, 0, 1, 2, 0, 0, 0}
-};
-
-static const Colony::ColonyEngine::PrismPartDef kSnoopAbdomenDef = {4, kSnoopAbdomenPts, 2, kSnoopAbdomenSurf};
-static const Colony::ColonyEngine::PrismPartDef kSnoopHeadDef = {4, kSnoopHeadPts, 3, kSnoopHeadSurf};
-
-
 void ColonyEngine::quadrant() {
 	int remain;
 	int quad;
@@ -1351,13 +328,6 @@ static bool projectCorridorPoint(const Common::Rect &screenR, uint8 look, int8 l
 	return true;
 }
 
-static void resetObjectBounds(const Common::Rect &screenR, Locate &loc) {
-	loc.xmn = screenR.right;
-	loc.xmx = screenR.left;
-	loc.zmn = screenR.bottom;
-	loc.zmx = screenR.top;
-}
-
 void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride, bool accumulateBounds) {
 	// +32 compensates for the original sine table's 45° phase offset.
 	// Object angles from game data were stored assuming that offset.
@@ -1749,980 +719,8 @@ void ColonyEngine::computeVisibleCells() {
 	}
 }
 
-void ColonyEngine::drawStaticObjects() {
-	for (uint i = 0; i < _objects.size(); i++) {
-		Thing &obj = _objects[i];
-		if (!obj.alive)
-			continue;
-		resetObjectBounds(_screenR, obj.where);
-		int ox = obj.where.xindex;
-		int oy = obj.where.yindex;
-		if (ox < 0 || ox >= 32 || oy < 0 || oy >= 32 || !_visibleCell[ox][oy])
-			continue;
-		drawStaticObjectPrisms3D(obj);
-	}
-}
-
-// Get the 4 corners of a wall face in 3D world space.
-// corners[0] = bottom-left, corners[1] = bottom-right, corners[2] = top-right, corners[3] = top-left
-// "left" and "right" are as seen from the cell that owns the feature.
-void ColonyEngine::getWallFace3D(int cellX, int cellY, int direction, float corners[4][3]) {
-	float x0 = cellX * 256.0f;
-	float y0 = cellY * 256.0f;
-	float x1 = (cellX + 1) * 256.0f;
-	float y1 = (cellY + 1) * 256.0f;
-	const float zBot = -160.0f;
-	const float zTop = 160.0f;
-	// Pull slightly into the cell to prevent z-fighting
-	const float eps = 1.0f;
-
-	switch (direction) {
-	case kDirNorth: // Wall at y1 (+Y); viewed from inside (looking North)
-		corners[0][0] = x0;  corners[0][1] = y1 - eps;  corners[0][2] = zBot; // BL
-		corners[1][0] = x1;  corners[1][1] = y1 - eps;  corners[1][2] = zBot; // BR
-		corners[2][0] = x1;  corners[2][1] = y1 - eps;  corners[2][2] = zTop; // TR
-		corners[3][0] = x0;  corners[3][1] = y1 - eps;  corners[3][2] = zTop; // TL
-		break;
-	case kDirSouth: // Wall at y0 (-Y); viewed from inside (looking South)
-		corners[0][0] = x1;  corners[0][1] = y0 + eps;  corners[0][2] = zBot; // BL
-		corners[1][0] = x0;  corners[1][1] = y0 + eps;  corners[1][2] = zBot; // BR
-		corners[2][0] = x0;  corners[2][1] = y0 + eps;  corners[2][2] = zTop; // TR
-		corners[3][0] = x1;  corners[3][1] = y0 + eps;  corners[3][2] = zTop; // TL
-		break;
-	case kDirEast: // Wall at x1 (+X); viewed from inside (looking East)
-		corners[0][0] = x1 - eps;  corners[0][1] = y1;  corners[0][2] = zBot; // BL
-		corners[1][0] = x1 - eps;  corners[1][1] = y0;  corners[1][2] = zBot; // BR
-		corners[2][0] = x1 - eps;  corners[2][1] = y0;  corners[2][2] = zTop; // TR
-		corners[3][0] = x1 - eps;  corners[3][1] = y1;  corners[3][2] = zTop; // TL
-		break;
-	case kDirWest: // Wall at x0 (-X); viewed from inside (looking West)
-		corners[0][0] = x0 + eps;  corners[0][1] = y0;  corners[0][2] = zBot; // BL
-		corners[1][0] = x0 + eps;  corners[1][1] = y1;  corners[1][2] = zBot; // BR
-		corners[2][0] = x0 + eps;  corners[2][1] = y1;  corners[2][2] = zTop; // TR
-		corners[3][0] = x0 + eps;  corners[3][1] = y0;  corners[3][2] = zTop; // TL
-		break;
-	default:
-		return;
-	}
-}
-
-// Interpolate a point on the wall face.
-// u: 0=left, 1=right (horizontal fraction)
-// v: 0=bottom, 1=top (vertical fraction)
-static void wallPoint(const float corners[4][3], float u, float v, float out[3]) {
-	float botX = corners[0][0] + (corners[1][0] - corners[0][0]) * u;
-	float botY = corners[0][1] + (corners[1][1] - corners[0][1]) * u;
-	float botZ = corners[0][2] + (corners[1][2] - corners[0][2]) * u;
-	float topX = corners[3][0] + (corners[2][0] - corners[3][0]) * u;
-	float topY = corners[3][1] + (corners[2][1] - corners[3][1]) * u;
-	float topZ = corners[3][2] + (corners[2][2] - corners[3][2]) * u;
-	out[0] = botX + (topX - botX) * v;
-	out[1] = botY + (topY - botY) * v;
-	out[2] = botZ + (topZ - botZ) * v;
-}
-
-// Draw a line on a wall face using normalized (u,v) coordinates
-void ColonyEngine::wallLine(const float corners[4][3], float u1, float v1, float u2, float v2, uint32 color) {
-	float p1[3], p2[3];
-	wallPoint(corners, u1, v1, p1);
-	wallPoint(corners, u2, v2, p2);
-	// We assume this is only called when lit (handled in drawWallFeatures3D)
-	_gfx->draw3DLine(p1[0], p1[1], p1[2], p2[0], p2[1], p2[2], color);
-}
-
-// Draw a filled polygon on a wall face using normalized (u,v) coordinates
-void ColonyEngine::wallPolygon(const float corners[4][3], const float *u, const float *v, int count, uint32 color) {
-	float px[64], py[64], pz[64];
-	if (count > 64)
-		count = 64;
-	for (int i = 0; i < count; i++) {
-		float p[3];
-		wallPoint(corners, u[i], v[i], p);
-		px[i] = p[0]; py[i] = p[1]; pz[i] = p[2];
-	}
-	_gfx->draw3DPolygon(px, py, pz, count, color);
-}
-
-static bool nearlyEqual(float a, float b, float eps = 0.0001f) {
-	return fabsf(a - b) <= eps;
-}
-
-static void addSortedUniqueFloat(float *values, int &count, float value, float eps = 0.0001f) {
-	for (int i = 0; i < count; ++i) {
-		if (nearlyEqual(values[i], value, eps))
-			return;
-		if (value < values[i]) {
-			for (int j = count; j > i; --j)
-				values[j] = values[j - 1];
-			values[i] = value;
-			++count;
-			return;
-		}
-	}
-
-	values[count++] = value;
-}
-
-static bool segmentIntersection2D(float ax, float ay, float bx, float by,
-		float cx, float cy, float dx, float dy, float &ix, float &iy) {
-	const float den = (ax - bx) * (cy - dy) - (ay - by) * (cx - dx);
-	if (fabsf(den) < 0.0001f)
-		return false;
-
-	const float t = ((ax - cx) * (cy - dy) - (ay - cy) * (cx - dx)) / den;
-	const float u = ((ax - cx) * (ay - by) - (ay - cy) * (ax - bx)) / den;
-	if (t <= 0.0001f || t >= 0.9999f || u <= 0.0001f || u >= 0.9999f)
-		return false;
-
-	ix = ax + t * (bx - ax);
-	iy = ay + t * (by - ay);
-	return true;
-}
-
-static float edgeXAtY(float x0, float y0, float x1, float y1, float y) {
-	if (nearlyEqual(y0, y1))
-		return x0;
-
-	const float t = (y - y0) / (y1 - y0);
-	return x0 + (x1 - x0) * t;
-}
-
-struct WallCharSpan {
-	float xMid;
-	float xTop;
-	float xBottom;
-};
-
-static void sortWallCharSpans(WallCharSpan *spans, int count) {
-	for (int i = 1; i < count; ++i) {
-		WallCharSpan span = spans[i];
-		int j = i - 1;
-		while (j >= 0 && spans[j].xMid > span.xMid) {
-			spans[j + 1] = spans[j];
-			--j;
-		}
-		spans[j + 1] = span;
-	}
-}
-
-static const char *const kWallCharData[] = {
-	"\00",
-	"\02\10\02\00\03\00\03\01\02\01\10\02\02\03\02\03\06\02\06",
-	"\02\10\01\04\02\04\02\05\01\05\10\03\04\04\04\04\05\03\05",
-	"\04\10\01\00\02\00\02\05\01\05\10\03\00\04\00\04\05\03\05\10\00\01\05\01\05\02\00\02\10\00\03\05\03\05\04\00\04",
-	"\02\10\02\00\03\00\03\06\02\06\050\00\02\00\01\01\00\04\00\05\01\05\02\01\04\01\05\04\05\04\04\05\04\05\05\04\06\01\06\00\05\00\04\04\02\04\01\01\01\01\02",
-	"\03\10\01\00\06\05\05\06\00\01\032\01\03\02\03\03\04\03\05\02\06\01\06\00\05\00\04\01\03\01\05\02\05\02\04\01\04\032\04\00\05\00\06\01\06\02\05\03\04\03\03\02\03\01\04\00\04\02\05\02\05\01\04\01",
-	"\03\10\05\01\05\02\03\04\02\04\014\02\04\01\05\02\06\01\06\00\05\01\04\032\05\04\03\01\01\01\01\03\03\05\02\06\01\06\02\05\00\03\00\01\01\00\03\00\05\03",
-	"\01\10\02\04\03\05\03\06\02\06",
-	"\01\014\04\00\03\02\03\04\04\06\02\04\02\02",
-	"\01\014\02\00\04\02\04\04\02\06\03\04\03\02",
-	"\06\06\01\00\03\03\00\02\06\02\00\04\00\03\03\06\05\00\06\02\03\03\06\06\04\05\06\03\03\06\02\06\04\06\03\03\06\00\04\01\06\03\03",
-	"\02\10\02\00\03\00\03\05\02\05\10\00\02\05\02\05\03\00\03",
-	"\01\10\02\00\03\01\03\02\02\02",
-	"\01\10\00\02\05\02\05\03\00\03",
-	"\01\10\02\00\03\00\03\01\02\01",
-	"\01\10\01\00\06\05\05\06\00\01",
-	"\02\032\01\00\05\00\06\01\06\05\05\06\01\06\00\05\00\01\01\00\01\05\05\05\05\01\01\01\10\01\01\02\01\05\05\04\05",
-	"\01\026\01\00\04\00\04\01\03\01\03\06\02\06\01\05\01\04\02\04\02\01\01\01",
-	"\01\042\06\00\06\01\02\01\05\02\06\03\06\05\05\06\01\06\00\05\00\04\01\04\01\05\05\05\05\04\01\02\00\01\00\00",
-	"\01\054\00\02\00\01\01\00\05\00\06\01\06\02\05\03\06\04\06\05\05\06\01\06\00\05\00\04\01\04\01\05\05\05\05\04\04\03\05\02\05\01\01\01\01\02",
-	"\01\036\04\00\05\00\05\02\06\02\06\03\05\03\05\06\04\06\04\03\01\03\02\06\01\06\00\03\00\02\04\02",
-	"\01\044\00\02\00\01\01\00\05\00\06\01\06\03\05\04\01\04\02\05\06\05\06\06\01\06\00\04\00\03\05\03\05\01\01\01\01\02",
-	"\01\046\01\02\05\02\05\01\01\01\01\05\05\05\05\04\06\04\06\05\05\06\01\06\00\05\00\01\01\00\05\00\06\01\06\02\05\03\01\03",
-	"\01\020\02\00\03\00\03\03\06\06\00\06\00\05\04\05\02\03",
-	"\02\040\03\00\03\01\01\01\01\02\04\03\01\04\01\05\03\05\03\06\01\06\00\05\00\04\01\03\00\02\00\01\01\00\040\03\00\05\00\06\01\06\02\05\03\06\04\06\05\05\06\03\06\03\05\05\05\05\04\02\03\05\02\05\01\03\01",
-	"\01\046\00\02\00\01\01\00\05\00\06\01\06\05\05\06\01\06\00\05\00\04\01\03\05\03\05\04\01\04\01\05\05\05\05\01\01\01\01\02",
-	"\02\10\02\01\03\01\03\02\02\02\10\02\03\03\03\03\04\02\04",
-	"\02\10\02\00\03\01\03\02\02\02\10\02\03\03\03\03\04\02\04",
-	"\01\014\06\00\06\01\02\03\06\05\06\06\00\03",
-	"\02\10\00\01\05\01\05\02\00\02\10\00\03\05\03\05\04\00\04",
-	"\01\014\00\00\06\03\00\06\00\05\04\03\00\01",
-	"\02\10\02\00\03\00\03\01\02\01\030\02\02\03\02\05\04\05\05\04\06\01\06\00\05\00\04\01\04\01\05\04\05\04\04",
-	"\05\012\04\00\01\01\00\04\00\01\01\00\012\02\00\05\00\06\01\06\04\05\01\012\06\02\06\05\05\06\02\06\05\05\012\04\06\01\06\00\05\00\02\01\05\034\05\01\05\02\03\05\02\05\01\04\01\02\02\01\03\01\05\04\05\05\03\02\02\02\02\04\03\04",
-	"\01\034\03\06\04\06\06\00\05\00\04\02\02\02\01\00\00\00\02\06\03\06\03\05\02\03\04\03\03\05",
-	"\01\050\00\00\00\06\05\06\06\05\06\04\05\03\06\02\06\01\05\00\01\00\01\01\05\01\05\02\04\03\01\03\01\04\05\04\05\05\01\05\01\00",
-	"\01\040\06\02\06\01\05\00\01\00\00\01\00\05\01\06\05\06\06\05\06\04\05\04\05\05\01\05\01\01\05\01\05\02",
-	"\01\034\00\00\00\06\04\06\06\04\06\02\04\00\01\00\01\01\04\01\05\02\05\04\04\05\01\05\01\00",
-	"\01\030\00\00\00\06\06\06\06\05\01\05\01\04\04\04\04\03\01\03\01\01\06\01\06\00",
-	"\01\024\00\00\00\06\06\06\06\05\01\05\01\03\04\03\04\02\01\02\01\00",
-	"\01\044\03\03\06\03\06\01\05\00\01\00\00\01\00\05\01\06\05\06\06\05\06\04\05\04\05\05\01\05\01\01\05\01\05\02\03\02",
-	"\01\030\00\00\00\06\01\06\01\04\05\04\05\06\06\06\06\00\05\00\05\03\01\03\01\00",
-	"\01\030\01\00\01\01\02\01\02\05\01\05\01\06\04\06\04\05\03\05\03\01\04\01\04\00",
-	"\01\034\00\02\00\01\01\00\04\00\05\01\05\05\06\05\06\06\03\06\03\05\04\05\04\01\01\01\01\02",
-	"\01\026\00\00\00\06\01\06\01\04\04\06\06\06\02\03\06\00\04\00\01\02\01\00",
-	"\01\014\00\06\00\00\06\00\06\01\01\01\01\06",
-	"\01\030\00\00\00\06\01\06\03\04\05\06\06\06\06\00\05\00\05\04\03\03\01\04\01\00",
-	"\01\024\00\00\00\06\02\06\05\01\05\06\06\06\06\00\04\00\01\05\01\00",
-	"\01\032\00\01\00\05\01\06\05\06\06\05\06\01\05\00\01\00\00\01\05\01\05\05\01\05\01\01",
-	"\01\030\00\00\00\06\05\06\06\05\06\03\05\02\01\02\01\03\05\03\05\05\01\05\01\00",
-	"\02\036\04\00\01\00\00\01\00\05\01\06\05\06\06\05\06\02\04\00\04\02\05\02\05\05\01\05\01\01\04\01\014\06\00\06\01\05\02\04\02\04\01\05\00",
-	"\01\036\00\00\00\06\05\06\06\05\06\03\05\02\06\00\05\00\04\02\01\02\01\03\05\03\05\05\01\05\01\00",
-	"\01\054\00\02\00\01\01\00\05\00\06\01\06\02\05\03\01\04\01\05\05\05\05\04\06\04\06\05\05\06\01\06\00\05\00\04\01\03\05\02\05\01\01\01\01\02",
-	"\01\020\02\00\02\05\00\05\00\06\05\06\05\05\03\05\03\00",
-	"\01\024\00\06\00\01\01\00\05\00\06\01\06\06\05\06\05\01\01\01\01\06",
-	"\01\016\00\06\02\00\04\00\06\06\05\06\03\01\01\06",
-	"\01\030\00\06\01\00\02\00\03\02\04\00\05\00\06\06\05\06\04\02\03\04\02\02\01\06",
-	"\01\030\00\00\02\03\00\06\01\06\03\04\05\06\06\06\04\03\06\00\05\00\03\02\01\00",
-	"\02\014\00\06\02\03\02\00\03\00\03\03\01\06\10\02\03\03\03\05\06\04\06",
-	"\01\024\00\05\04\05\00\01\00\00\06\00\06\01\02\01\06\05\06\06\00\06",
-	"\01\020\04\00\02\00\02\06\04\06\04\05\03\05\03\01\04\01",
-	"\01\10\00\05\05\00\06\01\01\06",
-	"\01\020\02\00\02\01\03\01\03\05\02\05\02\06\04\06\04\00",
-	"\01\014\00\02\03\06\06\02\05\02\03\05\01\02",
-	"\01\10\00\01\06\01\06\02\00\02",
-	"\01\10\02\04\03\05\03\06\02\06",
-	"\04\06\03\04\03\05\04\06\024\00\02\00\04\01\05\02\05\03\04\04\05\05\05\06\04\05\04\04\02\012\00\02\04\02\04\01\02\00\01\00\014\02\01\04\04\05\02\06\02\05\00\04\00",
-	"\01\016\00\03\03\00\03\02\06\02\06\04\03\04\03\06",
-	"\01\016\00\02\00\04\03\04\03\06\06\03\03\00\03\02",
-	"\01\06\00\00\03\06\06\00",
-	"\03\06\01\00\05\00\03\03\06\03\03\00\03\01\06\06\03\03\06\03\05\06",
-};
-
-void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
-	if (cnum < 0x20 || cnum > 0x65)
-		cnum = 0x20;
-
-	const uint8 *data = reinterpret_cast<const uint8 *>(kWallCharData[cnum - 0x20]);
-	if (!data || data[0] == 0)
-		return;
-
-	const bool macMode = (_renderMode == Common::kRenderMacintosh);
-	const bool macColors = (macMode && _hasMacColors);
-	const uint32 fillColor = macColors ? packMacColor(_macColors[8 + _level - 1].bg) : 0;
-	const uint32 lineColor = macColors ? (uint32)0xFF000000 : 0;
-
-	auto drawFilledCharPolygon = [&](const float *u, const float *v, int count) {
-		if (!macMode || count < 3)
-			return;
-
-		float cuts[96];
-		int cutCount = 0;
-		for (int i = 0; i < count; ++i)
-			addSortedUniqueFloat(cuts, cutCount, v[i]);
-
-		for (int i = 0; i < count; ++i) {
-			const int nextI = (i + 1) % count;
-			for (int j = i + 1; j < count; ++j) {
-				const int nextJ = (j + 1) % count;
-				if (j == i || j == nextI || nextJ == i)
-					continue;
-				if (i == 0 && nextJ == 0)
-					continue;
-				if ((nearlyEqual(u[i], u[j]) && nearlyEqual(v[i], v[j])) ||
-				    (nearlyEqual(u[i], u[nextJ]) && nearlyEqual(v[i], v[nextJ])) ||
-				    (nearlyEqual(u[nextI], u[j]) && nearlyEqual(v[nextI], v[j])) ||
-				    (nearlyEqual(u[nextI], u[nextJ]) && nearlyEqual(v[nextI], v[nextJ])))
-					continue;
-
-				float ix, iy;
-				if (segmentIntersection2D(u[i], v[i], u[nextI], v[nextI], u[j], v[j], u[nextJ], v[nextJ], ix, iy))
-					addSortedUniqueFloat(cuts, cutCount, iy);
-			}
-		}
-
-		for (int band = 0; band < cutCount - 1; ++band) {
-			const float y0 = cuts[band];
-			const float y1 = cuts[band + 1];
-			if (y1 - y0 <= 0.0001f)
-				continue;
-
-			const float yMid = (y0 + y1) * 0.5f;
-			const float yTopSample = y0 + (y1 - y0) * 0.001f;
-			const float yBottomSample = y1 - (y1 - y0) * 0.001f;
-			WallCharSpan spans[32];
-			int spanCount = 0;
-
-			for (int i = 0; i < count; ++i) {
-				const int next = (i + 1) % count;
-				const float yA = v[i];
-				const float yB = v[next];
-				if (nearlyEqual(yA, yB))
-					continue;
-				if (!((yA <= yMid && yMid < yB) || (yB <= yMid && yMid < yA)))
-					continue;
-
-				spans[spanCount].xMid = edgeXAtY(u[i], yA, u[next], yB, yMid);
-				spans[spanCount].xTop = edgeXAtY(u[i], yA, u[next], yB, yTopSample);
-				spans[spanCount].xBottom = edgeXAtY(u[i], yA, u[next], yB, yBottomSample);
-				++spanCount;
-			}
-
-			if (spanCount < 2)
-				continue;
-
-			sortWallCharSpans(spans, spanCount);
-			for (int i = 0; i + 1 < spanCount; i += 2) {
-				if (spans[i + 1].xMid - spans[i].xMid <= 0.0001f)
-					continue;
-
-				const float quadU[4] = {spans[i].xTop, spans[i + 1].xTop, spans[i + 1].xBottom, spans[i].xBottom};
-				const float quadV[4] = {y0, y0, y1, y1};
-				wallPolygon(corners, quadU, quadV, 4, fillColor);
-			}
-		}
-	};
-
-	if (macMode)
-		_gfx->setWireframe(false);
-
-	int offset = 1;
-	for (int poly = 0; poly < data[0]; ++poly) {
-		const int coordCount = data[offset++];
-		int count = coordCount / 2;
-		if (count > 32)
-			count = 32;
-
-		float u[32], v[32];
-		for (int i = 0; i < count; ++i) {
-			u[i] = (float)data[offset + i * 2] / 6.0f;
-			v[i] = (float)data[offset + i * 2 + 1] / 6.0f;
-		}
-
-		drawFilledCharPolygon(u, v, count);
-		for (int i = 0; i < count; ++i) {
-			const int next = (i + 1) % count;
-			wallLine(corners, u[i], v[i], u[next], v[next], lineColor);
-		}
-
-		offset += coordCount;
-	}
-
-	if (macMode) {
-		const uint32 wallFill = _hasMacColors
-			? packMacColor(_macColors[8 + _level - 1].fg)
-			: (uint32)255;
-		_gfx->setWireframe(true, wallFill);
-	}
-}
-
-void ColonyEngine::getCellFace3D(int cellX, int cellY, bool ceiling, float corners[4][3]) {
-	float z = ceiling ? 160.0f : -160.0f;
-	float x0 = cellX * 256.0f;
-	float y0 = cellY * 256.0f;
-	float x1 = x0 + 256.0f;
-	float y1 = y0 + 256.0f;
-	const float eps = 0.1f;
-	if (ceiling)
-		z -= eps;
-	else
-		z += eps;
-
-	corners[0][0] = x0; corners[0][1] = y0; corners[0][2] = z;
-	corners[1][0] = x1; corners[1][1] = y0; corners[1][2] = z;
-	corners[2][0] = x1; corners[2][1] = y1; corners[2][2] = z;
-	corners[3][0] = x0; corners[3][1] = y1; corners[3][2] = z;
-}
-
-void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
-	const uint8 *map = mapFeatureAt(cellX, cellY, kDirCenter);
-	if (!map || map[0] == 0)
-		return;
-
-	float corners[4][3];
-	bool ceiling = (map[0] == 3 || map[0] == 4); // SMHOLECEIL, LGHOLECEIL
-	getCellFace3D(cellX, cellY, ceiling, corners);
-
-	// DOS uses color_wall (PenColor) for hole outlines.
-	// In our inverted 3D renderer: lit=black outlines on white fill, dark=white on black.
-	bool lit = (_corePower[_coreIndex] > 0);
-	uint32 holeColor = lit ? 0 : 7;
-
-	const bool macMode = (_renderMode == Common::kRenderMacintosh);
-	const bool macColors = (macMode && _hasMacColors);
-
-	// 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) {
-		if (macColors) {
-			uint32 fg = packMacColor(_macColors[macIdx].fg);
-			uint32 bg = packMacColor(_macColors[macIdx].bg);
-			int pat = _macColors[macIdx].pattern;
-			const byte *stipple = setupMacPattern(_gfx, pat, fg, bg);
-			wallPolygon(corners, u, v, cnt, fg);
-			if (stipple)
-			_gfx->setStippleData(nullptr);
-		} else if (macMode) {
-			_gfx->setStippleData(kStippleGray);
-			wallPolygon(corners, u, v, cnt, 0);
-			_gfx->setStippleData(nullptr);
-		} else {
-			wallPolygon(corners, u, v, cnt, holeColor);
-		}
-	};
-
-	switch (map[0]) {
-	case 1: // SMHOLEFLR
-	case 3: // SMHOLECEIL
-	{
-		float u[4] = {0.25f, 0.75f, 0.75f, 0.25f};
-		float v[4] = {0.25f, 0.25f, 0.75f, 0.75f};
-		drawHolePoly(u, v, 4, 30); // c_hole
-		break;
-	}
-	case 2: // LGHOLEFLR
-	case 4: // LGHOLECEIL
-	{
-		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-		drawHolePoly(u, v, 4, 30); // c_hole
-		break;
-	}
-	case 5: // HOTFOOT
-	{
-		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-		if (macMode) {
-			drawHolePoly(u, v, 4, 31); // c_hotplate
-		} else {
-			// DOS non-polyfill: X pattern (two diagonals)
-			wallLine(corners, 0.0f, 0.0f, 1.0f, 1.0f, holeColor);
-			wallLine(corners, 1.0f, 0.0f, 0.0f, 1.0f, holeColor);
-		}
-		break;
-	}
-	default:
-		break;
-	}
-}
-
-void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
-	const uint8 *map = mapFeatureAt(cellX, cellY, direction);
-	if (!map || map[0] == kWallFeatureNone)
-		return;
-
-	// Backface culling: only draw features for the side facing the camera.
-	// This prevents backside decorations (like Level 2 lines) from bleeding through.
-	// We use non-inclusive comparisons so features remain visible while standing on the boundary.
-	switch (direction) {
-	case kDirNorth:
-		if (_me.yloc > (cellY + 1) * 256)
-			return;
-		break;
-	case kDirSouth:
-		if (_me.yloc < cellY * 256)
-			return;
-		break;
-	case kDirWest:
-		if (_me.xloc < cellX * 256)
-			return;
-		break;
-	case kDirEast:
-		if (_me.xloc > (cellX + 1) * 256)
-			return;
-		break;
-	default: break;
-	}
-	
-	float corners[4][3];
-	getWallFace3D(cellX, cellY, direction, corners);
-	const bool macMode = (_renderMode == Common::kRenderMacintosh);
-	const bool macColors = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
-	const bool lit = (_corePower[_coreIndex] > 0);
-	const uint32 wallFeatureFill = macColors
-		? packMacColor(lit ? _macColors[8 + _level - 1].fg : _macColors[6].bg)
-		: (lit ? (macMode ? 255u : 7u) : 0u);
-
-	// Wall faces are already filled with level-specific color (c_char0+level-1.fg)
-	// by the wall grid in renderCorridor3D(). Features are drawn on top.
-
-	// Helper lambda: Mac color fill for a wall feature polygon
-	auto macFillPoly = [&](const float *u, const float *v, int cnt, int macIdx) {
-		uint32 fg = packMacColor(_macColors[macIdx].fg);
-		uint32 bg = packMacColor(_macColors[macIdx].bg);
-		const byte *stipple = setupMacPattern(_gfx, _macColors[macIdx].pattern, fg, bg);
-		wallPolygon(corners, u, v, cnt, fg);
-		if (stipple)
-			_gfx->setStippleData(nullptr);
-	};
-
-	switch (map[0]) {
-	case kWallFeatureDoor: {
-		const uint32 doorColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
-		bool shipLevel = (_level == 1 || _level == 5 || _level == 6);
-
-		if (shipLevel) {
-			static const float uSs[8] = { 0.375f, 0.250f, 0.250f, 0.375f, 0.625f, 0.750f, 0.750f, 0.625f };
-			static const float vSs[8] = { 0.125f, 0.250f, 0.750f, 0.875f, 0.875f, 0.750f, 0.250f, 0.125f };
-			const uint32 shipDoorColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : (_wireframe ? 8u : 0u));
-
-			if (macMode) {
-				if (map[1] != 0) {
-					if (macColors) {
-						macFillPoly(uSs, vSs, 8, 15); // c_bulkhead
-					} else {
-						_gfx->setStippleData(kStippleGray);
-						wallPolygon(corners, uSs, vSs, 8, 0);
-						_gfx->setStippleData(nullptr);
-					}
-				} else {
-					// Open: fill with BLACK (passable opening)
-					_gfx->setWireframe(true, 0);
-					wallPolygon(corners, uSs, vSs, 8, 0);
-				}
-			} else if (!_wireframe) {
-				if (map[1] != 0) {
-					const byte *stipple = setupMacPattern(_gfx, kPatternLtGray, 0, 15);
-					wallPolygon(corners, uSs, vSs, 8, 0);
-					if (stipple)
-						_gfx->setStippleData(nullptr);
-				} else {
-					_gfx->setWireframe(true, 0);
-					wallPolygon(corners, uSs, vSs, 8, 0);
-				}
-			} else {
-				_gfx->setWireframe(true);
-				wallPolygon(corners, uSs, vSs, 8, 8);
-			}
-
-			for (int i = 0; i < 8; i++)
-				wallLine(corners, uSs[i], vSs[i], uSs[(i + 1) % 8], vSs[(i + 1) % 8], shipDoorColor);
-
-			if (map[1] != 0) {
-				wallLine(corners, 0.375f, 0.25f, 0.375f, 0.75f, shipDoorColor);
-				wallLine(corners, 0.375f, 0.75f, 0.625f, 0.75f, shipDoorColor);
-				wallLine(corners, 0.625f, 0.75f, 0.625f, 0.25f, shipDoorColor);
-				wallLine(corners, 0.625f, 0.25f, 0.375f, 0.25f, shipDoorColor);
-			}
-		} else {
-			static const float xl = 0.25f, xr = 0.75f;
-			static const float yb = 0.0f, yt = 0.875f;
-
-			if (macMode) {
-				float ud[4] = {xl, xr, xr, xl};
-				float vd[4] = {yb, yb, yt, yt};
-				if (map[1] != 0) {
-					if (macColors) {
-						macFillPoly(ud, vd, 4, 16); // c_door
-					} else {
-						_gfx->setStippleData(kStippleGray);
-						wallPolygon(corners, ud, vd, 4, 0);
-						_gfx->setStippleData(nullptr);
-					}
-				} else {
-					// Open: fill with BLACK (passable opening)
-					_gfx->setWireframe(true, 0);
-					wallPolygon(corners, ud, vd, 4, 0);
-					_gfx->setWireframe(true, 255);
-				}
-			}
-
-			wallLine(corners, xl, yb, xl, yt, doorColor);
-			wallLine(corners, xl, yt, xr, yt, doorColor);
-			wallLine(corners, xr, yt, xr, yb, doorColor);
-			wallLine(corners, xr, yb, xl, yb, doorColor);
-
-			if (map[1] != 0) {
-				wallLine(corners, 0.3125f, 0.4375f, 0.6875f, 0.4375f, doorColor);
-			}
-		}
-		break;
-	}
-	case kWallFeatureWindow: {
-		const uint32 winColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
-		float xl = 0.25f, xr = 0.75f;
-		float yb = 0.25f, yt = 0.75f;
-		float xc = 0.5f, yc = 0.5f;
-
-		// Mac: fill window pane
-		if (macMode) {
-			float uw[4] = {xl, xr, xr, xl};
-			float vw[4] = {yb, yb, yt, yt};
-			if (macColors) {
-				macFillPoly(uw, vw, 4, 17); // c_window
-			} else {
-				_gfx->setStippleData(kStippleDkGray);
-				wallPolygon(corners, uw, vw, 4, 0);
-				_gfx->setStippleData(nullptr);
-			}
-		}
-
-		wallLine(corners, xl, yb, xl, yt, winColor);
-		wallLine(corners, xl, yt, xr, yt, winColor);
-		wallLine(corners, xr, yt, xr, yb, winColor);
-		wallLine(corners, xr, yb, xl, yb, winColor);
-		wallLine(corners, xc, yb, xc, yt, winColor);
-		wallLine(corners, xl, yc, xr, yc, winColor);
-		break;
-	}
-	case kWallFeatureShelves: {
-		// DOS drawbooks: recessed bookcase with 3D depth.
-		const uint32 shelfColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
-
-		// Mac: fill shelves area
-		if (macMode) {
-			float us[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-			float vs[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-			if (macColors) {
-				macFillPoly(us, vs, 4, 18); // c_shelves
-			} else {
-				_gfx->setStippleData(kStippleLtGray);
-				wallPolygon(corners, us, vs, 4, 0);
-				_gfx->setStippleData(nullptr);
-			}
-		}
-		float bx = 0.1875f, bxr = 0.8125f;
-		float by = 0.1875f, byt = 0.8125f;
-		// Back face rectangle
-		wallLine(corners, bx, by, bxr, by, shelfColor);
-		wallLine(corners, bxr, by, bxr, byt, shelfColor);
-		wallLine(corners, bxr, byt, bx, byt, shelfColor);
-		wallLine(corners, bx, byt, bx, by, shelfColor);
-		// Connecting lines (front corners to back corners)
-		wallLine(corners, 0.0f, 0.0f, bx, by, shelfColor);
-		wallLine(corners, 0.0f, 1.0f, bx, byt, shelfColor);
-		wallLine(corners, 1.0f, 0.0f, bxr, by, shelfColor);
-		wallLine(corners, 1.0f, 1.0f, bxr, byt, shelfColor);
-		// 7 shelf lines across front face (DOS split7 at 1/8..7/8 intervals)
-		for (int i = 1; i <= 7; i++) {
-			float v = (float)i / 8.0f;
-			wallLine(corners, 0.0f, v, 1.0f, v, shelfColor);
-		}
-		break;
-	}
-	case kWallFeatureUpStairs: {
-		// DOS: draw_up_stairs  staircase ascending into the wall with perspective
-		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
-
-		// Mac: fill entire wall face (c_upstairs)
-		if (_renderMode == Common::kRenderMacintosh) {
-			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-			float vf2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-			if (macColors) {
-				macFillPoly(uf, vf2, 4, 19); // c_upstairs1
-			} else {
-				_gfx->setStippleData(kStippleGray);
-				wallPolygon(corners, uf, vf2, 4, 0);
-				_gfx->setStippleData(nullptr);
-			}
-		}
-
-		// Perspective convergence: back of passage at ~1/3 width (1 cell deep)
-		float ul[7], ur[7], vf[7], vc[7];
-		for (int i = 0; i < 7; i++) {
-			float f = (i + 1) / 8.0f;
-			float inset = f * (1.0f / 3.0f);
-			ul[i] = inset;
-			ur[i] = 1.0f - inset;
-			vf[i] = inset;        // floor rises toward center
-			vc[i] = 1.0f - inset; // ceiling drops toward center
-		}
-		// Step height: at depth d, step s is at fraction (s+1)/8 from floor to ceiling
-		auto vh = [&](int d, int s) -> float {
-			return vf[d] + (s + 1) / 8.0f * (vc[d] - vf[d]);
-		};
-		// Back of passage (full depth)
-		float bi = 1.0f / 3.0f; // back inset
-		float bu = bi, bur = 1.0f - bi, bvc = 1.0f - bi;
-
-		// 1. Side wall verticals at back of passage
-		wallLine(corners, bu, bvc, bu, 0.5f, col);
-		wallLine(corners, bur, 0.5f, bur, bvc, col);
-
-		// 2. Back wall landing (depth 6 to full depth)
-		wallLine(corners, ul[6], vh(6, 6), bu, bvc, col);
-		wallLine(corners, bu, bvc, bur, bvc, col);
-		wallLine(corners, bur, bvc, ur[6], vh(6, 6), col);
-		wallLine(corners, ur[6], vh(6, 6), ul[6], vh(6, 6), col);
-
-		// 3. First step tread (floor from wall face to depth 0)
-		wallLine(corners, 0.0f, 0.0f, ul[0], vf[0], col);
-		wallLine(corners, ul[0], vf[0], ur[0], vf[0], col);
-		wallLine(corners, ur[0], vf[0], 1.0f, 0.0f, col);
-		wallLine(corners, 1.0f, 0.0f, 0.0f, 0.0f, col);
-
-		// 4. First step riser (at depth 0)
-		wallLine(corners, ul[0], vh(0, 0), ul[0], vf[0], col);
-		wallLine(corners, ur[0], vf[0], ur[0], vh(0, 0), col);
-		wallLine(corners, ur[0], vh(0, 0), ul[0], vh(0, 0), col);
-
-		// 5. Step treads (i=3..0: depth i to depth i+1)
-		for (int i = 3; i >= 0; i--) {
-			wallLine(corners, ul[i], vh(i, i), ul[i + 1], vh(i + 1, i), col);
-			wallLine(corners, ul[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i), col);
-			wallLine(corners, ur[i + 1], vh(i + 1, i), ur[i], vh(i, i), col);
-			wallLine(corners, ur[i], vh(i, i), ul[i], vh(i, i), col);
-		}
-
-		// 6. Step risers (i=5..0: vertical face at depth i+1)
-		for (int i = 5; i >= 0; i--) {
-			wallLine(corners, ul[i + 1], vh(i + 1, i + 1), ul[i + 1], vh(i + 1, i), col);
-			wallLine(corners, ul[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i), col);
-			wallLine(corners, ur[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i + 1), col);
-			wallLine(corners, ur[i + 1], vh(i + 1, i + 1), ul[i + 1], vh(i + 1, i + 1), col);
-		}
-
-		// 7. Handrails: from center of wall edges up to near-ceiling at mid-depth
-		wallLine(corners, 0.0f, 0.5f, ul[3], vc[0], col);
-		wallLine(corners, 1.0f, 0.5f, ur[3], vc[0], col);
-		break;
-	}
-	case kWallFeatureDnStairs: {
-		// DOS: draw_dn_stairs  staircase descending into the wall with perspective
-		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
-
-		// Mac: fill entire wall face (c_dnstairs)
-		if (_renderMode == Common::kRenderMacintosh) {
-			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-			float vf2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-			if (macColors) {
-				macFillPoly(uf, vf2, 4, 21); // c_dnstairs
-			} else {
-				_gfx->setStippleData(kStippleGray);
-				wallPolygon(corners, uf, vf2, 4, 0);
-				_gfx->setStippleData(nullptr);
-			}
-		}
-
-		float ul[7], ur[7], vf[7], vc[7];
-		for (int i = 0; i < 7; i++) {
-			float f = (i + 1) / 8.0f;
-			float inset = f * (1.0f / 3.0f);
-			ul[i] = inset;
-			ur[i] = 1.0f - inset;
-			vf[i] = inset;
-			vc[i] = 1.0f - inset;
-		}
-		float bi = 1.0f / 3.0f;
-		float bu = bi, bur = 1.0f - bi;
-
-		// 1. Ceiling: front ceiling slopes down to mid-depth
-		wallLine(corners, 0.0f, 1.0f, ul[3], vc[3], col);
-		wallLine(corners, ul[3], vc[3], ur[3], vc[3], col);
-		wallLine(corners, ur[3], vc[3], 1.0f, 1.0f, col);
-
-		// 2. Slant: from mid-depth ceiling down to center at back
-		wallLine(corners, ul[3], vc[3], bu, 0.5f, col);
-		wallLine(corners, bu, 0.5f, bur, 0.5f, col);
-		wallLine(corners, bur, 0.5f, ur[3], vc[3], col);
-
-		// 3. Side wall verticals: from center at back down to floor level
-		wallLine(corners, bu, 0.5f, bu, vf[0], col);
-		wallLine(corners, bur, 0.5f, bur, vf[0], col);
-
-		// 4. First step (floor from wall face to depth 0)
-		wallLine(corners, 0.0f, 0.0f, ul[0], vf[0], col);
-		wallLine(corners, ul[0], vf[0], ur[0], vf[0], col);
-		wallLine(corners, ur[0], vf[0], 1.0f, 0.0f, col);
-		wallLine(corners, 1.0f, 0.0f, 0.0f, 0.0f, col);
-
-		// 5. Handrails: from center of wall edges down to floor at mid-depth
-		wallLine(corners, 0.0f, 0.5f, ul[3], vf[3], col);
-		wallLine(corners, 1.0f, 0.5f, ur[3], vf[3], col);
-		break;
-	}
-	case kWallFeatureChar:
-		wallChar(corners, map[1]);
-		break;
-	case kWallFeatureGlyph: {
-		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
-		const uint32 glyphColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
-
-		// Mac: fill glyph area
-		if (macMode) {
-			float ug[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-			float vg[4] = {0.0f, 0.0f, 1.0f, 1.0f};
-			if (macColors) {
-				macFillPoly(ug, vg, 4, 22); // c_glyph
-			} else {
-				_gfx->setStippleData(kStippleGray);
-				wallPolygon(corners, ug, vg, 4, 0);
-				_gfx->setStippleData(nullptr);
-			}
-		}
-
-		for (int i = 0; i < 7; i++) {
-			float v = 0.2f + i * 0.1f;
-			wallLine(corners, 0.2f, v, 0.8f, v, glyphColor);
-		}
-		break;
-	}
-	case kWallFeatureElevator: {
-		const uint32 elevColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
-		float xl = 0.2f, xr = 0.8f;
-		float yb = 0.1f, yt = 0.9f;
-
-		// Mac: fill elevator door
-		if (macMode) {
-			float ue[4] = {xl, xr, xr, xl};
-			float ve[4] = {yb, yb, yt, yt};
-			if (macColors) {
-				macFillPoly(ue, ve, 4, 23); // c_elevator
-			} else {
-				_gfx->setStippleData(kStippleGray);
-				wallPolygon(corners, ue, ve, 4, 0);
-				_gfx->setStippleData(nullptr);
-			}
-		}
-
-		wallLine(corners, xl, yb, xl, yt, elevColor);
-		wallLine(corners, xl, yt, xr, yt, elevColor);
-		wallLine(corners, xr, yt, xr, yb, elevColor);
-		wallLine(corners, xr, yb, xl, yb, elevColor);
-		wallLine(corners, 0.5f, yb, 0.5f, yt, elevColor);
-		break;
-	}
-	case kWallFeatureTunnel: {
-		// Tunnel: hexagonal opening from Grid (0,0 0,5 1,6 5,6 6,5 6,0)
-		static const float uT[6] = { 0.0f,    0.0f,    1/6.0f,  5/6.0f,  1.0f,    1.0f };
-		static const float vT[6] = { 0.0f,    0.750f,  0.875f,  0.875f,  0.750f,  0.0f };
-		if (_renderMode == Common::kRenderMacintosh) {
-			if (macColors) {
-				macFillPoly(uT, vT, 6, 24); // c_tunnel
-			} else {
-				_gfx->setStippleData(kStippleGray);
-				wallPolygon(corners, uT, vT, 6, 0);
-				_gfx->setStippleData(nullptr);
-			}
-		} else {
-			wallPolygon(corners, uT, vT, 6, 0); // vBLACK outline
-		}
-		break;
-	}
-	case kWallFeatureAirlock: {
-		// Direct port of drawALOpen/drawALClosed from WALLFTRS.C / wallftrs.c.
-		// These are the exact split7x7 positions on the wall face.
-		static const float u[8] = {0.125f, 0.25f, 0.5f, 0.75f, 0.875f, 0.75f, 0.5f, 0.25f};
-		static const float v[8] = {0.5f, 0.75f, 0.875f, 0.75f, 0.5f, 0.25f, 0.125f, 0.25f};
-		static const float spokeU[8] = {0.375f, 0.5f, 0.625f, 0.625f, 0.625f, 0.5f, 0.375f, 0.375f};
-		static const float spokeV[8] = {0.625f, 0.625f, 0.625f, 0.5f, 0.375f, 0.375f, 0.375f, 0.5f};
-		static const float centerU = 0.5f;
-		static const float centerV = 0.5f;
-
-		if (map[1] == 0) {
-			// Original drawALOpen: solid black opening on both DOS and Mac.
-			if (macMode || !_wireframe)
-				_gfx->setWireframe(true, 0);
-			else
-				_gfx->setWireframe(true);
-			wallPolygon(corners, u, v, 8, 0);
-		} else {
-			// Mac: fill airlock when closed
-			if (macMode) {
-				if (macColors) {
-					macFillPoly(u, v, 8, 25); // c_airlock
-				} else {
-					_gfx->setStippleData(kStippleGray);
-					wallPolygon(corners, u, v, 8, 0);
-					_gfx->setStippleData(nullptr);
-				}
-			} else if (!_wireframe) {
-				const byte *stipple = setupMacPattern(_gfx, kPatternLtGray, 0, 15);
-				wallPolygon(corners, u, v, 8, 0);
-				if (stipple)
-					_gfx->setStippleData(nullptr);
-			}
-
-			const uint32 airlockColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
-			for (int i = 0; i < 8; i++) {
-				int n = (i + 1) % 8;
-				wallLine(corners, u[i], v[i], u[n], v[n], airlockColor);
-			}
-			for (int i = 0; i < 8; i++) {
-				wallLine(corners, u[i], v[i], spokeU[i], spokeV[i], airlockColor);
-				wallLine(corners, spokeU[i], spokeV[i], centerU, centerV, airlockColor);
-			}
-		}
-		break;
-	}
-	case kWallFeatureColor: {
-		// Mac drawColor / DOS drawColor: 4 horizontal bands.
-		// 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 (macColors) {
-				// Mac drawColor: map[i+1] selects color (0→c_color0..3→c_color3, 4→BLACK).
-				// Values >= 5: animated = (map[i+1] + _displayCount) % 5.
-				for (int i = 0; i < 4; i++) {
-					int val = map[i + 1];
-					if (val > 4)
-						val = (val + _displayCount / 6) % 5;
-					float vb = (3 - i) / 4.0f;
-					float vt = (4 - i) / 4.0f;
-					float ub[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-					float vb4[4] = {vb, vb, vt, vt};
-					if (val == 4) {
-						// BLACK: solid black fill
-						_gfx->setWireframe(true, (uint32)0xFF000000);
-						wallPolygon(corners, ub, vb4, 4, 0xFF000000);
-						_gfx->setWireframe(true, packMacColor(_macColors[8 + _level - 1].fg));
-					} else {
-						macFillPoly(ub, vb4, 4, 26 + val); // c_color0 + val
-					}
-				}
-			} else {
-				static const byte *stripPatterns[5] = {
-					nullptr, kStippleLtGray, kStippleGray, kStippleDkGray, nullptr
-				};
-				for (int i = 0; i < 4; i++) {
-					int pat = map[i + 1];
-					if (pat > 4)
-						pat = (pat + _displayCount / 6) % 5; // animated cycling
-					float vb = (3 - i) / 4.0f;
-					float vt = (4 - i) / 4.0f;
-					float ub[4] = {0.0f, 1.0f, 1.0f, 0.0f};
-					float vb4[4] = {vb, vb, vt, vt};
-					if (pat == 4) {
-						_gfx->setWireframe(true, 0);
-						wallPolygon(corners, ub, vb4, 4, 0);
-						_gfx->setWireframe(true, 255);
-					} else if (pat == 0) {
-						// WHITE: no fill needed (wall background is white)
-					} else {
-						_gfx->setStippleData(stripPatterns[pat]);
-						wallPolygon(corners, ub, vb4, 4, 0);
-						_gfx->setStippleData(nullptr);
-					}
-				}
-			}
-		}
-
-		// EGA / Mac B&W: colored lines at band boundaries.
-		// DOS non-polyfill draws 3 lines; we animate line colors for bands > 4.
-		// Mac color mode uses macFillPoly for band fills instead.
-		if (!macColors) {
-			for (int i = 1; i <= 3; i++) {
-				int val = map[i];
-				if (val > 4)
-					val = (val + _displayCount / 6) % 5; // animated cycling
-				uint32 c = 120 + val * 20;
-				if (c == 120 && val == 0 && !map[1] && !map[2] && !map[3] && !map[4])
-					c = 100 + (_level * 15);
-				float v = (float)i / 4.0f;
-				wallLine(corners, 0.0f, v, 1.0f, v, c);
-			}
-		}
-		break;
-	}
-	default:
-		break;
-	}
-
-	_gfx->setStippleData(nullptr);
-	_gfx->setWireframe(true, wallFeatureFill);
-}
-
-void ColonyEngine::drawWallFeatures3D() {
-	if (_corePower[_coreIndex] == 0)
-		return;
-
-	for (int y = 0; y < 31; y++) {
-		for (int x = 0; x < 31; x++) {
-			if (!_visibleCell[x][y])
-				continue;
-			drawCellFeature3D(x, y);
-			for (int dir = 0; dir < 4; dir++) {
-				const uint8 *map = mapFeatureAt(x, y, dir);
-				if (map && map[0] != kWallFeatureNone) {
-					drawWallFeature3D(x, y, dir);
-				}
-			}
-		}
-	}
-}
-
-
-void ColonyEngine::renderCorridor3D() {
-	computeVisibleCells();
+void ColonyEngine::renderCorridor3D() {
+	computeVisibleCells();
 
 	bool lit = (_corePower[_coreIndex] > 0);
 	bool macMode = (_renderMode == Common::kRenderMacintosh);
@@ -2834,351 +832,4 @@ void ColonyEngine::renderCorridor3D() {
 	_gfx->setWireframe(false);
 }
 
-bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
-	const auto drawPrism = [&](const PrismPartDef &def, bool useLook, int colorOverride = -1) {
-		draw3DPrism(obj, def, useLook, colorOverride, true);
-	};
-	const auto drawSphere = [&](int pt0x, int pt0y, int pt0z, int pt1x, int pt1y, int pt1z,
-	                            uint32 fillColor, uint32 outlineColor) {
-		draw3DSphere(obj, pt0x, pt0y, pt0z, pt1x, pt1y, pt1z, fillColor, outlineColor, true);
-	};
-
-	switch (obj.type) {
-	case kObjConsole:
-		drawPrism(kConsolePart, false);
-		break;
-	case kObjCChair:
-		for (int i = 0; i < 5; i++) {
-			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			drawPrism(kCChairParts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	case kObjPlant:
-		// DOS MakePlant draw order: top pot, then green leaf lines, then pot on top.
-		drawPrism(kPlantParts[1], false); // top pot (soil)
-		for (int i = 2; i < 8; i++)
-			draw3DLeaf(obj, kPlantParts[i]); // leaves as lines
-		drawPrism(kPlantParts[0], false); // pot (drawn last, on top)
-		break;
-	case kObjCouch:
-	case kObjChair: {
-		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
-		for (int i = 0; i < 4; i++) {
-			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
-			drawPrism(parts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	}
-	case kObjTV:
-		_gfx->setDepthRange(0.002, 1.0); // body base layer (pushed back)
-		drawPrism(kTVParts[0], false);
-		_gfx->setDepthRange(0.0, 1.0);   // screen on top of body face
-		drawPrism(kTVParts[1], false);
-		break;
-	case kObjDrawer:
-		for (int i = 0; i < 2; i++) {
-			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kDrawerParts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	case kObjFWall:
-		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
-			drawPrism(kFWallPart, false, kColorCorridorWall);
-		else
-			drawPrism(kFWallPart, false);
-		break;
-	case kObjCWall:
-		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
-			drawPrism(kCWallPart, false, kColorCorridorWall);
-		else
-			drawPrism(kCWallPart, false);
-		break;
-	case kObjScreen:
-		drawPrism(kScreenPart, false);
-		break;
-	case kObjTable:
-		for (int i = 0; i < 2; i++) {
-			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kTableParts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	case kObjBed:
-	case kObjBBed: {
-		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
-		for (int i = 0; i < 3; i++) {
-			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
-			drawPrism(parts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	}
-	case kObjDesk:
-		for (int i = 0; i < 10; i++) {
-			_gfx->setDepthRange((9 - i) * 0.002, 1.0);
-			drawPrism(kDeskParts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	case kObjBox1:
-		drawPrism(kBox1Part, false);
-		break;
-	case kObjBench:
-		drawPrism(kBenchPart, false);
-		break;
-	case kObjCBench:
-		for (int i = 0; i < 2; i++) {
-			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kCBenchParts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	case kObjBox2:
-		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kBox2Parts[1], false); // base first
-		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kBox2Parts[0], false); // top second
-		break;
-	case kObjReactor: {
-		// MakeReactor: animate core height and recolor only the original
-		// side faces. The reactor body stays c_reactor and the core cap stays c_ccore.
-		switch (_coreState[_coreIndex]) {
-		case 0: if (_coreHeight[_coreIndex] < 256) _coreHeight[_coreIndex] += 16; break;
-		case 1: if (_coreHeight[_coreIndex] > 0) _coreHeight[_coreIndex] -= 16; break;
-		case 2: _coreHeight[_coreIndex] = 0; break;
-		}
-		int height = _coreHeight[_coreIndex];
-
-		// Create modified point arrays for height animation.
-		// Core bottom hex (pts 6-11) slides up with height.
-		// When height=256 (closed), bottom matches top → core invisible.
-		// When height=0 (open), bottom at Z=32 → full core visible.
-		int modCorePts[12][3];
-		memcpy(modCorePts, kReactorCorePts, sizeof(modCorePts));
-		for (int i = 6; i < 12; i++)
-			modCorePts[i][2] = height + 32;
-
-		// Ring slides vertically with height.
-		// When height=256, ring at Z=256..288 (encasing core top).
-		// When height=0, ring at Z=0..32 (at base, core exposed).
-		int modRingPts[8][3];
-		memcpy(modRingPts, kReactorBasePts, sizeof(modRingPts));
-		for (int i = 0; i < 4; i++)
-			modRingPts[i][2] = height;
-		for (int i = 4; i < 8; i++)
-			modRingPts[i][2] = height + 32;
-
-		int modCoreSurf[7][8];
-		int modRingSurf[6][8];
-		int modTopSurf[6][8];
-		memcpy(modCoreSurf, kReactorCoreSurf, sizeof(modCoreSurf));
-		memcpy(modRingSurf, kReactorBaseSurf, sizeof(modRingSurf));
-		memcpy(modTopSurf, kReactorBaseSurf, sizeof(modTopSurf));
-
-		// Mac MakeReactor(): first 4 ring/top faces cycle through c_color0..c_color3.
-		static const int kRingColors[] = {kColorRainbow1, kColorRainbow2, kColorRainbow3, kColorRainbow4};
-		const int ringColor = kRingColors[_displayCount % 4];
-		for (int i = 0; i < 4; ++i) {
-			modRingSurf[i][0] = ringColor;
-			modTopSurf[i][0] = ringColor;
-		}
-
-		// Only the 6 core side faces animate. The top face remains c_ccore.
-		static const int kCoreCycle[] = {kColorHCore1, kColorHCore2, kColorHCore3, kColorHCore4, kColorHCore3, kColorHCore2};
-		int coreColor;
-		if (_corePower[_coreIndex] > 1)
-			coreColor = kCoreCycle[_displayCount % 6];
-		else
-			coreColor = kColorCCore;
-		for (int i = 0; i < 6; ++i)
-			modCoreSurf[i][0] = coreColor;
-
-		PrismPartDef modCoreDef = {12, modCorePts, 7, modCoreSurf};
-		PrismPartDef modRingDef = {8, modRingPts, 6, modRingSurf};
-		PrismPartDef modTopDef = {8, kReactorTopPts, 6, modTopSurf};
-
-		// Depth separation matches the original draw order closely, but the
-		// per-face colors now follow MakeReactor() exactly.
-		_gfx->setDepthRange(0.004, 1.0);
-		drawPrism(modTopDef, false);
-		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(modRingDef, false);
-		if (_coreState[_coreIndex] < 2) {
-			_gfx->setDepthRange(0.0, 1.0);
-			drawPrism(modCoreDef, false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	}
-	case kObjPowerSuit: {
-		// MakePowerSuit: part[4] (power source hexagon) surface[0] pulsates.
-		// Mac: pcycle[count%6]; DOS: pcycle[count%8]
-		static const int kSuitCycle[] = {kColorHCore1, kColorHCore2, kColorHCore3, kColorHCore4, kColorHCore3, kColorHCore2};
-		int sourceColor = kSuitCycle[_displayCount % 6];
-
-		for (int i = 0; i < 4; i++) {
-			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			drawPrism(kPowerSuitParts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kPowerSuitParts[4], false, sourceColor);
-		break;
-	}
-	case kObjTeleport:
-		drawPrism(kTelePart, false);
-		break;
-	case kObjCryo:
-		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kCryoParts[1], false); // base first
-		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kCryoParts[0], false); // top second
-		break;
-	case kObjProjector:
-		// Projector sits on table  draw table first, then projector parts
-		_gfx->setDepthRange(0.008, 1.0);
-		drawPrism(kTableParts[0], false); // table base
-		_gfx->setDepthRange(0.006, 1.0);
-		drawPrism(kTableParts[1], false); // table top
-		_gfx->setDepthRange(0.004, 1.0);
-		drawPrism(kProjectorParts[1], false); // stand
-		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kProjectorParts[0], false); // body
-		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kProjectorParts[2], false); // lens
-		break;
-	case kObjTub:
-		for (int i = 0; i < 2; i++) {
-			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kTubParts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	case kObjSink:
-		for (int i = 0; i < 3; i++) {
-			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
-			drawPrism(kSinkParts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	case kObjToilet:
-		for (int i = 0; i < 4; i++) {
-			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
-			drawPrism(kToiletParts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	case kObjPToilet:
-		for (int i = 0; i < 5; i++) {
-			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			drawPrism(kPToiletParts[i], false);
-		}
-		_gfx->setDepthRange(0.0, 1.0);
-		break;
-	case kObjForkLift:
-		// Draw order: forks, arms, treads, cab (back-to-front)
-		_gfx->setDepthRange(0.010, 1.0);
-		drawPrism(kForkliftParts[3], false); // FLLL (left fork)
-		_gfx->setDepthRange(0.008, 1.0);
-		drawPrism(kForkliftParts[2], false); // FLUL (left arm)
-		_gfx->setDepthRange(0.006, 1.0);
-		drawPrism(kForkliftParts[5], false); // FLLR (right fork)
-		_gfx->setDepthRange(0.004, 1.0);
-		drawPrism(kForkliftParts[4], false); // FLUR (right arm)
-		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kForkliftParts[1], false); // treads
-		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kForkliftParts[0], false); // cab
-		break;
-	// === Robot types (1-20) ===
-	case kRobEye:
-		drawSphere(0, 0, 100, 0, 0, 200, 15, 15); // ball: white
-		drawPrism(kEyeIrisDef, false);
-		drawPrism(kEyePupilDef, false);
-		break;
-	case kRobPyramid:
-		drawPrism(kPShadowDef, false);
-		drawPrism(kPyramidBodyDef, false);
-		drawSphere(0, 0, 175, 0, 0, 200, 15, 15); // ball on top
-		drawPrism(kPIrisDef, false);
-		drawPrism(kPPupilDef, false);
-		break;
-	case kRobCube:
-		drawPrism(kCubeBodyDef, false);
-		break;
-	case kRobUPyramid:
-		drawPrism(kUPShadowDef, false);
-		drawPrism(kUPyramidBodyDef, false);
-		break;
-	case kRobFEye:
-		drawSphere(0, 0, 0, 0, 0, 100, 15, 15);
-		drawPrism(kFEyeIrisDef, false);
-		drawPrism(kFEyePupilDef, false);
-		break;
-	case kRobFPyramid:
-		drawPrism(kFPyramidBodyDef, false);
-		break;
-	case kRobFCube:
-		drawPrism(kFCubeBodyDef, false);
-		break;
-	case kRobFUPyramid:
-		drawPrism(kFUPyramidBodyDef, false);
-		break;
-	case kRobSEye:
-		drawSphere(0, 0, 0, 0, 0, 50, 15, 15);
-		drawPrism(kSEyeIrisDef, false);
-		drawPrism(kSEyePupilDef, false);
-		break;
-	case kRobSPyramid:
-		drawPrism(kSPyramidBodyDef, false);
-		break;
-	case kRobSCube:
-		drawPrism(kSCubeBodyDef, false);
-		break;
-	case kRobSUPyramid:
-		drawPrism(kSUPyramidBodyDef, false);
-		break;
-	case kRobMEye:
-		drawSphere(0, 0, 0, 0, 0, 25, 15, 15);
-		drawPrism(kMEyeIrisDef, false);
-		drawPrism(kMEyePupilDef, false);
-		break;
-	case kRobMPyramid:
-		drawPrism(kMPyramidBodyDef, false);
-		break;
-	case kRobMCube:
-		drawPrism(kMCubeBodyDef, false);
-		break;
-	case kRobMUPyramid:
-		drawPrism(kMUPyramidBodyDef, false);
-		break;
-	case kRobQueen:
-		drawPrism(kQThoraxDef, false);
-		drawPrism(kQAbdomenDef, false);
-		drawPrism(kQLWingDef, false);
-		drawPrism(kQRWingDef, false);
-		drawSphere(0, 0, 130, 0, 0, 155, 15, 15); // queen ball
-		drawPrism(kQIrisDef, false);
-		drawPrism(kQPupilDef, false);
-		break;
-	case kRobDrone:
-	case kRobSoldier:
-		drawPrism(kDAbdomenDef, false);
-		drawPrism(kDLLPincerDef, false);
-		drawPrism(kDRRPincerDef, false);
-		drawPrism(kDLEyeDef, false);
-		drawPrism(kDREyeDef, false);
-		break;
-	case kRobSnoop:
-		drawPrism(kSnoopAbdomenDef, false);
-		drawPrism(kSnoopHeadDef, false);
-		break;
-	default:
-		return false;
-	}
-	return true;
-}
-
 } // End of namespace Colony
diff --git a/engines/colony/render_features.cpp b/engines/colony/render_features.cpp
new file mode 100644
index 00000000000..c2903515ff4
--- /dev/null
+++ b/engines/colony/render_features.cpp
@@ -0,0 +1,983 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "colony/render_internal.h"
+#include "common/system.h"
+#include <math.h>
+
+namespace Colony {
+
+void ColonyEngine::getWallFace3D(int cellX, int cellY, int direction, float corners[4][3]) {
+	float x0 = cellX * 256.0f;
+	float y0 = cellY * 256.0f;
+	float x1 = (cellX + 1) * 256.0f;
+	float y1 = (cellY + 1) * 256.0f;
+	const float zBot = -160.0f;
+	const float zTop = 160.0f;
+	// Pull slightly into the cell to prevent z-fighting
+	const float eps = 1.0f;
+
+	switch (direction) {
+	case kDirNorth: // Wall at y1 (+Y); viewed from inside (looking North)
+		corners[0][0] = x0;  corners[0][1] = y1 - eps;  corners[0][2] = zBot; // BL
+		corners[1][0] = x1;  corners[1][1] = y1 - eps;  corners[1][2] = zBot; // BR
+		corners[2][0] = x1;  corners[2][1] = y1 - eps;  corners[2][2] = zTop; // TR
+		corners[3][0] = x0;  corners[3][1] = y1 - eps;  corners[3][2] = zTop; // TL
+		break;
+	case kDirSouth: // Wall at y0 (-Y); viewed from inside (looking South)
+		corners[0][0] = x1;  corners[0][1] = y0 + eps;  corners[0][2] = zBot; // BL
+		corners[1][0] = x0;  corners[1][1] = y0 + eps;  corners[1][2] = zBot; // BR
+		corners[2][0] = x0;  corners[2][1] = y0 + eps;  corners[2][2] = zTop; // TR
+		corners[3][0] = x1;  corners[3][1] = y0 + eps;  corners[3][2] = zTop; // TL
+		break;
+	case kDirEast: // Wall at x1 (+X); viewed from inside (looking East)
+		corners[0][0] = x1 - eps;  corners[0][1] = y1;  corners[0][2] = zBot; // BL
+		corners[1][0] = x1 - eps;  corners[1][1] = y0;  corners[1][2] = zBot; // BR
+		corners[2][0] = x1 - eps;  corners[2][1] = y0;  corners[2][2] = zTop; // TR
+		corners[3][0] = x1 - eps;  corners[3][1] = y1;  corners[3][2] = zTop; // TL
+		break;
+	case kDirWest: // Wall at x0 (-X); viewed from inside (looking West)
+		corners[0][0] = x0 + eps;  corners[0][1] = y0;  corners[0][2] = zBot; // BL
+		corners[1][0] = x0 + eps;  corners[1][1] = y1;  corners[1][2] = zBot; // BR
+		corners[2][0] = x0 + eps;  corners[2][1] = y1;  corners[2][2] = zTop; // TR
+		corners[3][0] = x0 + eps;  corners[3][1] = y0;  corners[3][2] = zTop; // TL
+		break;
+	default:
+		return;
+	}
+}
+
+// Interpolate a point on the wall face.
+// u: 0=left, 1=right (horizontal fraction)
+// v: 0=bottom, 1=top (vertical fraction)
+static void wallPoint(const float corners[4][3], float u, float v, float out[3]) {
+	float botX = corners[0][0] + (corners[1][0] - corners[0][0]) * u;
+	float botY = corners[0][1] + (corners[1][1] - corners[0][1]) * u;
+	float botZ = corners[0][2] + (corners[1][2] - corners[0][2]) * u;
+	float topX = corners[3][0] + (corners[2][0] - corners[3][0]) * u;
+	float topY = corners[3][1] + (corners[2][1] - corners[3][1]) * u;
+	float topZ = corners[3][2] + (corners[2][2] - corners[3][2]) * u;
+	out[0] = botX + (topX - botX) * v;
+	out[1] = botY + (topY - botY) * v;
+	out[2] = botZ + (topZ - botZ) * v;
+}
+
+// Draw a line on a wall face using normalized (u,v) coordinates
+void ColonyEngine::wallLine(const float corners[4][3], float u1, float v1, float u2, float v2, uint32 color) {
+	float p1[3], p2[3];
+	wallPoint(corners, u1, v1, p1);
+	wallPoint(corners, u2, v2, p2);
+	// We assume this is only called when lit (handled in drawWallFeatures3D)
+	_gfx->draw3DLine(p1[0], p1[1], p1[2], p2[0], p2[1], p2[2], color);
+}
+
+// Draw a filled polygon on a wall face using normalized (u,v) coordinates
+void ColonyEngine::wallPolygon(const float corners[4][3], const float *u, const float *v, int count, uint32 color) {
+	float px[64], py[64], pz[64];
+	if (count > 64)
+		count = 64;
+	for (int i = 0; i < count; i++) {
+		float p[3];
+		wallPoint(corners, u[i], v[i], p);
+		px[i] = p[0]; py[i] = p[1]; pz[i] = p[2];
+	}
+	_gfx->draw3DPolygon(px, py, pz, count, color);
+}
+
+static bool nearlyEqual(float a, float b, float eps = 0.0001f) {
+	return fabsf(a - b) <= eps;
+}
+
+static void addSortedUniqueFloat(float *values, int &count, float value, float eps = 0.0001f) {
+	for (int i = 0; i < count; ++i) {
+		if (nearlyEqual(values[i], value, eps))
+			return;
+		if (value < values[i]) {
+			for (int j = count; j > i; --j)
+				values[j] = values[j - 1];
+			values[i] = value;
+			++count;
+			return;
+		}
+	}
+
+	values[count++] = value;
+}
+
+static bool segmentIntersection2D(float ax, float ay, float bx, float by,
+		float cx, float cy, float dx, float dy, float &ix, float &iy) {
+	const float den = (ax - bx) * (cy - dy) - (ay - by) * (cx - dx);
+	if (fabsf(den) < 0.0001f)
+		return false;
+
+	const float t = ((ax - cx) * (cy - dy) - (ay - cy) * (cx - dx)) / den;
+	const float u = ((ax - cx) * (ay - by) - (ay - cy) * (ax - bx)) / den;
+	if (t <= 0.0001f || t >= 0.9999f || u <= 0.0001f || u >= 0.9999f)
+		return false;
+
+	ix = ax + t * (bx - ax);
+	iy = ay + t * (by - ay);
+	return true;
+}
+
+static float edgeXAtY(float x0, float y0, float x1, float y1, float y) {
+	if (nearlyEqual(y0, y1))
+		return x0;
+
+	const float t = (y - y0) / (y1 - y0);
+	return x0 + (x1 - x0) * t;
+}
+
+struct WallCharSpan {
+	float xMid;
+	float xTop;
+	float xBottom;
+};
+
+static void sortWallCharSpans(WallCharSpan *spans, int count) {
+	for (int i = 1; i < count; ++i) {
+		WallCharSpan span = spans[i];
+		int j = i - 1;
+		while (j >= 0 && spans[j].xMid > span.xMid) {
+			spans[j + 1] = spans[j];
+			--j;
+		}
+		spans[j + 1] = span;
+	}
+}
+
+static const char *const kWallCharData[] = {
+	"\00",
+	"\02\10\02\00\03\00\03\01\02\01\10\02\02\03\02\03\06\02\06",
+	"\02\10\01\04\02\04\02\05\01\05\10\03\04\04\04\04\05\03\05",
+	"\04\10\01\00\02\00\02\05\01\05\10\03\00\04\00\04\05\03\05\10\00\01\05\01\05\02\00\02\10\00\03\05\03\05\04\00\04",
+	"\02\10\02\00\03\00\03\06\02\06\050\00\02\00\01\01\00\04\00\05\01\05\02\01\04\01\05\04\05\04\04\05\04\05\05\04\06\01\06\00\05\00\04\04\02\04\01\01\01\01\02",
+	"\03\10\01\00\06\05\05\06\00\01\032\01\03\02\03\03\04\03\05\02\06\01\06\00\05\00\04\01\03\01\05\02\05\02\04\01\04\032\04\00\05\00\06\01\06\02\05\03\04\03\03\02\03\01\04\00\04\02\05\02\05\01\04\01",
+	"\03\10\05\01\05\02\03\04\02\04\014\02\04\01\05\02\06\01\06\00\05\01\04\032\05\04\03\01\01\01\01\03\03\05\02\06\01\06\02\05\00\03\00\01\01\00\03\00\05\03",
+	"\01\10\02\04\03\05\03\06\02\06",
+	"\01\014\04\00\03\02\03\04\04\06\02\04\02\02",
+	"\01\014\02\00\04\02\04\04\02\06\03\04\03\02",
+	"\06\06\01\00\03\03\00\02\06\02\00\04\00\03\03\06\05\00\06\02\03\03\06\06\04\05\06\03\03\06\02\06\04\06\03\03\06\00\04\01\06\03\03",
+	"\02\10\02\00\03\00\03\05\02\05\10\00\02\05\02\05\03\00\03",
+	"\01\10\02\00\03\01\03\02\02\02",
+	"\01\10\00\02\05\02\05\03\00\03",
+	"\01\10\02\00\03\00\03\01\02\01",
+	"\01\10\01\00\06\05\05\06\00\01",
+	"\02\032\01\00\05\00\06\01\06\05\05\06\01\06\00\05\00\01\01\00\01\05\05\05\05\01\01\01\10\01\01\02\01\05\05\04\05",
+	"\01\026\01\00\04\00\04\01\03\01\03\06\02\06\01\05\01\04\02\04\02\01\01\01",
+	"\01\042\06\00\06\01\02\01\05\02\06\03\06\05\05\06\01\06\00\05\00\04\01\04\01\05\05\05\05\04\01\02\00\01\00\00",
+	"\01\054\00\02\00\01\01\00\05\00\06\01\06\02\05\03\06\04\06\05\05\06\01\06\00\05\00\04\01\04\01\05\05\05\05\04\04\03\05\02\05\01\01\01\01\02",
+	"\01\036\04\00\05\00\05\02\06\02\06\03\05\03\05\06\04\06\04\03\01\03\02\06\01\06\00\03\00\02\04\02",
+	"\01\044\00\02\00\01\01\00\05\00\06\01\06\03\05\04\01\04\02\05\06\05\06\06\01\06\00\04\00\03\05\03\05\01\01\01\01\02",
+	"\01\046\01\02\05\02\05\01\01\01\01\05\05\05\05\04\06\04\06\05\05\06\01\06\00\05\00\01\01\00\05\00\06\01\06\02\05\03\01\03",
+	"\01\020\02\00\03\00\03\03\06\06\00\06\00\05\04\05\02\03",
+	"\02\040\03\00\03\01\01\01\01\02\04\03\01\04\01\05\03\05\03\06\01\06\00\05\00\04\01\03\00\02\00\01\01\00\040\03\00\05\00\06\01\06\02\05\03\06\04\06\05\05\06\03\06\03\05\05\05\05\04\02\03\05\02\05\01\03\01",
+	"\01\046\00\02\00\01\01\00\05\00\06\01\06\05\05\06\01\06\00\05\00\04\01\03\05\03\05\04\01\04\01\05\05\05\05\01\01\01\01\02",
+	"\02\10\02\01\03\01\03\02\02\02\10\02\03\03\03\03\04\02\04",
+	"\02\10\02\00\03\01\03\02\02\02\10\02\03\03\03\03\04\02\04",
+	"\01\014\06\00\06\01\02\03\06\05\06\06\00\03",
+	"\02\10\00\01\05\01\05\02\00\02\10\00\03\05\03\05\04\00\04",
+	"\01\014\00\00\06\03\00\06\00\05\04\03\00\01",
+	"\02\10\02\00\03\00\03\01\02\01\030\02\02\03\02\05\04\05\05\04\06\01\06\00\05\00\04\01\04\01\05\04\05\04\04",
+	"\05\012\04\00\01\01\00\04\00\01\01\00\012\02\00\05\00\06\01\06\04\05\01\012\06\02\06\05\05\06\02\06\05\05\012\04\06\01\06\00\05\00\02\01\05\034\05\01\05\02\03\05\02\05\01\04\01\02\02\01\03\01\05\04\05\05\03\02\02\02\02\04\03\04",
+	"\01\034\03\06\04\06\06\00\05\00\04\02\02\02\01\00\00\00\02\06\03\06\03\05\02\03\04\03\03\05",
+	"\01\050\00\00\00\06\05\06\06\05\06\04\05\03\06\02\06\01\05\00\01\00\01\01\05\01\05\02\04\03\01\03\01\04\05\04\05\05\01\05\01\00",
+	"\01\040\06\02\06\01\05\00\01\00\00\01\00\05\01\06\05\06\06\05\06\04\05\04\05\05\01\05\01\01\05\01\05\02",
+	"\01\034\00\00\00\06\04\06\06\04\06\02\04\00\01\00\01\01\04\01\05\02\05\04\04\05\01\05\01\00",
+	"\01\030\00\00\00\06\06\06\06\05\01\05\01\04\04\04\04\03\01\03\01\01\06\01\06\00",
+	"\01\024\00\00\00\06\06\06\06\05\01\05\01\03\04\03\04\02\01\02\01\00",
+	"\01\044\03\03\06\03\06\01\05\00\01\00\00\01\00\05\01\06\05\06\06\05\06\04\05\04\05\05\01\05\01\01\05\01\05\02\03\02",
+	"\01\030\00\00\00\06\01\06\01\04\05\04\05\06\06\06\06\00\05\00\05\03\01\03\01\00",
+	"\01\030\01\00\01\01\02\01\02\05\01\05\01\06\04\06\04\05\03\05\03\01\04\01\04\00",
+	"\01\034\00\02\00\01\01\00\04\00\05\01\05\05\06\05\06\06\03\06\03\05\04\05\04\01\01\01\01\02",
+	"\01\026\00\00\00\06\01\06\01\04\04\06\06\06\02\03\06\00\04\00\01\02\01\00",
+	"\01\014\00\06\00\00\06\00\06\01\01\01\01\06",
+	"\01\030\00\00\00\06\01\06\03\04\05\06\06\06\06\00\05\00\05\04\03\03\01\04\01\00",
+	"\01\024\00\00\00\06\02\06\05\01\05\06\06\06\06\00\04\00\01\05\01\00",
+	"\01\032\00\01\00\05\01\06\05\06\06\05\06\01\05\00\01\00\00\01\05\01\05\05\01\05\01\01",
+	"\01\030\00\00\00\06\05\06\06\05\06\03\05\02\01\02\01\03\05\03\05\05\01\05\01\00",
+	"\02\036\04\00\01\00\00\01\00\05\01\06\05\06\06\05\06\02\04\00\04\02\05\02\05\05\01\05\01\01\04\01\014\06\00\06\01\05\02\04\02\04\01\05\00",
+	"\01\036\00\00\00\06\05\06\06\05\06\03\05\02\06\00\05\00\04\02\01\02\01\03\05\03\05\05\01\05\01\00",
+	"\01\054\00\02\00\01\01\00\05\00\06\01\06\02\05\03\01\04\01\05\05\05\05\04\06\04\06\05\05\06\01\06\00\05\00\04\01\03\05\02\05\01\01\01\01\02",
+	"\01\020\02\00\02\05\00\05\00\06\05\06\05\05\03\05\03\00",
+	"\01\024\00\06\00\01\01\00\05\00\06\01\06\06\05\06\05\01\01\01\01\06",
+	"\01\016\00\06\02\00\04\00\06\06\05\06\03\01\01\06",
+	"\01\030\00\06\01\00\02\00\03\02\04\00\05\00\06\06\05\06\04\02\03\04\02\02\01\06",
+	"\01\030\00\00\02\03\00\06\01\06\03\04\05\06\06\06\04\03\06\00\05\00\03\02\01\00",
+	"\02\014\00\06\02\03\02\00\03\00\03\03\01\06\10\02\03\03\03\05\06\04\06",
+	"\01\024\00\05\04\05\00\01\00\00\06\00\06\01\02\01\06\05\06\06\00\06",
+	"\01\020\04\00\02\00\02\06\04\06\04\05\03\05\03\01\04\01",
+	"\01\10\00\05\05\00\06\01\01\06",
+	"\01\020\02\00\02\01\03\01\03\05\02\05\02\06\04\06\04\00",
+	"\01\014\00\02\03\06\06\02\05\02\03\05\01\02",
+	"\01\10\00\01\06\01\06\02\00\02",
+	"\01\10\02\04\03\05\03\06\02\06",
+	"\04\06\03\04\03\05\04\06\024\00\02\00\04\01\05\02\05\03\04\04\05\05\05\06\04\05\04\04\02\012\00\02\04\02\04\01\02\00\01\00\014\02\01\04\04\05\02\06\02\05\00\04\00",
+	"\01\016\00\03\03\00\03\02\06\02\06\04\03\04\03\06",
+	"\01\016\00\02\00\04\03\04\03\06\06\03\03\00\03\02",
+	"\01\06\00\00\03\06\06\00",
+	"\03\06\01\00\05\00\03\03\06\03\03\00\03\01\06\06\03\03\06\03\05\06",
+};
+
+void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
+	if (cnum < 0x20 || cnum > 0x65)
+		cnum = 0x20;
+
+	const uint8 *data = reinterpret_cast<const uint8 *>(kWallCharData[cnum - 0x20]);
+	if (!data || data[0] == 0)
+		return;
+
+	const bool macMode = (_renderMode == Common::kRenderMacintosh);
+	const bool macColors = (macMode && _hasMacColors);
+	const uint32 fillColor = macColors ? packMacColor(_macColors[8 + _level - 1].bg) : 0;
+	const uint32 lineColor = macColors ? (uint32)0xFF000000 : 0;
+
+	auto drawFilledCharPolygon = [&](const float *u, const float *v, int count) {
+		if (!macMode || count < 3)
+			return;
+
+		float cuts[96];
+		int cutCount = 0;
+		for (int i = 0; i < count; ++i)
+			addSortedUniqueFloat(cuts, cutCount, v[i]);
+
+		for (int i = 0; i < count; ++i) {
+			const int nextI = (i + 1) % count;
+			for (int j = i + 1; j < count; ++j) {
+				const int nextJ = (j + 1) % count;
+				if (j == i || j == nextI || nextJ == i)
+					continue;
+				if (i == 0 && nextJ == 0)
+					continue;
+				if ((nearlyEqual(u[i], u[j]) && nearlyEqual(v[i], v[j])) ||
+				    (nearlyEqual(u[i], u[nextJ]) && nearlyEqual(v[i], v[nextJ])) ||
+				    (nearlyEqual(u[nextI], u[j]) && nearlyEqual(v[nextI], v[j])) ||
+				    (nearlyEqual(u[nextI], u[nextJ]) && nearlyEqual(v[nextI], v[nextJ])))
+					continue;
+
+				float ix, iy;
+				if (segmentIntersection2D(u[i], v[i], u[nextI], v[nextI], u[j], v[j], u[nextJ], v[nextJ], ix, iy))
+					addSortedUniqueFloat(cuts, cutCount, iy);
+			}
+		}
+
+		for (int band = 0; band < cutCount - 1; ++band) {
+			const float y0 = cuts[band];
+			const float y1 = cuts[band + 1];
+			if (y1 - y0 <= 0.0001f)
+				continue;
+
+			const float yMid = (y0 + y1) * 0.5f;
+			const float yTopSample = y0 + (y1 - y0) * 0.001f;
+			const float yBottomSample = y1 - (y1 - y0) * 0.001f;
+			WallCharSpan spans[32];
+			int spanCount = 0;
+
+			for (int i = 0; i < count; ++i) {
+				const int next = (i + 1) % count;
+				const float yA = v[i];
+				const float yB = v[next];
+				if (nearlyEqual(yA, yB))
+					continue;
+				if (!((yA <= yMid && yMid < yB) || (yB <= yMid && yMid < yA)))
+					continue;
+
+				spans[spanCount].xMid = edgeXAtY(u[i], yA, u[next], yB, yMid);
+				spans[spanCount].xTop = edgeXAtY(u[i], yA, u[next], yB, yTopSample);
+				spans[spanCount].xBottom = edgeXAtY(u[i], yA, u[next], yB, yBottomSample);
+				++spanCount;
+			}
+
+			if (spanCount < 2)
+				continue;
+
+			sortWallCharSpans(spans, spanCount);
+			for (int i = 0; i + 1 < spanCount; i += 2) {
+				if (spans[i + 1].xMid - spans[i].xMid <= 0.0001f)
+					continue;
+
+				const float quadU[4] = {spans[i].xTop, spans[i + 1].xTop, spans[i + 1].xBottom, spans[i].xBottom};
+				const float quadV[4] = {y0, y0, y1, y1};
+				wallPolygon(corners, quadU, quadV, 4, fillColor);
+			}
+		}
+	};
+
+	if (macMode)
+		_gfx->setWireframe(false);
+
+	int offset = 1;
+	for (int poly = 0; poly < data[0]; ++poly) {
+		const int coordCount = data[offset++];
+		int count = coordCount / 2;
+		if (count > 32)
+			count = 32;
+
+		float u[32], v[32];
+		for (int i = 0; i < count; ++i) {
+			u[i] = (float)data[offset + i * 2] / 6.0f;
+			v[i] = (float)data[offset + i * 2 + 1] / 6.0f;
+		}
+
+		drawFilledCharPolygon(u, v, count);
+		for (int i = 0; i < count; ++i) {
+			const int next = (i + 1) % count;
+			wallLine(corners, u[i], v[i], u[next], v[next], lineColor);
+		}
+
+		offset += coordCount;
+	}
+
+	if (macMode) {
+		const uint32 wallFill = _hasMacColors
+			? packMacColor(_macColors[8 + _level - 1].fg)
+			: (uint32)255;
+		_gfx->setWireframe(true, wallFill);
+	}
+}
+
+void ColonyEngine::getCellFace3D(int cellX, int cellY, bool ceiling, float corners[4][3]) {
+	float z = ceiling ? 160.0f : -160.0f;
+	float x0 = cellX * 256.0f;
+	float y0 = cellY * 256.0f;
+	float x1 = x0 + 256.0f;
+	float y1 = y0 + 256.0f;
+	const float eps = 0.1f;
+	if (ceiling)
+		z -= eps;
+	else
+		z += eps;
+
+	corners[0][0] = x0; corners[0][1] = y0; corners[0][2] = z;
+	corners[1][0] = x1; corners[1][1] = y0; corners[1][2] = z;
+	corners[2][0] = x1; corners[2][1] = y1; corners[2][2] = z;
+	corners[3][0] = x0; corners[3][1] = y1; corners[3][2] = z;
+}
+
+void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
+	const uint8 *map = mapFeatureAt(cellX, cellY, kDirCenter);
+	if (!map || map[0] == 0)
+		return;
+
+	float corners[4][3];
+	bool ceiling = (map[0] == 3 || map[0] == 4); // SMHOLECEIL, LGHOLECEIL
+	getCellFace3D(cellX, cellY, ceiling, corners);
+
+	// DOS uses color_wall (PenColor) for hole outlines.
+	// In our inverted 3D renderer: lit=black outlines on white fill, dark=white on black.
+	bool lit = (_corePower[_coreIndex] > 0);
+	uint32 holeColor = lit ? 0 : 7;
+
+	const bool macMode = (_renderMode == Common::kRenderMacintosh);
+	const bool macColors = (macMode && _hasMacColors);
+
+	// 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) {
+		if (macColors) {
+			uint32 fg = packMacColor(_macColors[macIdx].fg);
+			uint32 bg = packMacColor(_macColors[macIdx].bg);
+			int pat = _macColors[macIdx].pattern;
+			const byte *stipple = setupMacPattern(_gfx, pat, fg, bg);
+			wallPolygon(corners, u, v, cnt, fg);
+			if (stipple)
+			_gfx->setStippleData(nullptr);
+		} else if (macMode) {
+			_gfx->setStippleData(kStippleGray);
+			wallPolygon(corners, u, v, cnt, 0);
+			_gfx->setStippleData(nullptr);
+		} else {
+			wallPolygon(corners, u, v, cnt, holeColor);
+		}
+	};
+
+	switch (map[0]) {
+	case 1: // SMHOLEFLR
+	case 3: // SMHOLECEIL
+	{
+		float u[4] = {0.25f, 0.75f, 0.75f, 0.25f};
+		float v[4] = {0.25f, 0.25f, 0.75f, 0.75f};
+		drawHolePoly(u, v, 4, 30); // c_hole
+		break;
+	}
+	case 2: // LGHOLEFLR
+	case 4: // LGHOLECEIL
+	{
+		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+		drawHolePoly(u, v, 4, 30); // c_hole
+		break;
+	}
+	case 5: // HOTFOOT
+	{
+		float u[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+		float v[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+		if (macMode) {
+			drawHolePoly(u, v, 4, 31); // c_hotplate
+		} else {
+			// DOS non-polyfill: X pattern (two diagonals)
+			wallLine(corners, 0.0f, 0.0f, 1.0f, 1.0f, holeColor);
+			wallLine(corners, 1.0f, 0.0f, 0.0f, 1.0f, holeColor);
+		}
+		break;
+	}
+	default:
+		break;
+	}
+}
+
+void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
+	const uint8 *map = mapFeatureAt(cellX, cellY, direction);
+	if (!map || map[0] == kWallFeatureNone)
+		return;
+
+	// Backface culling: only draw features for the side facing the camera.
+	// This prevents backside decorations (like Level 2 lines) from bleeding through.
+	// We use non-inclusive comparisons so features remain visible while standing on the boundary.
+	switch (direction) {
+	case kDirNorth:
+		if (_me.yloc > (cellY + 1) * 256)
+			return;
+		break;
+	case kDirSouth:
+		if (_me.yloc < cellY * 256)
+			return;
+		break;
+	case kDirWest:
+		if (_me.xloc < cellX * 256)
+			return;
+		break;
+	case kDirEast:
+		if (_me.xloc > (cellX + 1) * 256)
+			return;
+		break;
+	default: break;
+	}
+
+	float corners[4][3];
+	getWallFace3D(cellX, cellY, direction, corners);
+	const bool macMode = (_renderMode == Common::kRenderMacintosh);
+	const bool macColors = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const bool lit = (_corePower[_coreIndex] > 0);
+	const uint32 wallFeatureFill = macColors
+		? packMacColor(lit ? _macColors[8 + _level - 1].fg : _macColors[6].bg)
+		: (lit ? (macMode ? 255u : 7u) : 0u);
+
+	// Wall faces are already filled with level-specific color (c_char0+level-1.fg)
+	// by the wall grid in renderCorridor3D(). Features are drawn on top.
+
+	// Helper lambda: Mac color fill for a wall feature polygon
+	auto macFillPoly = [&](const float *u, const float *v, int cnt, int macIdx) {
+		uint32 fg = packMacColor(_macColors[macIdx].fg);
+		uint32 bg = packMacColor(_macColors[macIdx].bg);
+		const byte *stipple = setupMacPattern(_gfx, _macColors[macIdx].pattern, fg, bg);
+		wallPolygon(corners, u, v, cnt, fg);
+		if (stipple)
+			_gfx->setStippleData(nullptr);
+	};
+
+	switch (map[0]) {
+	case kWallFeatureDoor: {
+		const uint32 doorColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
+		bool shipLevel = (_level == 1 || _level == 5 || _level == 6);
+
+		if (shipLevel) {
+			static const float uSs[8] = { 0.375f, 0.250f, 0.250f, 0.375f, 0.625f, 0.750f, 0.750f, 0.625f };
+			static const float vSs[8] = { 0.125f, 0.250f, 0.750f, 0.875f, 0.875f, 0.750f, 0.250f, 0.125f };
+			const uint32 shipDoorColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : (_wireframe ? 8u : 0u));
+
+			if (macMode) {
+				if (map[1] != 0) {
+					if (macColors) {
+						macFillPoly(uSs, vSs, 8, 15); // c_bulkhead
+					} else {
+						_gfx->setStippleData(kStippleGray);
+						wallPolygon(corners, uSs, vSs, 8, 0);
+						_gfx->setStippleData(nullptr);
+					}
+				} else {
+					// Open: fill with BLACK (passable opening)
+					_gfx->setWireframe(true, 0);
+					wallPolygon(corners, uSs, vSs, 8, 0);
+				}
+			} else if (!_wireframe) {
+				if (map[1] != 0) {
+					const byte *stipple = setupMacPattern(_gfx, kPatternLtGray, 0, 15);
+					wallPolygon(corners, uSs, vSs, 8, 0);
+					if (stipple)
+						_gfx->setStippleData(nullptr);
+				} else {
+					_gfx->setWireframe(true, 0);
+					wallPolygon(corners, uSs, vSs, 8, 0);
+				}
+			} else {
+				_gfx->setWireframe(true);
+				wallPolygon(corners, uSs, vSs, 8, 8);
+			}
+
+			for (int i = 0; i < 8; i++)
+				wallLine(corners, uSs[i], vSs[i], uSs[(i + 1) % 8], vSs[(i + 1) % 8], shipDoorColor);
+
+			if (map[1] != 0) {
+				wallLine(corners, 0.375f, 0.25f, 0.375f, 0.75f, shipDoorColor);
+				wallLine(corners, 0.375f, 0.75f, 0.625f, 0.75f, shipDoorColor);
+				wallLine(corners, 0.625f, 0.75f, 0.625f, 0.25f, shipDoorColor);
+				wallLine(corners, 0.625f, 0.25f, 0.375f, 0.25f, shipDoorColor);
+			}
+		} else {
+			static const float xl = 0.25f, xr = 0.75f;
+			static const float yb = 0.0f, yt = 0.875f;
+
+			if (macMode) {
+				float ud[4] = {xl, xr, xr, xl};
+				float vd[4] = {yb, yb, yt, yt};
+				if (map[1] != 0) {
+					if (macColors) {
+						macFillPoly(ud, vd, 4, 16); // c_door
+					} else {
+						_gfx->setStippleData(kStippleGray);
+						wallPolygon(corners, ud, vd, 4, 0);
+						_gfx->setStippleData(nullptr);
+					}
+				} else {
+					// Open: fill with BLACK (passable opening)
+					_gfx->setWireframe(true, 0);
+					wallPolygon(corners, ud, vd, 4, 0);
+					_gfx->setWireframe(true, 255);
+				}
+			}
+
+			wallLine(corners, xl, yb, xl, yt, doorColor);
+			wallLine(corners, xl, yt, xr, yt, doorColor);
+			wallLine(corners, xr, yt, xr, yb, doorColor);
+			wallLine(corners, xr, yb, xl, yb, doorColor);
+
+			if (map[1] != 0) {
+				wallLine(corners, 0.3125f, 0.4375f, 0.6875f, 0.4375f, doorColor);
+			}
+		}
+		break;
+	}
+	case kWallFeatureWindow: {
+		const uint32 winColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
+		float xl = 0.25f, xr = 0.75f;
+		float yb = 0.25f, yt = 0.75f;
+		float xc = 0.5f, yc = 0.5f;
+
+		// Mac: fill window pane
+		if (macMode) {
+			float uw[4] = {xl, xr, xr, xl};
+			float vw[4] = {yb, yb, yt, yt};
+			if (macColors) {
+				macFillPoly(uw, vw, 4, 17); // c_window
+			} else {
+				_gfx->setStippleData(kStippleDkGray);
+				wallPolygon(corners, uw, vw, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
+		}
+
+		wallLine(corners, xl, yb, xl, yt, winColor);
+		wallLine(corners, xl, yt, xr, yt, winColor);
+		wallLine(corners, xr, yt, xr, yb, winColor);
+		wallLine(corners, xr, yb, xl, yb, winColor);
+		wallLine(corners, xc, yb, xc, yt, winColor);
+		wallLine(corners, xl, yc, xr, yc, winColor);
+		break;
+	}
+	case kWallFeatureShelves: {
+		// DOS drawbooks: recessed bookcase with 3D depth.
+		const uint32 shelfColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
+
+		// Mac: fill shelves area
+		if (macMode) {
+			float us[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+			float vs[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+			if (macColors) {
+				macFillPoly(us, vs, 4, 18); // c_shelves
+			} else {
+				_gfx->setStippleData(kStippleLtGray);
+				wallPolygon(corners, us, vs, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
+		}
+		float bx = 0.1875f, bxr = 0.8125f;
+		float by = 0.1875f, byt = 0.8125f;
+		// Back face rectangle
+		wallLine(corners, bx, by, bxr, by, shelfColor);
+		wallLine(corners, bxr, by, bxr, byt, shelfColor);
+		wallLine(corners, bxr, byt, bx, byt, shelfColor);
+		wallLine(corners, bx, byt, bx, by, shelfColor);
+		// Connecting lines (front corners to back corners)
+		wallLine(corners, 0.0f, 0.0f, bx, by, shelfColor);
+		wallLine(corners, 0.0f, 1.0f, bx, byt, shelfColor);
+		wallLine(corners, 1.0f, 0.0f, bxr, by, shelfColor);
+		wallLine(corners, 1.0f, 1.0f, bxr, byt, shelfColor);
+		// 7 shelf lines across front face (DOS split7 at 1/8..7/8 intervals)
+		for (int i = 1; i <= 7; i++) {
+			float v = (float)i / 8.0f;
+			wallLine(corners, 0.0f, v, 1.0f, v, shelfColor);
+		}
+		break;
+	}
+	case kWallFeatureUpStairs: {
+		// DOS: draw_up_stairs  staircase ascending into the wall with perspective
+		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
+
+		// Mac: fill entire wall face (c_upstairs)
+		if (_renderMode == Common::kRenderMacintosh) {
+			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+			float vf2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+			if (macColors) {
+				macFillPoly(uf, vf2, 4, 19); // c_upstairs1
+			} else {
+				_gfx->setStippleData(kStippleGray);
+				wallPolygon(corners, uf, vf2, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
+		}
+
+		// Perspective convergence: back of passage at ~1/3 width (1 cell deep)
+		float ul[7], ur[7], vf[7], vc[7];
+		for (int i = 0; i < 7; i++) {
+			float f = (i + 1) / 8.0f;
+			float inset = f * (1.0f / 3.0f);
+			ul[i] = inset;
+			ur[i] = 1.0f - inset;
+			vf[i] = inset;        // floor rises toward center
+			vc[i] = 1.0f - inset; // ceiling drops toward center
+		}
+		// Step height: at depth d, step s is at fraction (s+1)/8 from floor to ceiling
+		auto vh = [&](int d, int s) -> float {
+			return vf[d] + (s + 1) / 8.0f * (vc[d] - vf[d]);
+		};
+		// Back of passage (full depth)
+		float bi = 1.0f / 3.0f; // back inset
+		float bu = bi, bur = 1.0f - bi, bvc = 1.0f - bi;
+
+		// 1. Side wall verticals at back of passage
+		wallLine(corners, bu, bvc, bu, 0.5f, col);
+		wallLine(corners, bur, 0.5f, bur, bvc, col);
+
+		// 2. Back wall landing (depth 6 to full depth)
+		wallLine(corners, ul[6], vh(6, 6), bu, bvc, col);
+		wallLine(corners, bu, bvc, bur, bvc, col);
+		wallLine(corners, bur, bvc, ur[6], vh(6, 6), col);
+		wallLine(corners, ur[6], vh(6, 6), ul[6], vh(6, 6), col);
+
+		// 3. First step tread (floor from wall face to depth 0)
+		wallLine(corners, 0.0f, 0.0f, ul[0], vf[0], col);
+		wallLine(corners, ul[0], vf[0], ur[0], vf[0], col);
+		wallLine(corners, ur[0], vf[0], 1.0f, 0.0f, col);
+		wallLine(corners, 1.0f, 0.0f, 0.0f, 0.0f, col);
+
+		// 4. First step riser (at depth 0)
+		wallLine(corners, ul[0], vh(0, 0), ul[0], vf[0], col);
+		wallLine(corners, ur[0], vf[0], ur[0], vh(0, 0), col);
+		wallLine(corners, ur[0], vh(0, 0), ul[0], vh(0, 0), col);
+
+		// 5. Step treads (i=3..0: depth i to depth i+1)
+		for (int i = 3; i >= 0; i--) {
+			wallLine(corners, ul[i], vh(i, i), ul[i + 1], vh(i + 1, i), col);
+			wallLine(corners, ul[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i), col);
+			wallLine(corners, ur[i + 1], vh(i + 1, i), ur[i], vh(i, i), col);
+			wallLine(corners, ur[i], vh(i, i), ul[i], vh(i, i), col);
+		}
+
+		// 6. Step risers (i=5..0: vertical face at depth i+1)
+		for (int i = 5; i >= 0; i--) {
+			wallLine(corners, ul[i + 1], vh(i + 1, i + 1), ul[i + 1], vh(i + 1, i), col);
+			wallLine(corners, ul[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i), col);
+			wallLine(corners, ur[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i + 1), col);
+			wallLine(corners, ur[i + 1], vh(i + 1, i + 1), ul[i + 1], vh(i + 1, i + 1), col);
+		}
+
+		// 7. Handrails: from center of wall edges up to near-ceiling at mid-depth
+		wallLine(corners, 0.0f, 0.5f, ul[3], vc[0], col);
+		wallLine(corners, 1.0f, 0.5f, ur[3], vc[0], col);
+		break;
+	}
+	case kWallFeatureDnStairs: {
+		// DOS: draw_dn_stairs  staircase descending into the wall with perspective
+		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
+
+		// Mac: fill entire wall face (c_dnstairs)
+		if (_renderMode == Common::kRenderMacintosh) {
+			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+			float vf2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+			if (macColors) {
+				macFillPoly(uf, vf2, 4, 21); // c_dnstairs
+			} else {
+				_gfx->setStippleData(kStippleGray);
+				wallPolygon(corners, uf, vf2, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
+		}
+
+		float ul[7], ur[7], vf[7], vc[7];
+		for (int i = 0; i < 7; i++) {
+			float f = (i + 1) / 8.0f;
+			float inset = f * (1.0f / 3.0f);
+			ul[i] = inset;
+			ur[i] = 1.0f - inset;
+			vf[i] = inset;
+			vc[i] = 1.0f - inset;
+		}
+		float bi = 1.0f / 3.0f;
+		float bu = bi, bur = 1.0f - bi;
+
+		// 1. Ceiling: front ceiling slopes down to mid-depth
+		wallLine(corners, 0.0f, 1.0f, ul[3], vc[3], col);
+		wallLine(corners, ul[3], vc[3], ur[3], vc[3], col);
+		wallLine(corners, ur[3], vc[3], 1.0f, 1.0f, col);
+
+		// 2. Slant: from mid-depth ceiling down to center at back
+		wallLine(corners, ul[3], vc[3], bu, 0.5f, col);
+		wallLine(corners, bu, 0.5f, bur, 0.5f, col);
+		wallLine(corners, bur, 0.5f, ur[3], vc[3], col);
+
+		// 3. Side wall verticals: from center at back down to floor level
+		wallLine(corners, bu, 0.5f, bu, vf[0], col);
+		wallLine(corners, bur, 0.5f, bur, vf[0], col);
+
+		// 4. First step (floor from wall face to depth 0)
+		wallLine(corners, 0.0f, 0.0f, ul[0], vf[0], col);
+		wallLine(corners, ul[0], vf[0], ur[0], vf[0], col);
+		wallLine(corners, ur[0], vf[0], 1.0f, 0.0f, col);
+		wallLine(corners, 1.0f, 0.0f, 0.0f, 0.0f, col);
+
+		// 5. Handrails: from center of wall edges down to floor at mid-depth
+		wallLine(corners, 0.0f, 0.5f, ul[3], vf[3], col);
+		wallLine(corners, 1.0f, 0.5f, ur[3], vf[3], col);
+		break;
+	}
+	case kWallFeatureChar:
+		wallChar(corners, map[1]);
+		break;
+	case kWallFeatureGlyph: {
+		// DOS wireframe: PenColor(realcolor[vDKGRAY]) = 8
+		const uint32 glyphColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
+
+		// Mac: fill glyph area
+		if (macMode) {
+			float ug[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+			float vg[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+			if (macColors) {
+				macFillPoly(ug, vg, 4, 22); // c_glyph
+			} else {
+				_gfx->setStippleData(kStippleGray);
+				wallPolygon(corners, ug, vg, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
+		}
+
+		for (int i = 0; i < 7; i++) {
+			float v = 0.2f + i * 0.1f;
+			wallLine(corners, 0.2f, v, 0.8f, v, glyphColor);
+		}
+		break;
+	}
+	case kWallFeatureElevator: {
+		const uint32 elevColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
+		float xl = 0.2f, xr = 0.8f;
+		float yb = 0.1f, yt = 0.9f;
+
+		// Mac: fill elevator door
+		if (macMode) {
+			float ue[4] = {xl, xr, xr, xl};
+			float ve[4] = {yb, yb, yt, yt};
+			if (macColors) {
+				macFillPoly(ue, ve, 4, 23); // c_elevator
+			} else {
+				_gfx->setStippleData(kStippleGray);
+				wallPolygon(corners, ue, ve, 4, 0);
+				_gfx->setStippleData(nullptr);
+			}
+		}
+
+		wallLine(corners, xl, yb, xl, yt, elevColor);
+		wallLine(corners, xl, yt, xr, yt, elevColor);
+		wallLine(corners, xr, yt, xr, yb, elevColor);
+		wallLine(corners, xr, yb, xl, yb, elevColor);
+		wallLine(corners, 0.5f, yb, 0.5f, yt, elevColor);
+		break;
+	}
+	case kWallFeatureTunnel: {
+		// Tunnel: hexagonal opening from Grid (0,0 0,5 1,6 5,6 6,5 6,0)
+		static const float uT[6] = { 0.0f,    0.0f,    1/6.0f,  5/6.0f,  1.0f,    1.0f };
+		static const float vT[6] = { 0.0f,    0.750f,  0.875f,  0.875f,  0.750f,  0.0f };
+		if (_renderMode == Common::kRenderMacintosh) {
+			if (macColors) {
+				macFillPoly(uT, vT, 6, 24); // c_tunnel
+			} else {
+				_gfx->setStippleData(kStippleGray);
+				wallPolygon(corners, uT, vT, 6, 0);
+				_gfx->setStippleData(nullptr);
+			}
+		} else {
+			wallPolygon(corners, uT, vT, 6, 0); // vBLACK outline
+		}
+		break;
+	}
+	case kWallFeatureAirlock: {
+		// Direct port of drawALOpen/drawALClosed from WALLFTRS.C / wallftrs.c.
+		// These are the exact split7x7 positions on the wall face.
+		static const float u[8] = {0.125f, 0.25f, 0.5f, 0.75f, 0.875f, 0.75f, 0.5f, 0.25f};
+		static const float v[8] = {0.5f, 0.75f, 0.875f, 0.75f, 0.5f, 0.25f, 0.125f, 0.25f};
+		static const float spokeU[8] = {0.375f, 0.5f, 0.625f, 0.625f, 0.625f, 0.5f, 0.375f, 0.375f};
+		static const float spokeV[8] = {0.625f, 0.625f, 0.625f, 0.5f, 0.375f, 0.375f, 0.375f, 0.5f};
+		static const float centerU = 0.5f;
+		static const float centerV = 0.5f;
+
+		if (map[1] == 0) {
+			// Original drawALOpen: solid black opening on both DOS and Mac.
+			if (macMode || !_wireframe)
+				_gfx->setWireframe(true, 0);
+			else
+				_gfx->setWireframe(true);
+			wallPolygon(corners, u, v, 8, 0);
+		} else {
+			// Mac: fill airlock when closed
+			if (macMode) {
+				if (macColors) {
+					macFillPoly(u, v, 8, 25); // c_airlock
+				} else {
+					_gfx->setStippleData(kStippleGray);
+					wallPolygon(corners, u, v, 8, 0);
+					_gfx->setStippleData(nullptr);
+				}
+			} else if (!_wireframe) {
+				const byte *stipple = setupMacPattern(_gfx, kPatternLtGray, 0, 15);
+				wallPolygon(corners, u, v, 8, 0);
+				if (stipple)
+					_gfx->setStippleData(nullptr);
+			}
+
+			const uint32 airlockColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : 8);
+			for (int i = 0; i < 8; i++) {
+				int n = (i + 1) % 8;
+				wallLine(corners, u[i], v[i], u[n], v[n], airlockColor);
+			}
+			for (int i = 0; i < 8; i++) {
+				wallLine(corners, u[i], v[i], spokeU[i], spokeV[i], airlockColor);
+				wallLine(corners, spokeU[i], spokeV[i], centerU, centerV, airlockColor);
+			}
+		}
+		break;
+	}
+	case kWallFeatureColor: {
+		// Mac drawColor / DOS drawColor: 4 horizontal bands.
+		// 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 (macColors) {
+				// Mac drawColor: map[i+1] selects color (0→c_color0..3→c_color3, 4→BLACK).
+				// Values >= 5: animated = (map[i+1] + _displayCount) % 5.
+				for (int i = 0; i < 4; i++) {
+					int val = map[i + 1];
+					if (val > 4)
+						val = (val + _displayCount / 6) % 5;
+					float vb = (3 - i) / 4.0f;
+					float vt = (4 - i) / 4.0f;
+					float ub[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+					float vb4[4] = {vb, vb, vt, vt};
+					if (val == 4) {
+						// BLACK: solid black fill
+						_gfx->setWireframe(true, (uint32)0xFF000000);
+						wallPolygon(corners, ub, vb4, 4, 0xFF000000);
+						_gfx->setWireframe(true, packMacColor(_macColors[8 + _level - 1].fg));
+					} else {
+						macFillPoly(ub, vb4, 4, 26 + val); // c_color0 + val
+					}
+				}
+			} else {
+				static const byte *stripPatterns[5] = {
+					nullptr, kStippleLtGray, kStippleGray, kStippleDkGray, nullptr
+				};
+				for (int i = 0; i < 4; i++) {
+					int pat = map[i + 1];
+					if (pat > 4)
+						pat = (pat + _displayCount / 6) % 5; // animated cycling
+					float vb = (3 - i) / 4.0f;
+					float vt = (4 - i) / 4.0f;
+					float ub[4] = {0.0f, 1.0f, 1.0f, 0.0f};
+					float vb4[4] = {vb, vb, vt, vt};
+					if (pat == 4) {
+						_gfx->setWireframe(true, 0);
+						wallPolygon(corners, ub, vb4, 4, 0);
+						_gfx->setWireframe(true, 255);
+					} else if (pat == 0) {
+						// WHITE: no fill needed (wall background is white)
+					} else {
+						_gfx->setStippleData(stripPatterns[pat]);
+						wallPolygon(corners, ub, vb4, 4, 0);
+						_gfx->setStippleData(nullptr);
+					}
+				}
+			}
+		}
+
+		// EGA / Mac B&W: colored lines at band boundaries.
+		// DOS non-polyfill draws 3 lines; we animate line colors for bands > 4.
+		// Mac color mode uses macFillPoly for band fills instead.
+		if (!macColors) {
+			for (int i = 1; i <= 3; i++) {
+				int val = map[i];
+				if (val > 4)
+					val = (val + _displayCount / 6) % 5; // animated cycling
+				uint32 c = 120 + val * 20;
+				if (c == 120 && val == 0 && !map[1] && !map[2] && !map[3] && !map[4])
+					c = 100 + (_level * 15);
+				float v = (float)i / 4.0f;
+				wallLine(corners, 0.0f, v, 1.0f, v, c);
+			}
+		}
+		break;
+	}
+	default:
+		break;
+	}
+
+	_gfx->setStippleData(nullptr);
+	_gfx->setWireframe(true, wallFeatureFill);
+}
+
+void ColonyEngine::drawWallFeatures3D() {
+	if (_corePower[_coreIndex] == 0)
+		return;
+
+	for (int y = 0; y < 31; y++) {
+		for (int x = 0; x < 31; x++) {
+			if (!_visibleCell[x][y])
+				continue;
+			drawCellFeature3D(x, y);
+			for (int dir = 0; dir < 4; dir++) {
+				const uint8 *map = mapFeatureAt(x, y, dir);
+				if (map && map[0] != kWallFeatureNone) {
+					drawWallFeature3D(x, y, dir);
+				}
+			}
+		}
+	}
+}
+
+} // End of namespace Colony
diff --git a/engines/colony/render_internal.h b/engines/colony/render_internal.h
new file mode 100644
index 00000000000..25f47b3135d
--- /dev/null
+++ b/engines/colony/render_internal.h
@@ -0,0 +1,123 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef COLONY_RENDER_INTERNAL_H
+#define COLONY_RENDER_INTERNAL_H
+
+#include "colony/colony.h"
+#include "colony/gfx.h"
+
+namespace Colony {
+
+// Mac Classic dither patterns (from colorize.c cColor[].pattern).
+// Mac Classic was a 1-bit B&W display. QuickDraw used 8x8 dither patterns
+// to simulate grayscale: WHITE, LTGRAY, GRAY, DKGRAY, BLACK, CLEAR.
+// Rendered via GL_POLYGON_STIPPLE: two-pass fill (white bg + black fg with stipple mask).
+enum MacPattern {
+	kPatternWhite  = 0, // Solid white (all background)
+	kPatternLtGray = 1, // 25% foreground (sparse black dots on white)
+	kPatternGray   = 2, // 50% foreground (checkerboard)
+	kPatternDkGray = 3, // 75% foreground (dense black, sparse white)
+	kPatternBlack  = 4, // Solid black (all foreground)
+	kPatternClear  = 5  // Outline only (no fill)
+};
+
+// Internal-only surface color for Mac wall helper objects (FWALL/CWALL).
+// In the original Mac renderer these use c_lwall/c_dwall, which are
+// special corridor-wall colors rather than normal material palette entries.
+static const int kColorCorridorWall = 1000;
+
+// GL_POLYGON_STIPPLE patterns: 32x32 bit arrays (128 bytes), tiled from 8x8 Mac patterns.
+// Bit=1 → fragment drawn (black foreground), bit=0 → fragment discarded (white background shows).
+// Mac QuickDraw convention: bit=1 = foreground (BLACK), bit=0 = background (WHITE).
+static const byte kStippleLtGray[128] = {
+	// 0x88,0x22 alternating rows = 25% coverage (sparse dots)
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+	0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22, 0x88,0x88,0x88,0x88, 0x22,0x22,0x22,0x22,
+};
+
+static const byte kStippleGray[128] = {
+	// 0xAA,0x55 alternating rows = 50% coverage (checkerboard)
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+	0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55, 0xAA,0xAA,0xAA,0xAA, 0x55,0x55,0x55,0x55,
+};
+
+static const byte kStippleDkGray[128] = {
+	// 0x77,0xDD alternating rows = 75% coverage (dense dots)
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+	0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD, 0x77,0x77,0x77,0x77, 0xDD,0xDD,0xDD,0xDD,
+};
+
+// Lookup: MacPattern enum → GL stipple data pointer (null for solid/clear patterns)
+static const byte *kMacStippleData[] = {
+	nullptr,        // kPatternWhite  - solid white, no stipple
+	kStippleLtGray, // kPatternLtGray - 25% black dots
+	kStippleGray,   // kPatternGray   - 50% checkerboard
+	kStippleDkGray, // kPatternDkGray - 75% black
+	nullptr,        // kPatternBlack  - solid black, no stipple
+	nullptr         // kPatternClear  - outline only
+};
+
+// Pack Mac 16-bit RGB into 32-bit ARGB with 0xFF000000 marker for direct RGB.
+static uint32 packMacColor(const uint16 rgb[3]) {
+	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
+}
+
+// Helper: set up Mac color stipple rendering for a given cColor[] pattern.
+// Configures setMacColors/setStippleData/setWireframe for the pattern type.
+// Returns the stipple pointer (null for solid patterns)  caller must clear with setStippleData(nullptr).
+static const byte *setupMacPattern(Renderer *gfx, int pattern, uint32 fg, uint32 bg) {
+	const byte *stipple = (pattern >= 1 && pattern <= 3) ? kMacStippleData[pattern] : nullptr;
+	if (stipple) {
+		gfx->setMacColors(fg, bg);
+		gfx->setStippleData(stipple);
+		gfx->setWireframe(true, bg);
+	} else if (pattern == 4) {
+		// BLACK: solid fg fill
+		gfx->setWireframe(true, fg);
+	} else {
+		// WHITE (0): solid bg fill + fg outline
+		gfx->setWireframe(true, bg);
+	}
+	return stipple;
+}
+
+} // namespace Colony
+
+#endif // COLONY_RENDER_INTERNAL_H
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
new file mode 100644
index 00000000000..1c960a758e6
--- /dev/null
+++ b/engines/colony/render_objects.cpp
@@ -0,0 +1,1329 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "colony/colony.h"
+#include "common/system.h"
+
+namespace Colony {
+
+// Must match the value in render_internal.h.
+static const int kColorCorridorWall = 1000;
+
+static const int kScreenPts[8][3] = {
+	{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
+	{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
+};
+static const int kScreenSurf[4][8] = {
+	{kColorBlack, 4, 0, 3, 7, 4, 0, 0}, {kColorBlack, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBlack, 4, 1, 0, 4, 5, 0, 0}, {kColorBlack, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kTableTopPts[4][3] = {
+	{-128, 128, 100}, {128, 128, 100}, {128, -128, 100}, {-128, -128, 100}
+};
+static const int kTableTopSurf[1][8] = {{kColorTable, 4, 3, 2, 1, 0, 0, 0}};
+static const int kTableBasePts[8][3] = {
+	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+	{-5, 5, 100}, {5, 5, 100}, {5, -5, 100}, {-5, -5, 100}
+};
+static const int kTableBaseSurf[4][8] = {
+	{kColorTableBase, 4, 0, 3, 7, 4, 0, 0}, {kColorTableBase, 4, 3, 2, 6, 7, 0, 0},
+	{kColorTableBase, 4, 1, 0, 4, 5, 0, 0}, {kColorTableBase, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kBedPostPts[4][3] = {
+	{-82, 128, 100}, {82, 128, 100}, {82, 128, 0}, {-82, 128, 0}
+};
+static const int kBedPostSurf[1][8] = {{kColorBed, 4, 3, 2, 1, 0, 0, 0}};
+static const int kBedBlanketPts[8][3] = {
+	{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
+	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
+};
+static const int kBlanketSurf[4][8] = {
+	{kColorBlanket, 4, 0, 3, 7, 4, 0, 0}, {kColorBlanket, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBlanket, 4, 2, 1, 5, 6, 0, 0}, {kColorBlanket, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kBedSheetPts[8][3] = {
+	{-80, 128, 30}, {80, 128, 30}, {80, 70, 30}, {-80, 70, 30},
+	{-80, 128, 60}, {80, 128, 60}, {80, 70, 60}, {-80, 70, 60}
+};
+static const int kSheetSurf[3][8] = {
+	{kColorSheet, 4, 0, 3, 7, 4, 0, 0}, {kColorSheet, 4, 2, 1, 5, 6, 0, 0},
+	{kColorSheet, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kBBedBlanketPts[8][3] = {
+	{-128, 70, 0}, {128, 70, 0}, {128, -175, 0}, {-128, -175, 0},
+	{-128, 70, 60}, {128, 70, 60}, {128, -175, 60}, {-128, -175, 60}
+};
+static const int kBBedSheetPts[8][3] = {
+	{-128, 128, 30}, {128, 128, 30}, {128, 70, 30}, {-128, 70, 30},
+	{-128, 128, 60}, {128, 128, 60}, {128, 70, 60}, {-128, 70, 60}
+};
+static const int kBBedPostPts[4][3] = {
+	{-130, 128, 100}, {130, 128, 100}, {130, 128, 0}, {-130, 128, 0}
+};
+static const int kDeskTopPts[4][3] = {
+	{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
+};
+static const int kDeskTopSurf[1][8] = {{kColorDeskTop, 4, 3, 2, 1, 0, 0, 0}};
+static const int kDeskLeftPts[8][3] = {
+	{-135, 95, 0}, {-55, 95, 0}, {-55, -95, 0}, {-135, -95, 0},
+	{-135, 95, 100}, {-55, 95, 100}, {-55, -95, 100}, {-135, -95, 100}
+};
+static const int kDeskRightPts[8][3] = {
+	{55, 95, 0}, {135, 95, 0}, {135, -95, 0}, {55, -95, 0},
+	{55, 95, 100}, {135, 95, 100}, {135, -95, 100}, {55, -95, 100}
+};
+static const int kDeskCabSurf[4][8] = {
+	{kColorDesk, 4, 0, 3, 7, 4, 0, 0}, {kColorDesk, 4, 3, 2, 6, 7, 0, 0},
+	{kColorDesk, 4, 1, 0, 4, 5, 0, 0}, {kColorDesk, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kSeatPts[4][3] = {
+	{-40, 210, 60}, {40, 210, 60}, {40, 115, 60}, {-40, 115, 60}
+};
+static const int kSeatSurf[1][8] = {{kColorDeskChair, 4, 3, 2, 1, 0, 0, 0}};
+static const int kArmLeftPts[4][3] = {
+	{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
+};
+static const int kArmRightPts[4][3] = {
+	{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
+};
+static const int kArmSurf[2][8] = {
+	{kColorClear, 4, 3, 2, 1, 0, 0, 0}, {kColorClear, 4, 0, 1, 2, 3, 0, 0}
+};
+static const int kBackPts[4][3] = {
+	{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
+};
+static const int kBackSurf[2][8] = {
+	{kColorDeskChair, 4, 3, 2, 1, 0, 0, 0}, {kColorDeskChair, 4, 0, 1, 2, 3, 0, 0}
+};
+static const int kComputerPts[8][3] = {
+	{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
+	{70, 25, 120}, {120, 25, 120}, {120, -25, 120}, {70, -25, 120}
+};
+static const int kMonitorPts[8][3] = {
+	{75, 20, 120}, {115, 20, 120}, {115, -20, 120}, {75, -20, 120},
+	{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
+};
+static const int kComputerSurf[5][8] = {
+	{kColorMac, 4, 7, 6, 5, 4, 0, 0}, {kColorMac, 4, 0, 3, 7, 4, 0, 0},
+	{kColorMac, 4, 3, 2, 6, 7, 0, 0}, {kColorMac, 4, 1, 0, 4, 5, 0, 0},
+	{kColorMac, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kDeskScreenPts[4][3] = {
+	{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
+};
+static const int kDeskScreenSurf[1][8] = {{kColorMacScreen, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCSeatPts[4][3] = {
+	{-40, 40, 60}, {40, 40, 60}, {40, -40, 60}, {-40, -40, 60}
+};
+static const int kCSeatSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCArmLeftPts[4][3] = {
+	{-50, 40, 90}, {-40, 40, 60}, {-40, -40, 60}, {-50, -40, 90}
+};
+static const int kCArmLeftSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCArmRightPts[4][3] = {
+	{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
+};
+static const int kCArmRightSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCBackPts[4][3] = {
+	{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
+};
+static const int kCBackSurf[2][8] = {
+	{kColorChair, 4, 3, 2, 1, 0, 0, 0}, {kColorChair, 4, 0, 1, 2, 3, 0, 0}
+};
+static const int kCBasePts[8][3] = {
+	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+	{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
+};
+static const int kCBaseSurf[4][8] = {
+	{kColorChairBase, 4, 0, 3, 7, 4, 0, 0}, {kColorChairBase, 4, 3, 2, 6, 7, 0, 0},
+	{kColorChairBase, 4, 1, 0, 4, 5, 0, 0}, {kColorChairBase, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kConsolePts[8][3] = {
+	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
+	{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
+};
+static const int kConsoleSurf[5][8] = {
+	{kColorConsole, 4, 4, 0, 3, 7, 0, 0}, {kColorConsole, 4, 7, 3, 2, 6, 0, 0},
+	{kColorConsole, 4, 5, 1, 0, 4, 0, 0}, {kColorConsole, 4, 6, 2, 1, 5, 0, 0},
+	{kColorBlack, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kCouchSurf[5][8] = {
+	{kColorCouch, 4, 0, 3, 7, 4, 0, 0}, {kColorCouch, 4, 3, 2, 6, 7, 0, 0},
+	{kColorCouch, 4, 1, 0, 4, 5, 0, 0}, {kColorCouch, 4, 2, 1, 5, 6, 0, 0},
+	{kColorCouch, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kACouchPts[8][3] = {
+	{-50, 150, 0}, {50, 150, 0}, {50, -150, 0}, {-50, -150, 0},
+	{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50}
+};
+static const int kBCouchPts[8][3] = {
+	{-80, 150, 0}, {-45, 150, 0}, {-45, -150, 0}, {-80, -150, 0},
+	{-80, 150, 120}, {-55, 150, 120}, {-55, -150, 120}, {-80, -150, 120}
+};
+static const int kCCouchPts[8][3] = {
+	{-70, 170, 0}, {50, 170, 0}, {50, 150, 0}, {-70, 150, 0},
+	{-70, 170, 80}, {50, 170, 80}, {50, 150, 80}, {-70, 150, 80}
+};
+static const int kDCouchPts[8][3] = {
+	{-70, -150, 0}, {50, -150, 0}, {50, -170, 0}, {-70, -170, 0},
+	{-70, -150, 80}, {50, -150, 80}, {50, -170, 80}, {-70, -170, 80}
+};
+static const int kAChairPts[8][3] = {
+	{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
+	{-50, 50, 50}, {50, 50, 50}, {50, -50, 50}, {-50, -50, 50}
+};
+static const int kBChairPts[8][3] = {
+	{-80, 50, 0}, {-45, 50, 0}, {-45, -50, 0}, {-80, -50, 0},
+	{-80, 50, 120}, {-55, 50, 120}, {-55, -50, 120}, {-80, -50, 120}
+};
+static const int kCChairPts2[8][3] = {
+	{-70, 70, 0}, {50, 70, 0}, {50, 50, 0}, {-70, 50, 0},
+	{-70, 70, 80}, {50, 70, 80}, {50, 50, 80}, {-70, 50, 80}
+};
+static const int kDChairPts[8][3] = {
+	{-70, -50, 0}, {50, -50, 0}, {50, -70, 0}, {-70, -70, 0},
+	{-70, -50, 80}, {50, -50, 80}, {50, -70, 80}, {-70, -70, 80}
+};
+static const int kTVBodyPts[8][3] = {
+	{-30, 60, 0}, {30, 60, 0}, {30, -60, 0}, {-30, -60, 0},
+	{-30, 60, 120}, {30, 60, 120}, {30, -60, 120}, {-30, -60, 120}
+};
+static const int kTVBodySurf[5][8] = {
+	{kColorTV, 4, 0, 3, 7, 4, 0, 0}, {kColorTV, 4, 3, 2, 6, 7, 0, 0},
+	{kColorTV, 4, 1, 0, 4, 5, 0, 0}, {kColorTV, 4, 2, 1, 5, 6, 0, 0},
+	{kColorTV, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kTVScreenPts[4][3] = {
+	{30, 50, 10}, {30, -50, 10}, {30, 50, 110}, {30, -50, 110}
+};
+static const int kTVScreenSurf[1][8] = {{kColorTVScreen, 4, 1, 0, 2, 3, 0, 0}};
+static const int kDrawerPts[8][3] = {
+	{-80, 70, 0}, {0, 70, 0}, {0, -70, 0}, {-80, -70, 0},
+	{-80, 70, 100}, {0, 70, 100}, {0, -70, 100}, {-80, -70, 100}
+};
+static const int kDrawerSurf[5][8] = {
+	{kColorDrawer, 4, 0, 3, 7, 4, 0, 0}, {kColorDrawer, 4, 3, 2, 6, 7, 0, 0},
+	{kColorDrawer, 4, 1, 0, 4, 5, 0, 0}, {kColorDrawer, 4, 2, 1, 5, 6, 0, 0},
+	{kColorDrawer, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kMirrorPts[4][3] = {
+	{-80, 65, 100}, {-80, -65, 100}, {-80, 65, 210}, {-80, -65, 210}
+};
+static const int kMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
+
+// Bathtub geometry
+static const int kTubPts[8][3] = {
+	{-128, 128,  0}, {   0, 128,  0}, {   0,-128,  0}, {-128,-128,  0},
+	{-128, 128, 70}, {   0, 128, 70}, {   0,-128, 70}, {-128,-128, 70}
+};
+static const int kTubSurf[5][8] = {
+	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
+	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kDTubPts[6][3] = {
+	{-16, 112, 70}, {-8, 0, 70}, {-16, -112, 70}, {-112, -112, 70}, {-120, 0, 70}, {-112, 112, 70}
+};
+static const int kDTubSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
+
+// Toilet geometry
+static const int kAToiletPts[8][3] = {
+	{-128, 45, 30}, {-100, 45, 30}, {-100, -45, 30}, {-128, -45, 30},
+	{-128, 45, 100}, {-100, 45, 100}, {-100, -45, 100}, {-128, -45, 100}
+};
+static const int kAToiletSurf[5][8] = {
+	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
+	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kBToiletPts[12][3] = {
+	{-100, 20, 50}, {-60, 40, 50}, {-20, 20, 50}, {-20, -20, 50}, {-60, -40, 50}, {-100, -20, 50},
+	{-80, 10,  0}, {-60, 20,  0}, {-40, 10,  0}, {-40, -10,  0}, {-60, -20,  0}, {-80, -10,  0}
+};
+static const int kBToiletSurf[7][8] = {
+	{kColorBath, 4, 0, 1, 7, 6, 0, 0}, {kColorBath, 4, 1, 2, 8, 7, 0, 0}, {kColorBath, 4, 2, 3, 9, 8, 0, 0},
+	{kColorBath, 4, 3, 4, 10, 9, 0, 0}, {kColorBath, 4, 4, 5, 11, 10, 0, 0}, {kColorBath, 4, 5, 0, 6, 11, 0, 0},
+	{kColorBath, 6, 5, 4, 3, 2, 1, 0}
+};
+static const int kCToiletPts[6][3] = {
+	{-95, 15, 50}, {-60, 35, 50}, {-25, 15, 50}, {-25, -15, 50}, {-60, -35, 50}, {-95, -15, 50}
+};
+static const int kCToiletSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
+static const int kDToiletPts[6][3] = {
+	{-100, 20, 50}, {-100, 40, 90}, {-100, 20, 130}, {-100, -20, 130}, {-100, -40, 90}, {-100, -20, 50}
+};
+static const int kDToiletSurf[1][8] = {{kColorLtGreen, 6, 5, 4, 3, 2, 1, 0}};
+static const int kEToiletPts[4][3] = {
+	{-128,-128, 20}, {-128,-128, 200}, { 128,-128, 200}, { 128,-128, 20}
+};
+static const int kEToiletSurf[1][8] = {{kColorDkGray, 4, 0, 1, 2, 3, 0, 0}};
+
+// Sink geometry
+static const int kSinkPts[8][3] = {
+	{-128, 50, 70}, {-50, 50, 90}, {-50,-50, 90}, {-128,-50, 70},
+	{-128, 50, 110}, {-50, 50, 110}, {-50,-50, 110}, {-128,-50, 110}
+};
+static const int kSinkSurf[5][8] = {
+	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
+	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kDSinkPts[6][3] = {
+	{-55, 0, 110}, {-60, -45, 110}, {-118, -45, 110}, {-123, 0, 110}, {-118, 45, 110}, {-60, 45, 110}
+};
+static const int kDSinkSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
+static const int kSinkMirrorPts[4][3] = {
+	{-128, 65, 130}, {-128, -65, 130}, {-128, 65, 250}, {-128, -65, 250}
+};
+static const int kSinkMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
+
+static const Colony::ColonyEngine::PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
+static const Colony::ColonyEngine::PrismPartDef kTableParts[2] = {
+	{4, kTableTopPts, 1, kTableTopSurf},
+	{8, kTableBasePts, 4, kTableBaseSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kBedParts[3] = {
+	{4, kBedPostPts, 1, kBedPostSurf},
+	{8, kBedBlanketPts, 4, kBlanketSurf},
+	{8, kBedSheetPts, 3, kSheetSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kBBedParts[3] = {
+	{4, kBBedPostPts, 1, kBedPostSurf},
+	{8, kBBedBlanketPts, 4, kBlanketSurf},
+	{8, kBBedSheetPts, 3, kSheetSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kDeskParts[10] = {
+	{4, kDeskTopPts, 1, kDeskTopSurf},
+	{8, kDeskLeftPts, 4, kDeskCabSurf},
+	{8, kDeskRightPts, 4, kDeskCabSurf},
+	{4, kSeatPts, 1, kSeatSurf},
+	{4, kArmLeftPts, 2, kArmSurf},
+	{4, kArmRightPts, 2, kArmSurf},
+	{4, kBackPts, 2, kBackSurf},
+	{8, kComputerPts, 5, kComputerSurf},
+	{8, kMonitorPts, 5, kComputerSurf},
+	{4, kDeskScreenPts, 1, kDeskScreenSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kCChairParts[5] = {
+	{4, kCSeatPts, 1, kCSeatSurf},
+	{4, kCArmLeftPts, 1, kCArmLeftSurf},
+	{4, kCArmRightPts, 1, kCArmRightSurf},
+	{4, kCBackPts, 1, kCBackSurf},
+	{8, kCBasePts, 4, kCBaseSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kConsolePart = {8, kConsolePts, 5, kConsoleSurf};
+static const Colony::ColonyEngine::PrismPartDef kCouchParts[4] = {
+	{8, kACouchPts, 5, kCouchSurf},
+	{8, kBCouchPts, 5, kCouchSurf},
+	{8, kCCouchPts, 5, kCouchSurf},
+	{8, kDCouchPts, 5, kCouchSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kChairParts[4] = {
+	{8, kAChairPts, 5, kCouchSurf},
+	{8, kBChairPts, 5, kCouchSurf},
+	{8, kCChairPts2, 5, kCouchSurf},
+	{8, kDChairPts, 5, kCouchSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kTVParts[2] = {
+	{8, kTVBodyPts, 5, kTVBodySurf},
+	{4, kTVScreenPts, 1, kTVScreenSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kDrawerParts[2] = {
+	{8, kDrawerPts, 5, kDrawerSurf},
+	{4, kMirrorPts, 1, kMirrorSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kTubParts[2] = {
+	{8, kTubPts, 5, kTubSurf},
+	{6, kDTubPts, 1, kDTubSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kSinkParts[3] = {
+	{8, kSinkPts, 5, kSinkSurf},
+	{6, kDSinkPts, 1, kDSinkSurf},
+	{4, kSinkMirrorPts, 1, kSinkMirrorSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kToiletParts[4] = {
+	{8, kAToiletPts, 5, kAToiletSurf},
+	{12, kBToiletPts, 7, kBToiletSurf},
+	{6, kCToiletPts, 1, kCToiletSurf},
+	{6, kDToiletPts, 1, kDToiletSurf}
+};
+static const Colony::ColonyEngine::PrismPartDef kPToiletParts[5] = {
+	{8, kAToiletPts, 5, kAToiletSurf},
+	{12, kBToiletPts, 7, kBToiletSurf},
+	{6, kCToiletPts, 1, kCToiletSurf},
+	{6, kDToiletPts, 1, kDToiletSurf},
+	{4, kEToiletPts, 1, kEToiletSurf}
+};
+
+static const int kFWallPts[4][3] = {
+	{-128, 128, 0}, {128, -128, 0}, {-128, 128, 320}, {128, -128, 320}
+};
+static const int kFWallSurf[1][8] = {{kColorWall, 4, 2, 3, 1, 0, 0, 0}};
+static const Colony::ColonyEngine::PrismPartDef kFWallPart = {4, kFWallPts, 1, kFWallSurf};
+
+static const int kCWallPts[8][3] = {
+	{-128, 128, 0}, {0, 112, 0}, {112, 0, 0}, {128, -128, 0},
+	{-128, 128, 320},  {0, 112, 320},  {112, 0, 320},  {128, -128, 320}
+};
+static const int kCWallSurf[3][8] = {
+	{kColorWall, 4, 1, 0, 4, 5, 0, 0}, {kColorWall, 4, 2, 1, 5, 6, 0, 0}, {kColorWall, 4, 3, 2, 6, 7, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kCWallPart = {8, kCWallPts, 3, kCWallSurf};
+
+static const int kPlantPotPts[12][3] = {
+	{10, 17, 40}, {20, 0, 40}, {10, -17, 40}, {-10, -17, 40}, {-20, 0, 40}, {-10, 17, 40},
+	{8, 12, 0}, {15, 0, 0}, {8, -12, 0}, {-8, -12, 0}, {-15, 0, 0}, {-8, 12, 0}
+};
+static const int kPlantPotSurf[6][8] = {
+	{kColorPot, 4, 0, 1, 7, 6, 0, 0}, {kColorPot, 4, 1, 2, 8, 7, 0, 0}, {kColorPot, 4, 2, 3, 9, 8, 0, 0},
+	{kColorPot, 4, 3, 4, 10, 9, 0, 0}, {kColorPot, 4, 4, 5, 11, 10, 0, 0}, {kColorPot, 4, 5, 0, 6, 11, 0, 0}
+};
+static const int kPlantTopPotPts[6][3] = {
+	{10, 17, 40}, {20, 0, 40}, {10, -17, 40}, {-10, -17, 40}, {-20, 0, 40}, {-10, 17, 40}
+};
+static const int kPlantTopPotSurf[1][8] = {{kColorDkGray, 6, 5, 4, 3, 2, 1, 0}};
+
+static const int kPlantLeaf0Pts[3][3] = {{0, 0, 0}, {20, 20, 150}, {70, 70, 100}};
+static const int kPlantLeaf1Pts[3][3] = {{0, 0, 0}, {-20, 30, 100}, {-60, 50, 50}};
+static const int kPlantLeaf2Pts[3][3] = {{0, 0, 0}, {-20, -10, 70}, {-90, -70, 50}};
+static const int kPlantLeaf3Pts[3][3] = {{0, 0, 0}, {20, -10, 50}, {90, -70, 70}};
+static const int kPlantLeaf4Pts[3][3] = {{0, 0, 0}, {20, -30, 190}, {40, -60, 150}};
+static const int kPlantLeaf5Pts[3][3] = {{0, 0, 0}, {20, -10, 130}, {50, -80, 200}};
+
+static const int kPlantLeafSurf[2][8] = {{kColorClear, 3, 0, 1, 2, 0, 0, 0}, {kColorClear, 3, 2, 1, 0, 0, 0, 0}};
+
+static const Colony::ColonyEngine::PrismPartDef kPlantParts[8] = {
+	{12, kPlantPotPts, 6, kPlantPotSurf},
+	{6, kPlantTopPotPts, 1, kPlantTopPotSurf},
+	{3, kPlantLeaf0Pts, 2, kPlantLeafSurf},
+	{3, kPlantLeaf1Pts, 2, kPlantLeafSurf},
+	{3, kPlantLeaf2Pts, 2, kPlantLeafSurf},
+	{3, kPlantLeaf3Pts, 2, kPlantLeafSurf},
+	{3, kPlantLeaf4Pts, 2, kPlantLeafSurf},
+	{3, kPlantLeaf5Pts, 2, kPlantLeafSurf}
+};
+
+static const int kBox1Pts[8][3] = {
+	{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
+	{-50, 50, 100}, {50, 50, 100}, {50, -50, 100}, {-50, -50, 100}
+};
+static const int kBox1Surf[5][8] = {
+	{kColorBox, 4, 0, 3, 7, 4, 0, 0}, {kColorBox, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBox, 4, 1, 0, 4, 5, 0, 0}, {kColorBox, 4, 2, 1, 5, 6, 0, 0}, {kColorBox, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kBox2Pts[8][3] = {
+	{-50, 50, 100}, {50, 50, 100}, {50, -50, 100}, {-50, -50, 100},
+	{-50, 50, 200}, {50, 50, 200}, {50, -50, 200}, {-50, -50, 200}
+};
+
+static const int kReactorCorePts[12][3] = {
+	{-40, 20, 288}, {0, 40, 288}, {40, 20, 288}, {40, -20, 288}, {0, -40, 288}, {-40, -20, 288},
+	{-40, 20, 32}, {0, 40, 32}, {40, 20, 32}, {40, -20, 32}, {0, -40, 32}, {-40, -20, 32}
+};
+static const int kReactorCoreSurf[7][8] = {
+	{kColorCCore, 4, 0, 1, 7, 6, 0, 0}, {kColorCCore, 4, 1, 2, 8, 7, 0, 0}, {kColorCCore, 4, 2, 3, 9, 8, 0, 0},
+	{kColorCCore, 4, 3, 4, 10, 9, 0, 0}, {kColorCCore, 4, 4, 5, 11, 10, 0, 0}, {kColorCCore, 4, 5, 0, 6, 11, 0, 0},
+	{kColorCCore, 6, 5, 4, 3, 2, 1, 0}
+};
+static const int kReactorBasePts[8][3] = {
+	{-128, 128, 0}, {128, 128, 0}, {128, -128, 0}, {-128, -128, 0},
+	{-128, 128, 32}, {128, 128, 32}, {128, -128, 32}, {-128, -128, 32}
+};
+static const int kReactorBaseSurf[6][8] = {
+	{kColorReactor, 4, 0, 3, 7, 4, 0, 0}, {kColorReactor, 4, 3, 2, 6, 7, 0, 0}, {kColorReactor, 4, 1, 0, 4, 5, 0, 0},
+	{kColorReactor, 4, 2, 1, 5, 6, 0, 0}, {kColorReactor, 4, 7, 6, 5, 4, 0, 0}, {kColorReactor, 4, 0, 1, 2, 3, 0, 0}
+};
+static const int kReactorTopPts[8][3] = {
+	{-128, 128, 288}, {128, 128, 288}, {128, -128, 288}, {-128, -128, 288},
+	{-128, 128, 320}, {128, 128, 320}, {128, -128, 320}, {-128, -128, 320}
+};
+
+static const Colony::ColonyEngine::PrismPartDef kBox1Part = {8, kBox1Pts, 5, kBox1Surf};
+static const Colony::ColonyEngine::PrismPartDef kBox2Parts[2] = {
+	{8, kBox2Pts, 4, kBox1Surf}, // Body (stacked on box1)
+	{8, kBox1Pts, 5, kBox1Surf}  // Lid (same geometry as box1 base)
+};
+// Bench: simple box (1 part). DOS INITOBJ.C InitBench.
+static const int kBenchPts[8][3] = {
+	{-60, 128, 0}, {60, 128, 0}, {60, -128, 0}, {-60, -128, 0},
+	{-60, 128, 120}, {60, 128, 120}, {60, -128, 120}, {-60, -128, 120}
+};
+static const int kBenchSurf[5][8] = {
+	{kColorBench, 4, 0, 3, 7, 4, 0, 0}, {kColorBench, 4, 3, 2, 6, 7, 0, 0},
+	{kColorBench, 4, 1, 0, 4, 5, 0, 0}, {kColorBench, 4, 2, 1, 5, 6, 0, 0},
+	{kColorBlack, 4, 7, 6, 5, 4, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kBenchPart = {8, kBenchPts, 5, kBenchSurf};
+
+// CBench: L-shaped corner bench (2 parts). DOS INITOBJ.C InitCBench.
+// Part 0: slanted front face (front-left Y=60, front-right Y=-60)
+static const int kCBenchPts[8][3] = {
+	{-60, 60, 0}, {60, -60, 0}, {60, -128, 0}, {-60, -128, 0},
+	{-60, 60, 120}, {60, -60, 120}, {60, -128, 120}, {-60, -128, 120}
+};
+// Part 1: wider perpendicular section
+static const int kDBenchPts[8][3] = {
+	{-60, 60, 0}, {128, 60, 0}, {128, -60, 0}, {60, -60, 0},
+	{-60, 60, 120}, {128, 60, 120}, {128, -60, 120}, {60, -60, 120}
+};
+static const Colony::ColonyEngine::PrismPartDef kCBenchParts[2] = {
+	{8, kCBenchPts, 5, kBenchSurf},
+	{8, kDBenchPts, 5, kBenchSurf}
+};
+
+// Power Suit: triangular prism body + small rectangular pedestal + flat table + hexagonal power source
+// DOS INITOBJ.C: 5 prism parts. Floor=160, so 2*Floor=320.
+static const int kPowerTopPts[3][3] = {{-150, 120, 320}, {150, 120, 320}, {0, -150, 320}};
+static const int kPowerTopSurf[1][8] = {{kColorPower, 3, 0, 1, 2, 0, 0, 0}};
+
+static const int kPowerBottomPts[3][3] = {{-150, 120, 0}, {150, 120, 0}, {0, -150, 0}};
+static const int kPowerBottomSurf[1][8] = {{kColorPower, 3, 2, 1, 0, 0, 0, 0}};
+
+static const int kPowerBasePts[8][3] = {
+	{-5, 100, 0}, {5, 100, 0}, {5, 90, 0}, {-5, 90, 0},
+	{-5, 100, 100}, {5, 100, 100}, {5, 90, 100}, {-5, 90, 100}
+};
+static const int kPowerBaseSurf[4][8] = {
+	{kColorPBase, 4, 0, 3, 7, 4, 0, 0}, {kColorPBase, 4, 3, 2, 6, 7, 0, 0},
+	{kColorPBase, 4, 1, 0, 4, 5, 0, 0}, {kColorPBase, 4, 2, 1, 5, 6, 0, 0}
+};
+
+static const int kPowerTablePts[4][3] = {{-50, 135, 100}, {50, 135, 100}, {50, 55, 100}, {-50, 55, 100}};
+static const int kPowerTableSurf[1][8] = {{kColorBlack, 4, 3, 2, 1, 0, 0, 0}};
+
+static const int kPowerSourcePts[12][3] = {
+	{-75, 0, 290}, {-35, 60, 290}, {35, 60, 290}, {75, 0, 290}, {35, -60, 290}, {-35, -60, 290},
+	{-75, 0, 320}, {-35, 60, 320}, {35, 60, 320}, {75, 0, 320}, {35, -60, 320}, {-35, -60, 320}
+};
+static const int kPowerSourceSurf[7][8] = {
+	{kColorRainbow1, 6, 0, 1, 2, 3, 4, 5},
+	{kColorPSource, 4, 0, 6, 7, 1, 0, 0}, {kColorPSource, 4, 1, 7, 8, 2, 0, 0},
+	{kColorPSource, 4, 2, 8, 9, 3, 0, 0}, {kColorPSource, 4, 3, 9, 10, 4, 0, 0},
+	{kColorPSource, 4, 4, 10, 11, 5, 0, 0}, {kColorPSource, 4, 5, 11, 6, 0, 0, 0}
+};
+
+static const Colony::ColonyEngine::PrismPartDef kPowerSuitParts[5] = {
+	{3, kPowerTopPts, 1, kPowerTopSurf},
+	{3, kPowerBottomPts, 1, kPowerBottomSurf},
+	{8, kPowerBasePts, 4, kPowerBaseSurf},
+	{4, kPowerTablePts, 1, kPowerTableSurf},
+	{12, kPowerSourcePts, 7, kPowerSourceSurf}
+};
+
+// Cryo tube: top (coffin-shaped lid) + base
+static const int kCryoTopPts[16][3] = {
+	{-130,  50,  80}, { 130,  50,  80}, { 130, -50,  80}, {-130, -50,  80},
+	{-130,  50, 140}, { 130,  50, 140}, { 130, -50, 140}, {-130, -50, 140},
+	{   0,  50, 140}, {   0, -50, 140},
+	{-140,  70, 110}, { 140,  70, 110}, { 140, -70, 110}, {-140, -70, 110},
+	{   0,  70, 110}, {   0, -70, 110}
+};
+static const int kCryoTopSurf[12][8] = {
+	{kColorCryo,      4, 7, 9, 8, 4, 0, 0},
+	{kColorCryoGlass, 4, 9, 6, 5, 8, 0, 0},
+	{kColorCryo,      4, 0, 10, 11, 1, 0, 0},
+	{kColorCryo,      4, 1, 11, 12, 2, 0, 0},
+	{kColorCryo,      4, 2, 12, 13, 3, 0, 0},
+	{kColorCryo,      4, 3, 13, 10, 0, 0, 0},
+	{kColorCryo,      4, 7, 13, 15, 9, 0, 0},
+	{kColorCryo,      4, 4, 10, 13, 7, 0, 0},
+	{kColorCryo,      4, 14, 10, 4, 8, 0, 0},
+	{kColorSilver,    4, 5, 11, 14, 8, 0, 0},
+	{kColorSilver,    4, 6, 12, 11, 5, 0, 0},
+	{kColorSilver,    4, 9, 15, 12, 6, 0, 0}
+};
+static const int kCryoBasePts[8][3] = {
+	{-130,  50,  0}, { 130,  50,  0}, { 130, -50,  0}, {-130, -50,  0},
+	{-130,  50, 80}, { 130,  50, 80}, { 130, -50, 80}, {-130, -50, 80}
+};
+static const int kCryoBaseSurf[5][8] = {
+	{kColorCryoBase, 4, 0, 3, 7, 4, 0, 0}, {kColorCryoBase, 4, 3, 2, 6, 7, 0, 0},
+	{kColorCryoBase, 4, 1, 0, 4, 5, 0, 0}, {kColorCryoBase, 4, 2, 1, 5, 6, 0, 0},
+	{kColorCryo,     4, 7, 6, 5, 4, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kCryoParts[2] = {
+	{16, kCryoTopPts, 12, kCryoTopSurf},
+	{8, kCryoBasePts, 5, kCryoBaseSurf}
+};
+
+// Forklift: cab + treads + upper-left arm + lower-left fork + upper-right arm + lower-right fork
+static const int kFLCabPts[14][3] = {
+	{-35, 60, 40}, {35, 60, 40}, {35, -60, 40}, {-35, -60, 40},
+	{-15, 60, 260}, {15, 60, 260}, {15, -60, 260}, {-15, -60, 260},
+	{25, 60, 140}, {25, -60, 140},
+	{70, 35, 120}, {70, -35, 120},
+	{-70, 40, 80}, {-70, -40, 80}
+};
+static const int kFLCabSurf[12][8] = {
+	{kColorForklift, 4, 0, 3, 13, 12, 0, 0}, {kColorForklift, 4, 12, 13, 7, 4, 0, 0},
+	{kColorForklift, 3, 0, 12, 4, 0, 0, 0},  {kColorForklift, 3, 3, 7, 13, 0, 0, 0},
+	{kColorForklift, 4, 3, 2, 6, 7, 0, 0},   {kColorForklift, 4, 1, 0, 4, 5, 0, 0},
+	{kColorForklift, 3, 1, 8, 10, 0, 0, 0},  {kColorForklift, 3, 2, 11, 9, 0, 0, 0},
+	{kColorForklift, 4, 1, 10, 11, 2, 0, 0},
+	{kColorSilver,   3, 8, 5, 10, 0, 0, 0},  {kColorSilver, 3, 11, 6, 9, 0, 0, 0},
+	{kColorSilver,   4, 10, 5, 6, 11, 0, 0}
+};
+static const int kFLTreadPts[12][3] = {
+	{-60, 60, 20}, {60, 60, 20}, {60, -60, 20}, {-60, -60, 20},
+	{-35, 60, 40}, {35, 60, 40}, {35, -60, 40}, {-35, -60, 40},
+	{-35, 60, 0}, {35, 60, 0}, {35, -60, 0}, {-35, -60, 0}
+};
+static const int kFLTreadSurf[6][8] = {
+	{kColorTread1, 4, 0, 3, 7, 4, 0, 0},
+	{kColorTread2, 6, 3, 11, 10, 2, 6, 7},
+	{kColorTread2, 6, 0, 4, 5, 1, 9, 8},
+	{kColorTread1, 4, 2, 1, 5, 6, 0, 0},
+	{kColorTread1, 4, 0, 8, 11, 3, 0, 0},
+	{kColorTread1, 4, 10, 9, 1, 2, 0, 0}
+};
+static const int kFLULPts[8][3] = {
+	{-15, 70, 120}, {15, 70, 120}, {15, 60, 120}, {-15, 60, 120},
+	{-25, 70, 230}, {25, 70, 230}, {25, 60, 230}, {-25, 60, 230}
+};
+static const int kFLArmSurf[4][8] = {
+	{kColorForklift, 4, 0, 3, 7, 4, 0, 0}, {kColorForklift, 4, 3, 2, 6, 7, 0, 0},
+	{kColorForklift, 4, 1, 0, 4, 5, 0, 0}, {kColorForklift, 4, 2, 1, 5, 6, 0, 0}
+};
+static const int kFLLLPts[8][3] = {
+	{-15, 80, 120}, {100, 80, 125}, {100, 70, 125}, {-15, 70, 120},
+	{-15, 80, 150}, {100, 80, 140}, {100, 70, 140}, {-15, 70, 150}
+};
+static const int kFLForkSurf[6][8] = {
+	{kColorForklift, 4, 0, 3, 7, 4, 0, 0}, {kColorForklift, 4, 3, 2, 6, 7, 0, 0},
+	{kColorForklift, 4, 1, 0, 4, 5, 0, 0}, {kColorBlack,    4, 2, 1, 5, 6, 0, 0},
+	{kColorForklift, 4, 7, 6, 5, 4, 0, 0}, {kColorForklift, 4, 0, 1, 2, 3, 0, 0}
+};
+static const int kFLURPts[8][3] = {
+	{-15, -60, 120}, {15, -60, 120}, {15, -70, 120}, {-15, -70, 120},
+	{-25, -60, 230}, {25, -60, 230}, {25, -70, 230}, {-25, -70, 230}
+};
+static const int kFLLRPts[8][3] = {
+	{-15, -70, 120}, {100, -70, 125}, {100, -80, 125}, {-15, -80, 120},
+	{-15, -70, 150}, {100, -70, 140}, {100, -80, 140}, {-15, -80, 150}
+};
+static const Colony::ColonyEngine::PrismPartDef kForkliftParts[6] = {
+	{14, kFLCabPts, 12, kFLCabSurf},
+	{12, kFLTreadPts, 6, kFLTreadSurf},
+	{8, kFLULPts, 4, kFLArmSurf},
+	{8, kFLLLPts, 6, kFLForkSurf},
+	{8, kFLURPts, 4, kFLArmSurf},
+	{8, kFLLRPts, 6, kFLForkSurf}
+};
+
+// Teleport: octagonal booth with flared middle
+static const int kTelePts[24][3] = {
+	// Ring 0: outer flared ring at z=140
+	{   0, 175, 140}, { 125, 125, 140}, { 175,   0, 140}, { 125,-125, 140},
+	{   0,-175, 140}, {-125,-125, 140}, {-175,   0, 140}, {-125, 125, 140},
+	// Ring 1: inner ring at z=0 (bottom)
+	{  0,  80, 0}, { 65,  65, 0}, { 80,   0, 0}, { 65, -65, 0},
+	{  0, -80, 0}, {-65, -65, 0}, {-80,   0, 0}, {-65,  65, 0},
+	// Ring 2: inner ring at z=280 (top)
+	{  0,  80, 280}, { 65,  65, 280}, { 80,   0, 280}, { 65, -65, 280},
+	{  0, -80, 280}, {-65, -65, 280}, {-80,   0, 280}, {-65,  65, 280}
+};
+static const int kTeleSurf[16][8] = {
+	// Bottom 8 panels (outer mid to inner bottom)
+	{kColorTeleDoor, 4, 0, 1, 9, 8, 0, 0},
+	{kColorTele,     4, 1, 2, 10, 9, 0, 0}, {kColorTele, 4, 2, 3, 11, 10, 0, 0},
+	{kColorTele,     4, 3, 4, 12, 11, 0, 0}, {kColorTele, 4, 4, 5, 13, 12, 0, 0},
+	{kColorTele,     4, 5, 6, 14, 13, 0, 0}, {kColorTele, 4, 6, 7, 15, 14, 0, 0},
+	{kColorTele,     4, 7, 0, 8, 15, 0, 0},
+	// Top 8 panels (outer mid to inner top)
+	{kColorSilver,   4, 1, 0, 16, 17, 0, 0},
+	{kColorTele,     4, 2, 1, 17, 18, 0, 0}, {kColorTele, 4, 3, 2, 18, 19, 0, 0},
+	{kColorTele,     4, 4, 3, 19, 20, 0, 0}, {kColorTele, 4, 5, 4, 20, 21, 0, 0},
+	{kColorTele,     4, 6, 5, 21, 22, 0, 0}, {kColorTele, 4, 7, 6, 22, 23, 0, 0},
+	{kColorTele,     4, 0, 7, 23, 16, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kTelePart = {24, kTelePts, 16, kTeleSurf};
+
+// Projector: body + stand + lens (sits on table)
+static const int kProjectorPts[8][3] = {
+	{-30, 30, 140}, {30, 30, 140}, {30, -30, 140}, {-30, -30, 140},
+	{-20, 30, 160}, {30, 30, 160}, {30, -30, 160}, {-20, -30, 160}
+};
+static const int kProjectorSurf[5][8] = {
+	{kColorProjector, 4, 0, 3, 7, 4, 0, 0}, {kColorProjector, 4, 3, 2, 6, 7, 0, 0},
+	{kColorProjector, 4, 1, 0, 4, 5, 0, 0}, {kColorProjector, 4, 2, 1, 5, 6, 0, 0},
+	{kColorProjector, 4, 7, 6, 5, 4, 0, 0}
+};
+static const int kPStandPts[4][3] = {
+	{-25, 50, 100}, {0, 10, 140}, {0, -10, 140}, {-25, -50, 100}
+};
+static const int kPStandSurf[1][8] = {{kColorPStand, 4, 0, 1, 2, 3, 0, 0}};
+static const int kPLensPts[12][3] = {
+	{30,  8, 154}, {30,  0, 158}, {30, -8, 154}, {30, -8, 146}, {30,  0, 142}, {30,  8, 146},
+	{55, 10, 155}, {55,  0, 160}, {55,-10, 155}, {55,-10, 145}, {55,  0, 140}, {55, 10, 145}
+};
+static const int kPLensSurf[7][8] = {
+	{kColorPLens, 4, 0, 1, 7, 6, 0, 0}, {kColorPLens, 4, 1, 2, 8, 7, 0, 0},
+	{kColorPLens, 4, 2, 3, 9, 8, 0, 0}, {kColorPLens, 4, 3, 4, 10, 9, 0, 0},
+	{kColorPLens, 4, 4, 5, 11, 10, 0, 0}, {kColorPLens, 4, 5, 0, 6, 11, 0, 0},
+	{kColorBlack, 6, 6, 7, 8, 9, 10, 11}
+};
+static const Colony::ColonyEngine::PrismPartDef kProjectorParts[3] = {
+	{8, kProjectorPts, 5, kProjectorSurf},
+	{4, kPStandPts, 1, kPStandSurf},
+	{12, kPLensPts, 7, kPLensSurf}
+};
+
+// ============================================================================
+// Robot geometry data (from DOS PYRAMID.H, CUBE.H, EYE.H, UPYRAMID.H,
+// QUEEN.H, DRONE.H, SNOOP.H)
+// ============================================================================
+
+// --- Pyramid (type 2) ---
+static const int kPyramidPts[5][3] = {
+	{-75, 75, 30}, {75, 75, 30}, {75, -75, 30}, {-75, -75, 30}, {0, 0, 200}
+};
+static const int kPyramidSurf[4][8] = {
+	{kColorPyramid, 3, 1, 0, 4, 1, 0, 0}, {kColorPyramid, 3, 2, 1, 4, 2, 0, 0},
+	{kColorPyramid, 3, 3, 2, 4, 3, 0, 0}, {kColorPyramid, 3, 0, 3, 4, 0, 0, 0}
+};
+static const int kPShadowPts[4][3] = {
+	{-75, 75, 0}, {75, 75, 0}, {75, -75, 0}, {-75, -75, 0}
+};
+static const int kPShadowSurf[1][8] = {{kColorShadow, 4, 3, 2, 1, 0, 3, 0}};
+// Pyramid eye (ball on top)
+static const int kPIrisPts[4][3] = {
+	{15, 0, 185}, {15, 15, 200}, {15, 0, 215}, {15, -15, 200}
+};
+static const int kPIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
+static const int kPPupilPts[4][3] = {
+	{16, 0, 194}, {16, 6, 200}, {16, 0, 206}, {16, -6, 200}
+};
+static const int kPPupilSurf[1][8] = {{kColorPupil, 4, 0, 1, 2, 3, 0, 0}};
+
+static const Colony::ColonyEngine::PrismPartDef kPyramidBodyDef = {5, kPyramidPts, 4, kPyramidSurf};
+static const Colony::ColonyEngine::PrismPartDef kPShadowDef = {4, kPShadowPts, 1, kPShadowSurf};
+static const Colony::ColonyEngine::PrismPartDef kPIrisDef = {4, kPIrisPts, 1, kPIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kPPupilDef = {4, kPPupilPts, 1, kPPupilSurf};
+
+// --- Cube (type 3) --- (octahedron)
+static const int kCubePts[6][3] = {
+	{0, 0, 200}, {100, 0, 100}, {0, 100, 100}, {-100, 0, 100}, {0, -100, 100}, {0, 0, 0}
+};
+static const int kCubeSurf[8][8] = {
+	{kColorCube, 3, 0, 1, 2, 0, 0, 0}, {kColorCube, 3, 0, 2, 3, 0, 0, 0},
+	{kColorCube, 3, 0, 3, 4, 0, 0, 0}, {kColorCube, 3, 0, 4, 1, 0, 0, 0},
+	{kColorCube, 3, 5, 2, 1, 5, 0, 0}, {kColorCube, 3, 5, 3, 2, 5, 0, 0},
+	{kColorCube, 3, 5, 4, 3, 5, 0, 0}, {kColorCube, 3, 5, 1, 4, 5, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kCubeBodyDef = {6, kCubePts, 8, kCubeSurf};
+
+// --- UPyramid (type 4) --- (inverted pyramid)
+static const int kUPyramidPts[5][3] = {
+	{-75, 75, 190}, {75, 75, 190}, {75, -75, 190}, {-75, -75, 190}, {0, 0, 30}
+};
+static const int kUPyramidSurf[5][8] = {
+	{kColorUPyramid, 3, 0, 1, 4, 0, 0, 0}, {kColorUPyramid, 3, 1, 2, 4, 1, 0, 0},
+	{kColorUPyramid, 3, 2, 3, 4, 2, 0, 0}, {kColorUPyramid, 3, 3, 0, 4, 3, 0, 0},
+	{kColorUPyramid, 4, 3, 2, 1, 0, 3, 0}
+};
+static const int kUPShadowPts[4][3] = {
+	{-25, 25, 0}, {25, 25, 0}, {25, -25, 0}, {-25, -25, 0}
+};
+static const int kUPShadowSurf[1][8] = {{kColorShadow, 4, 3, 2, 1, 0, 3, 0}};
+
+static const Colony::ColonyEngine::PrismPartDef kUPyramidBodyDef = {5, kUPyramidPts, 5, kUPyramidSurf};
+static const Colony::ColonyEngine::PrismPartDef kUPShadowDef = {4, kUPShadowPts, 1, kUPShadowSurf};
+
+// --- Eye (type 1) ---
+// Ball is rendered by draw3DSphere(), not as a prism
+static const int kEyeIrisPts[4][3] = {
+	{60, 0, 140}, {60, 60, 200}, {60, 0, 260}, {60, -60, 200}
+};
+static const int kEyeIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
+static const int kEyePupilPts[4][3] = {
+	{66, 0, 175}, {66, 25, 200}, {66, 0, 225}, {66, -25, 200}
+};
+static const int kEyePupilSurf[1][8] = {{kColorBlack, 4, 0, 1, 2, 3, 0, 0}};
+
+static const Colony::ColonyEngine::PrismPartDef kEyeIrisDef = {4, kEyeIrisPts, 1, kEyeIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kEyePupilDef = {4, kEyePupilPts, 1, kEyePupilSurf};
+
+// --- Floating Pyramid (type 6) --- egg on ground
+static const int kFPyramidPts[5][3] = {
+	{-75, 75, 0}, {75, 75, 0}, {75, -75, 0}, {-75, -75, 0}, {0, 0, 170}
+};
+static const Colony::ColonyEngine::PrismPartDef kFPyramidBodyDef = {5, kFPyramidPts, 4, kPyramidSurf};
+
+// --- Small Pyramid (type 10) ---
+static const int kSPyramidPts[5][3] = {
+	{-40, 40, 0}, {40, 40, 0}, {40, -40, 0}, {-40, -40, 0}, {0, 0, 100}
+};
+static const Colony::ColonyEngine::PrismPartDef kSPyramidBodyDef = {5, kSPyramidPts, 4, kPyramidSurf};
+
+// --- Mini Pyramid (type 14) ---
+static const int kMPyramidPts[5][3] = {
+	{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0}, {0, 0, 50}
+};
+static const Colony::ColonyEngine::PrismPartDef kMPyramidBodyDef = {5, kMPyramidPts, 4, kPyramidSurf};
+
+// --- Floating Cube (type 7) ---
+static const int kFCubePts[6][3] = {
+	{0, 0, 150}, {75, 0, 75}, {0, 75, 75}, {-75, 0, 75}, {0, -75, 75}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kFCubeBodyDef = {6, kFCubePts, 8, kCubeSurf};
+
+// --- Small Cube (type 11) ---
+static const int kSCubePts[6][3] = {
+	{0, 0, 100}, {50, 0, 50}, {0, 50, 50}, {-50, 0, 50}, {0, -50, 50}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kSCubeBodyDef = {6, kSCubePts, 8, kCubeSurf};
+
+// --- Mini Cube (type 15) ---
+static const int kMCubePts[6][3] = {
+	{0, 0, 50}, {25, 0, 25}, {0, 25, 25}, {-25, 0, 25}, {0, -25, 25}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kMCubeBodyDef = {6, kMCubePts, 8, kCubeSurf};
+
+// --- Floating UPyramid (type 8) ---
+static const int kFUPyramidPts[5][3] = {
+	{-75, 75, 170}, {75, 75, 170}, {75, -75, 170}, {-75, -75, 170}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kFUPyramidBodyDef = {5, kFUPyramidPts, 5, kUPyramidSurf};
+
+// --- Small UPyramid (type 12) ---
+static const int kSUPyramidPts[5][3] = {
+	{-40, 40, 100}, {40, 40, 100}, {40, -40, 100}, {-40, -40, 100}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kSUPyramidBodyDef = {5, kSUPyramidPts, 5, kUPyramidSurf};
+
+// --- Mini UPyramid (type 16) ---
+static const int kMUPyramidPts[5][3] = {
+	{-20, 20, 50}, {20, 20, 50}, {20, -20, 50}, {-20, -20, 50}, {0, 0, 0}
+};
+static const Colony::ColonyEngine::PrismPartDef kMUPyramidBodyDef = {5, kMUPyramidPts, 5, kUPyramidSurf};
+
+// --- Floating Eye (type 5) ---
+static const int kFEyeIrisPts[4][3] = {
+	{60, 0, 40}, {60, 60, 100}, {60, 0, 160}, {60, -60, 100}
+};
+static const int kFEyePupilPts[4][3] = {
+	{66, 0, 75}, {66, 25, 100}, {66, 0, 125}, {66, -25, 100}
+};
+static const Colony::ColonyEngine::PrismPartDef kFEyeIrisDef = {4, kFEyeIrisPts, 1, kEyeIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kFEyePupilDef = {4, kFEyePupilPts, 1, kEyePupilSurf};
+
+// --- Small Eye (type 9) ---
+static const int kSEyeIrisPts[4][3] = {
+	{30, 0, 20}, {30, 30, 50}, {30, 0, 80}, {30, -30, 50}
+};
+static const int kSEyePupilPts[4][3] = {
+	{33, 0, 38}, {33, 13, 50}, {33, 0, 63}, {33, -13, 50}
+};
+static const Colony::ColonyEngine::PrismPartDef kSEyeIrisDef = {4, kSEyeIrisPts, 1, kEyeIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kSEyePupilDef = {4, kSEyePupilPts, 1, kEyePupilSurf};
+
+// --- Mini Eye (type 13) ---
+static const int kMEyeIrisPts[4][3] = {
+	{15, 0, 10}, {15, 15, 25}, {15, 0, 40}, {15, -15, 25}
+};
+static const int kMEyePupilPts[4][3] = {
+	{16, 0, 19}, {16, 6, 25}, {16, 0, 31}, {16, -6, 25}
+};
+static const Colony::ColonyEngine::PrismPartDef kMEyeIrisDef = {4, kMEyeIrisPts, 1, kEyeIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kMEyePupilDef = {4, kMEyePupilPts, 1, kEyePupilSurf};
+
+// --- Queen (type 17) ---
+// Queen eye (ball rendered by draw3DSphere)
+static const int kQIrisPts[4][3] = {
+	{15, 0, 140}, {15, 15, 155}, {15, 0, 170}, {15, -15, 155}
+};
+static const int kQIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
+static const int kQPupilPts[4][3] = {
+	{16, 0, 148}, {16, 6, 155}, {16, 0, 161}, {16, -6, 155}
+};
+static const int kQPupilSurf[1][8] = {{kColorPupil, 4, 0, 1, 2, 3, 0, 0}};
+// Queen abdomen
+static const int kQAbdomenPts[9][3] = {
+	{120, 0, 130}, {30, 0, 160}, {30, 0, 100}, {30, 50, 130}, {30, -50, 130},
+	{0, 0, 150}, {0, 0, 110}, {0, 25, 130}, {0, -25, 130}
+};
+static const int kQAbdomenSurf[9][8] = {
+	{kColorQueen, 3, 0, 3, 1, 0, 0, 0}, {kColorQueen, 3, 0, 1, 4, 0, 0, 0},
+	{kColorQueen, 3, 0, 2, 3, 0, 0, 0}, {kColorQueen, 3, 0, 4, 2, 0, 0, 0},
+	{kColorQueen, 4, 1, 5, 8, 4, 1, 0}, {kColorQueen, 4, 1, 3, 7, 5, 1, 0},
+	{kColorQueen, 4, 2, 4, 8, 6, 2, 0}, {kColorQueen, 4, 2, 6, 7, 3, 2, 0},
+	{kColorClear, 4, 5, 7, 6, 8, 5, 0}
+};
+// Queen thorax
+static const int kQThoraxPts[9][3] = {
+	{-120, 0, 130}, {-50, 0, 170}, {-50, 0, 90}, {-50, 60, 130}, {-50, -60, 130},
+	{0, 0, 150}, {0, 0, 110}, {0, 25, 130}, {0, -25, 130}
+};
+static const int kQThoraxSurf[8][8] = {
+	{kColorQueen, 3, 0, 1, 3, 0, 0, 0}, {kColorQueen, 3, 0, 4, 1, 0, 0, 0},
+	{kColorQueen, 3, 0, 3, 2, 0, 0, 0}, {kColorQueen, 3, 0, 2, 4, 0, 0, 0},
+	{kColorQueen, 4, 1, 4, 8, 5, 1, 0}, {kColorQueen, 4, 1, 5, 7, 3, 1, 0},
+	{kColorQueen, 4, 2, 6, 8, 4, 2, 0}, {kColorQueen, 4, 2, 3, 7, 6, 2, 0}
+};
+// Queen wings
+static const int kQLWingPts[4][3] = {
+	{80, 0, 140}, {-40, 10, 200}, {-120, 60, 170}, {-40, 120, 140}
+};
+static const int kQLWingSurf[1][8] = {{kColorClear, 4, 0, 1, 2, 3, 0, 0}};
+static const int kQRWingPts[4][3] = {
+	{80, 0, 140}, {-40, -10, 200}, {-120, -60, 170}, {-40, -120, 140}
+};
+static const int kQRWingSurf[1][8] = {{kColorClear, 4, 0, 1, 2, 3, 0, 0}};
+
+static const Colony::ColonyEngine::PrismPartDef kQIrisDef = {4, kQIrisPts, 1, kQIrisSurf};
+static const Colony::ColonyEngine::PrismPartDef kQPupilDef = {4, kQPupilPts, 1, kQPupilSurf};
+static const Colony::ColonyEngine::PrismPartDef kQAbdomenDef = {9, kQAbdomenPts, 9, kQAbdomenSurf};
+static const Colony::ColonyEngine::PrismPartDef kQThoraxDef = {9, kQThoraxPts, 8, kQThoraxSurf};
+static const Colony::ColonyEngine::PrismPartDef kQLWingDef = {4, kQLWingPts, 1, kQLWingSurf};
+static const Colony::ColonyEngine::PrismPartDef kQRWingDef = {4, kQRWingPts, 1, kQRWingSurf};
+
+// --- Drone / Soldier (types 18, 19) ---
+static const int kDAbdomenPts[6][3] = {
+	{0, 0, 170}, {120, 0, 130}, {0, 100, 130}, {-130, 0, 130}, {0, -100, 130}, {0, 0, 100}
+};
+static const int kDAbdomenSurf[8][8] = {
+	{kColorDrone, 3, 0, 1, 2, 0, 0, 0}, {kColorDrone, 3, 0, 2, 3, 0, 0, 0},
+	{kColorDrone, 3, 0, 3, 4, 0, 0, 0}, {kColorDrone, 3, 0, 4, 1, 0, 0, 0},
+	{kColorDrone, 3, 5, 2, 1, 5, 0, 0}, {kColorDrone, 3, 5, 3, 2, 5, 0, 0},
+	{kColorDrone, 3, 5, 4, 3, 5, 0, 0}, {kColorDrone, 3, 5, 1, 4, 5, 0, 0}
+};
+// Drone static pincers (llPincer/rrPincer)
+static const int kDLLPincerPts[4][3] = {
+	{0, 0, 130}, {50, -2, 130}, {35, -20, 140}, {35, -20, 120}
+};
+static const int kDLPincerSurf[4][8] = {
+	{kColorClaw1, 3, 0, 2, 1, 0, 0, 0}, {kColorClaw1, 3, 0, 1, 3, 0, 0, 0},
+	{kColorClaw2, 3, 0, 3, 2, 0, 0, 0}, {kColorClaw2, 3, 1, 2, 3, 1, 0, 0}
+};
+static const int kDRRPincerPts[4][3] = {
+	{0, 0, 130}, {50, 2, 130}, {35, 20, 140}, {35, 20, 120}
+};
+static const int kDRPincerSurf[4][8] = {
+	{kColorClaw1, 3, 0, 1, 2, 0, 0, 0}, {kColorClaw1, 3, 0, 3, 1, 0, 0, 0},
+	{kColorClaw2, 3, 0, 2, 3, 0, 0, 0}, {kColorClaw2, 3, 1, 3, 2, 1, 0, 0}
+};
+// Drone eyes
+static const int kDLEyePts[3][3] = {
+	{60, 0, 150}, {60, 50, 130}, {60, 25, 150}
+};
+static const int kDLEyeSurf[2][8] = {
+	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
+};
+static const int kDREyePts[3][3] = {
+	{60, 0, 150}, {60, -50, 130}, {60, -25, 150}
+};
+static const int kDREyeSurf[2][8] = {
+	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
+};
+
+static const Colony::ColonyEngine::PrismPartDef kDAbdomenDef = {6, kDAbdomenPts, 8, kDAbdomenSurf};
+static const Colony::ColonyEngine::PrismPartDef kDLLPincerDef = {4, kDLLPincerPts, 4, kDLPincerSurf};
+static const Colony::ColonyEngine::PrismPartDef kDRRPincerDef = {4, kDRRPincerPts, 4, kDRPincerSurf};
+static const Colony::ColonyEngine::PrismPartDef kDLEyeDef = {3, kDLEyePts, 2, kDLEyeSurf};
+static const Colony::ColonyEngine::PrismPartDef kDREyeDef = {3, kDREyePts, 2, kDREyeSurf};
+
+// --- Snoop (type 20) ---
+static const int kSnoopAbdomenPts[4][3] = {
+	{0, 100, 0}, {-180, 0, 0}, {0, -100, 0}, {0, 0, 70}
+};
+static const int kSnoopAbdomenSurf[2][8] = {
+	{kColorTopSnoop, 3, 0, 1, 3, 0, 0, 0}, {kColorTopSnoop, 3, 2, 3, 1, 2, 0, 0}
+};
+static const int kSnoopHeadPts[4][3] = {
+	{0, 100, 0}, {150, 0, 0}, {0, -100, 0}, {0, 0, 70}
+};
+static const int kSnoopHeadSurf[3][8] = {
+	{kColorTopSnoop, 3, 0, 3, 1, 0, 0, 0}, {kColorTopSnoop, 3, 2, 1, 3, 2, 0, 0},
+	{kColorBottomSnoop, 3, 0, 1, 2, 0, 0, 0}
+};
+
+static const Colony::ColonyEngine::PrismPartDef kSnoopAbdomenDef = {4, kSnoopAbdomenPts, 2, kSnoopAbdomenSurf};
+static const Colony::ColonyEngine::PrismPartDef kSnoopHeadDef = {4, kSnoopHeadPts, 3, kSnoopHeadSurf};
+
+static void resetObjectBounds(const Common::Rect &screenR, Locate &loc) {
+	loc.xmn = screenR.right;
+	loc.xmx = screenR.left;
+	loc.zmn = screenR.bottom;
+	loc.zmx = screenR.top;
+}
+
+void ColonyEngine::drawStaticObjects() {
+	for (uint i = 0; i < _objects.size(); i++) {
+		Thing &obj = _objects[i];
+		if (!obj.alive)
+			continue;
+		resetObjectBounds(_screenR, obj.where);
+		int ox = obj.where.xindex;
+		int oy = obj.where.yindex;
+		if (ox < 0 || ox >= 32 || oy < 0 || oy >= 32 || !_visibleCell[ox][oy])
+			continue;
+		drawStaticObjectPrisms3D(obj);
+	}
+}
+
+bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
+	const auto drawPrism = [&](const PrismPartDef &def, bool useLook, int colorOverride = -1) {
+		draw3DPrism(obj, def, useLook, colorOverride, true);
+	};
+	const auto drawSphere = [&](int pt0x, int pt0y, int pt0z, int pt1x, int pt1y, int pt1z,
+	                            uint32 fillColor, uint32 outlineColor) {
+		draw3DSphere(obj, pt0x, pt0y, pt0z, pt1x, pt1y, pt1z, fillColor, outlineColor, true);
+	};
+
+	switch (obj.type) {
+	case kObjConsole:
+		drawPrism(kConsolePart, false);
+		break;
+	case kObjCChair:
+		for (int i = 0; i < 5; i++) {
+			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
+			drawPrism(kCChairParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	case kObjPlant:
+		// DOS MakePlant draw order: top pot, then green leaf lines, then pot on top.
+		drawPrism(kPlantParts[1], false); // top pot (soil)
+		for (int i = 2; i < 8; i++)
+			draw3DLeaf(obj, kPlantParts[i]); // leaves as lines
+		drawPrism(kPlantParts[0], false); // pot (drawn last, on top)
+		break;
+	case kObjCouch:
+	case kObjChair: {
+		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
+		for (int i = 0; i < 4; i++) {
+			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
+			drawPrism(parts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	}
+	case kObjTV:
+		_gfx->setDepthRange(0.002, 1.0); // body base layer (pushed back)
+		drawPrism(kTVParts[0], false);
+		_gfx->setDepthRange(0.0, 1.0);   // screen on top of body face
+		drawPrism(kTVParts[1], false);
+		break;
+	case kObjDrawer:
+		for (int i = 0; i < 2; i++) {
+			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
+			drawPrism(kDrawerParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	case kObjFWall:
+		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
+			drawPrism(kFWallPart, false, kColorCorridorWall);
+		else
+			drawPrism(kFWallPart, false);
+		break;
+	case kObjCWall:
+		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
+			drawPrism(kCWallPart, false, kColorCorridorWall);
+		else
+			drawPrism(kCWallPart, false);
+		break;
+	case kObjScreen:
+		drawPrism(kScreenPart, false);
+		break;
+	case kObjTable:
+		for (int i = 0; i < 2; i++) {
+			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
+			drawPrism(kTableParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	case kObjBed:
+	case kObjBBed: {
+		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
+		for (int i = 0; i < 3; i++) {
+			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
+			drawPrism(parts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	}
+	case kObjDesk:
+		for (int i = 0; i < 10; i++) {
+			_gfx->setDepthRange((9 - i) * 0.002, 1.0);
+			drawPrism(kDeskParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	case kObjBox1:
+		drawPrism(kBox1Part, false);
+		break;
+	case kObjBench:
+		drawPrism(kBenchPart, false);
+		break;
+	case kObjCBench:
+		for (int i = 0; i < 2; i++) {
+			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
+			drawPrism(kCBenchParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	case kObjBox2:
+		_gfx->setDepthRange(0.002, 1.0);
+		drawPrism(kBox2Parts[1], false); // base first
+		_gfx->setDepthRange(0.0, 1.0);
+		drawPrism(kBox2Parts[0], false); // top second
+		break;
+	case kObjReactor: {
+		// MakeReactor: animate core height and recolor only the original
+		// side faces. The reactor body stays c_reactor and the core cap stays c_ccore.
+		switch (_coreState[_coreIndex]) {
+		case 0: if (_coreHeight[_coreIndex] < 256) _coreHeight[_coreIndex] += 16; break;
+		case 1: if (_coreHeight[_coreIndex] > 0) _coreHeight[_coreIndex] -= 16; break;
+		case 2: _coreHeight[_coreIndex] = 0; break;
+		}
+		int height = _coreHeight[_coreIndex];
+
+		// Create modified point arrays for height animation.
+		// Core bottom hex (pts 6-11) slides up with height.
+		// When height=256 (closed), bottom matches top → core invisible.
+		// When height=0 (open), bottom at Z=32 → full core visible.
+		int modCorePts[12][3];
+		memcpy(modCorePts, kReactorCorePts, sizeof(modCorePts));
+		for (int i = 6; i < 12; i++)
+			modCorePts[i][2] = height + 32;
+
+		// Ring slides vertically with height.
+		// When height=256, ring at Z=256..288 (encasing core top).
+		// When height=0, ring at Z=0..32 (at base, core exposed).
+		int modRingPts[8][3];
+		memcpy(modRingPts, kReactorBasePts, sizeof(modRingPts));
+		for (int i = 0; i < 4; i++)
+			modRingPts[i][2] = height;
+		for (int i = 4; i < 8; i++)
+			modRingPts[i][2] = height + 32;
+
+		int modCoreSurf[7][8];
+		int modRingSurf[6][8];
+		int modTopSurf[6][8];
+		memcpy(modCoreSurf, kReactorCoreSurf, sizeof(modCoreSurf));
+		memcpy(modRingSurf, kReactorBaseSurf, sizeof(modRingSurf));
+		memcpy(modTopSurf, kReactorBaseSurf, sizeof(modTopSurf));
+
+		// Mac MakeReactor(): first 4 ring/top faces cycle through c_color0..c_color3.
+		static const int kRingColors[] = {kColorRainbow1, kColorRainbow2, kColorRainbow3, kColorRainbow4};
+		const int ringColor = kRingColors[_displayCount % 4];
+		for (int i = 0; i < 4; ++i) {
+			modRingSurf[i][0] = ringColor;
+			modTopSurf[i][0] = ringColor;
+		}
+
+		// Only the 6 core side faces animate. The top face remains c_ccore.
+		static const int kCoreCycle[] = {kColorHCore1, kColorHCore2, kColorHCore3, kColorHCore4, kColorHCore3, kColorHCore2};
+		int coreColor;
+		if (_corePower[_coreIndex] > 1)
+			coreColor = kCoreCycle[_displayCount % 6];
+		else
+			coreColor = kColorCCore;
+		for (int i = 0; i < 6; ++i)
+			modCoreSurf[i][0] = coreColor;
+
+		PrismPartDef modCoreDef = {12, modCorePts, 7, modCoreSurf};
+		PrismPartDef modRingDef = {8, modRingPts, 6, modRingSurf};
+		PrismPartDef modTopDef = {8, kReactorTopPts, 6, modTopSurf};
+
+		// Depth separation matches the original draw order closely, but the
+		// per-face colors now follow MakeReactor() exactly.
+		_gfx->setDepthRange(0.004, 1.0);
+		drawPrism(modTopDef, false);
+		_gfx->setDepthRange(0.002, 1.0);
+		drawPrism(modRingDef, false);
+		if (_coreState[_coreIndex] < 2) {
+			_gfx->setDepthRange(0.0, 1.0);
+			drawPrism(modCoreDef, false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	}
+	case kObjPowerSuit: {
+		// MakePowerSuit: part[4] (power source hexagon) surface[0] pulsates.
+		// Mac: pcycle[count%6]; DOS: pcycle[count%8]
+		static const int kSuitCycle[] = {kColorHCore1, kColorHCore2, kColorHCore3, kColorHCore4, kColorHCore3, kColorHCore2};
+		int sourceColor = kSuitCycle[_displayCount % 6];
+
+		for (int i = 0; i < 4; i++) {
+			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
+			drawPrism(kPowerSuitParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		drawPrism(kPowerSuitParts[4], false, sourceColor);
+		break;
+	}
+	case kObjTeleport:
+		drawPrism(kTelePart, false);
+		break;
+	case kObjCryo:
+		_gfx->setDepthRange(0.002, 1.0);
+		drawPrism(kCryoParts[1], false); // base first
+		_gfx->setDepthRange(0.0, 1.0);
+		drawPrism(kCryoParts[0], false); // top second
+		break;
+	case kObjProjector:
+		// Projector sits on table  draw table first, then projector parts
+		_gfx->setDepthRange(0.008, 1.0);
+		drawPrism(kTableParts[0], false); // table base
+		_gfx->setDepthRange(0.006, 1.0);
+		drawPrism(kTableParts[1], false); // table top
+		_gfx->setDepthRange(0.004, 1.0);
+		drawPrism(kProjectorParts[1], false); // stand
+		_gfx->setDepthRange(0.002, 1.0);
+		drawPrism(kProjectorParts[0], false); // body
+		_gfx->setDepthRange(0.0, 1.0);
+		drawPrism(kProjectorParts[2], false); // lens
+		break;
+	case kObjTub:
+		for (int i = 0; i < 2; i++) {
+			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
+			drawPrism(kTubParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	case kObjSink:
+		for (int i = 0; i < 3; i++) {
+			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
+			drawPrism(kSinkParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	case kObjToilet:
+		for (int i = 0; i < 4; i++) {
+			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
+			drawPrism(kToiletParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	case kObjPToilet:
+		for (int i = 0; i < 5; i++) {
+			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
+			drawPrism(kPToiletParts[i], false);
+		}
+		_gfx->setDepthRange(0.0, 1.0);
+		break;
+	case kObjForkLift:
+		// Draw order: forks, arms, treads, cab (back-to-front)
+		_gfx->setDepthRange(0.010, 1.0);
+		drawPrism(kForkliftParts[3], false); // FLLL (left fork)
+		_gfx->setDepthRange(0.008, 1.0);
+		drawPrism(kForkliftParts[2], false); // FLUL (left arm)
+		_gfx->setDepthRange(0.006, 1.0);
+		drawPrism(kForkliftParts[5], false); // FLLR (right fork)
+		_gfx->setDepthRange(0.004, 1.0);
+		drawPrism(kForkliftParts[4], false); // FLUR (right arm)
+		_gfx->setDepthRange(0.002, 1.0);
+		drawPrism(kForkliftParts[1], false); // treads
+		_gfx->setDepthRange(0.0, 1.0);
+		drawPrism(kForkliftParts[0], false); // cab
+		break;
+	// === Robot types (1-20) ===
+	case kRobEye:
+		drawSphere(0, 0, 100, 0, 0, 200, 15, 15); // ball: white
+		drawPrism(kEyeIrisDef, false);
+		drawPrism(kEyePupilDef, false);
+		break;
+	case kRobPyramid:
+		drawPrism(kPShadowDef, false);
+		drawPrism(kPyramidBodyDef, false);
+		drawSphere(0, 0, 175, 0, 0, 200, 15, 15); // ball on top
+		drawPrism(kPIrisDef, false);
+		drawPrism(kPPupilDef, false);
+		break;
+	case kRobCube:
+		drawPrism(kCubeBodyDef, false);
+		break;
+	case kRobUPyramid:
+		drawPrism(kUPShadowDef, false);
+		drawPrism(kUPyramidBodyDef, false);
+		break;
+	case kRobFEye:
+		drawSphere(0, 0, 0, 0, 0, 100, 15, 15);
+		drawPrism(kFEyeIrisDef, false);
+		drawPrism(kFEyePupilDef, false);
+		break;
+	case kRobFPyramid:
+		drawPrism(kFPyramidBodyDef, false);
+		break;
+	case kRobFCube:
+		drawPrism(kFCubeBodyDef, false);
+		break;
+	case kRobFUPyramid:
+		drawPrism(kFUPyramidBodyDef, false);
+		break;
+	case kRobSEye:
+		drawSphere(0, 0, 0, 0, 0, 50, 15, 15);
+		drawPrism(kSEyeIrisDef, false);
+		drawPrism(kSEyePupilDef, false);
+		break;
+	case kRobSPyramid:
+		drawPrism(kSPyramidBodyDef, false);
+		break;
+	case kRobSCube:
+		drawPrism(kSCubeBodyDef, false);
+		break;
+	case kRobSUPyramid:
+		drawPrism(kSUPyramidBodyDef, false);
+		break;
+	case kRobMEye:
+		drawSphere(0, 0, 0, 0, 0, 25, 15, 15);
+		drawPrism(kMEyeIrisDef, false);
+		drawPrism(kMEyePupilDef, false);
+		break;
+	case kRobMPyramid:
+		drawPrism(kMPyramidBodyDef, false);
+		break;
+	case kRobMCube:
+		drawPrism(kMCubeBodyDef, false);
+		break;
+	case kRobMUPyramid:
+		drawPrism(kMUPyramidBodyDef, false);
+		break;
+	case kRobQueen:
+		drawPrism(kQThoraxDef, false);
+		drawPrism(kQAbdomenDef, false);
+		drawPrism(kQLWingDef, false);
+		drawPrism(kQRWingDef, false);
+		drawSphere(0, 0, 130, 0, 0, 155, 15, 15); // queen ball
+		drawPrism(kQIrisDef, false);
+		drawPrism(kQPupilDef, false);
+		break;
+	case kRobDrone:
+	case kRobSoldier:
+		drawPrism(kDAbdomenDef, false);
+		drawPrism(kDLLPincerDef, false);
+		drawPrism(kDRRPincerDef, false);
+		drawPrism(kDLEyeDef, false);
+		drawPrism(kDREyeDef, false);
+		break;
+	case kRobSnoop:
+		drawPrism(kSnoopAbdomenDef, false);
+		drawPrism(kSnoopHeadDef, false);
+		break;
+	default:
+		return false;
+	}
+	return true;
+}
+
+} // End of namespace Colony


Commit: adb4d83fbee8428dbfa739594d7f736a69e74772
    https://github.com/scummvm/scummvm/commit/adb4d83fbee8428dbfa739594d7f736a69e74772
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:40+02:00

Commit Message:
COLONY: apply ScummVM coding standards formatting

Changed paths:
    engines/colony/battle.cpp
    engines/colony/colony.cpp
    engines/colony/intro.cpp
    engines/colony/render_objects.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 3d94bb7bf87..c57419ee0b8 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -695,7 +695,8 @@ void ColonyEngine::battleDrawTanks() {
 		int lPincerPts[4][3];
 		int nabs_lookx = (drone.lookx > 0) ? -drone.lookx : drone.lookx; // nabs
 		int lLook = nabs_lookx - 32;
-		if (lLook < 0) lLook += 256;
+		if (lLook < 0)
+			lLook += 256;
 		for (int j = 0; j < 4; j++) {
 			long tcos = _cost[(uint8)lLook];
 			long tsin = _sint[(uint8)lLook];
@@ -712,7 +713,8 @@ void ColonyEngine::battleDrawTanks() {
 		// Build animated right pincer vertices
 		int rPincerPts[4][3];
 		int rLook = ABS(drone.lookx) - 32;
-		if (rLook < 0) rLook += 256;
+		if (rLook < 0)
+			rLook += 256;
 		for (int j = 0; j < 4; j++) {
 			long tcos = _cost[(uint8)rLook];
 			long tsin = _sint[(uint8)rLook];
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 198a2853391..b54d30ff973 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -233,7 +233,8 @@ void ColonyEngine::loadMacColors() {
 	uint32 vers = file->readUint32BE(); // Should be 'v1.0' = 0x76312E30
 	(void)vers; // Ignore
 	uint16 cnum = file->readUint16BE();
-	if (cnum > 145) cnum = 145;
+	if (cnum > 145)
+		cnum = 145;
 
 	for (int i = 0; i < cnum; i++) {
 		_macColors[i].fg[0] = file->readUint16BE();
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index e1b5155e521..f8208055099 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -323,7 +323,8 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 		int s = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
 		int c = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
 		int d = _randomSource.getRandomNumber(MAXSTAR);
-		if (d < 1) d = 1;
+		if (d < 1)
+			d = 1;
 		int rr = rtable[d];
 		int xx = centerX + (int)(((long long)s * rr) >> 7);
 		int yy = centerY + (int)(((long long)c * rr) >> 7);
@@ -341,7 +342,8 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 
 	for (int i = 0; i < NSTARS; i++) {
 		int d = dist[i] = _randomSource.getRandomNumber(MAXSTAR);
-		if (d <= 0x030) d = dist[i] = MAXSTAR;
+		if (d <= 0x030)
+			d = dist[i] = MAXSTAR;
 		int s = xang[i] = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
 		int c = yang[i] = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
 
@@ -385,7 +387,8 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 			}
 
 			int d = (dist[i] -= deltapd);
-			if (d < 1) d = 1;
+			if (d < 1)
+				d = 1;
 			int rr = rtable[d];
 			xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
 			ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
@@ -399,7 +402,8 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 
 	// Fade-out phase: stars fly off without resetting (trails accumulate via XOR)
 	int nstars = 2 * ((MAXSTAR - 0x030) / deltapd);
-	if (nstars > 200) nstars = 200;
+	if (nstars > 200)
+		nstars = 200;
 	for (int k = 0; k < nstars; k++) {
 		if (checkSkipRequested()) {
 			_gfx->setXorMode(false);
@@ -411,7 +415,8 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 			int s = xang[i];
 			int c = yang[i];
 			dist[i] -= deltapd;
-			if (dist[i] <= 0x030) dist[i] = MAXSTAR;
+			if (dist[i] <= 0x030)
+				dist[i] = MAXSTAR;
 
 			if (d >= 1 && d <= MAXSTAR) {
 				int rr1 = rtable[d];
@@ -476,7 +481,8 @@ bool ColonyEngine::makeBlackHole() {
 			_gfx->setPalette(pal, 128, 1);
 
 			starcnt++;
-			if (starcnt == 8) starcnt = 0;
+			if (starcnt == 8)
+				starcnt = 0;
 
 			for (int j = 0; j < 256; j += 8) {
 				int idx = (j + starcnt) & 0xFF;
@@ -496,7 +502,8 @@ bool ColonyEngine::makeBlackHole() {
 			_gfx->copyToScreen();
 			_system->delayMillis(16);
 
-			if (checkSkipRequested()) return true;
+			if (checkSkipRequested())
+				return true;
 		}
 	}
 	_gfx->copyToScreen();
@@ -555,7 +562,8 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft);
 		_gfx->copyToScreen();
 
-		if (checkSkipRequested()) return true;
+		if (checkSkipRequested())
+			return true;
 		_system->delayMillis(8);
 	}
 
@@ -564,7 +572,8 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 	//   while(!SoundDone()); StopSound(); PlayKlaxon(); InvertRect(&invrt);
 	_sound->stop(); // EndCSound()
 	for (int i = 0; i < 6; i++) {
-		if (checkSkipRequested()) return true;
+		if (checkSkipRequested())
+			return true;
 
 		// Wait for previous klaxon to finish
 		while (_sound->isPlaying() && !shouldQuit())
@@ -590,7 +599,8 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 		_gfx->drawString(font, str, x, centery + 2, 176, Graphics::kTextAlignLeft);
 		_gfx->copyToScreen();
 
-		if (checkSkipRequested()) return true;
+		if (checkSkipRequested())
+			return true;
 		_system->delayMillis(8);
 	}
 
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 1c960a758e6..6eb6c359fc0 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1091,9 +1091,17 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		// MakeReactor: animate core height and recolor only the original
 		// side faces. The reactor body stays c_reactor and the core cap stays c_ccore.
 		switch (_coreState[_coreIndex]) {
-		case 0: if (_coreHeight[_coreIndex] < 256) _coreHeight[_coreIndex] += 16; break;
-		case 1: if (_coreHeight[_coreIndex] > 0) _coreHeight[_coreIndex] -= 16; break;
-		case 2: _coreHeight[_coreIndex] = 0; break;
+		case 0:
+			if (_coreHeight[_coreIndex] < 256)
+				_coreHeight[_coreIndex] += 16;
+			break;
+		case 1:
+			if (_coreHeight[_coreIndex] > 0)
+				_coreHeight[_coreIndex] -= 16;
+			break;
+		case 2:
+			_coreHeight[_coreIndex] = 0;
+			break;
 		}
 		int height = _coreHeight[_coreIndex];
 


Commit: 49765868b2f59c61fb8fa68d789a8ac35617e607
    https://github.com/scummvm/scummvm/commit/49765868b2f59c61fb8fa68d789a8ac35617e607
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:41+02:00

Commit Message:
COLONY: inline gfx.h contents into colony.h and remove separate header

Changed paths:
  R engines/colony/gfx.h
    engines/colony/animation.cpp
    engines/colony/battle.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_internal.h
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 0fcb1edd244..4746043643b 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "colony/colony.h"
-#include "colony/gfx.h"
+#include "colony/renderer.h"
 #include "common/system.h"
 #include "common/events.h"
 #include "common/debug.h"
diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index c57419ee0b8..1e16c89b8f5 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -25,7 +25,7 @@
 // OpenGL renderer with filled polygons and depth-tested 3D.
 
 #include "colony/colony.h"
-#include "colony/gfx.h"
+#include "colony/renderer.h"
 #include "colony/sound.h"
 #include "common/debug.h"
 #include "common/system.h"
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index b54d30ff973..069307fcfeb 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -21,7 +21,7 @@
 
 #include "colony/colony.h"
 #include "colony/debugger.h"
-#include "colony/gfx.h"
+#include "colony/renderer.h"
 #include "common/config-manager.h"
 #include "common/file.h"
 #include "common/system.h"
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 29db499615f..554aa7df038 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -29,7 +29,7 @@
 #include "common/rect.h"
 #include "graphics/framelimiter.h"
 #include "common/rendermode.h"
-#include "colony/gfx.h"
+#include "colony/renderer.h"
 #include "colony/sound.h"
 #include "graphics/macgui/macwindowmanager.h"
 #include "graphics/macgui/macmenu.h"
diff --git a/engines/colony/gfx.h b/engines/colony/gfx.h
deleted file mode 100644
index d1291cf5018..00000000000
--- a/engines/colony/gfx.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef COLONY_GFX_H
-#define COLONY_GFX_H
-
-// The Colony engine uses Renderer directly (like Freescape).
-// This header is kept for source compatibility with files that #include "colony/gfx.h".
-#include "colony/renderer.h"
-
-#endif
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index d3b185404d9..1b41120bb86 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "colony/colony.h"
-#include "colony/gfx.h"
+#include "colony/renderer.h"
 #include "colony/sound.h"
 #include "common/system.h"
 #include "common/debug.h"
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index f8208055099..41710ded8ef 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "colony/colony.h"
-#include "colony/gfx.h"
+#include "colony/renderer.h"
 #include "common/system.h"
 #include "common/events.h"
 #include "common/debug.h"
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 676a41e6a84..a52f8e2e18c 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "colony/colony.h"
-#include "colony/gfx.h"
+#include "colony/renderer.h"
 #include "common/system.h"
 #include "common/debug.h"
 #include <math.h>
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index cc155a79374..6d0ad506ace 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -21,7 +21,7 @@
 
 #include "colony/colony.h"
 #include "colony/render_internal.h"
-#include "colony/gfx.h"
+#include "colony/renderer.h"
 #include "common/system.h"
 #include "common/util.h"
 #include "common/algorithm.h"
diff --git a/engines/colony/render_internal.h b/engines/colony/render_internal.h
index 25f47b3135d..d1518ff7dc9 100644
--- a/engines/colony/render_internal.h
+++ b/engines/colony/render_internal.h
@@ -23,7 +23,7 @@
 #define COLONY_RENDER_INTERNAL_H
 
 #include "colony/colony.h"
-#include "colony/gfx.h"
+#include "colony/renderer.h"
 
 namespace Colony {
 
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 0605ddd10bc..b5a6abf9e4f 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "colony/colony.h"
-#include "colony/gfx.h"
+#include "colony/renderer.h"
 #include "common/system.h"
 #include "common/util.h"
 #include "common/debug.h"


Commit: 8d9e7f70e7326fca3e455b058a1e8ef24d0dba9c
    https://github.com/scummvm/scummvm/commit/8d9e7f70e7326fca3e455b058a1e8ef24d0dba9c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:41+02:00

Commit Message:
COLONY: drag and drop elements

Changed paths:
    engines/colony/animation.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 4746043643b..48f16d7fce6 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -1515,6 +1515,7 @@ void ColonyEngine::moveObject(int index) {
 		return;
 
 	ComplexSprite *ls = _lSprites[index];
+	const int objectFrames = (int)ls->objects.size();
 
 	// Build link group
 	Common::Array<int> linked;
@@ -1531,6 +1532,58 @@ void ColonyEngine::moveObject(int index) {
 	int ox = _screenR.left + (_screenR.width() - 416) / 2;
 	ox = (ox / 8) * 8;
 	int oy = _screenR.top + (_screenR.height() - 264) / 2;
+	int oldKeyTarget = -1;
+
+	auto findKeyTarget = [&](const Common::Point &screenPt) -> int {
+		if (!ls->key)
+			return -1;
+
+		const Common::Point pt(screenPt.x - ox, screenPt.y - oy);
+
+		for (int i = (int)_lSprites.size() - 1; i >= 0; --i) {
+			if (i == index || !_lSprites[i]->onoff || _lSprites[i]->lock != ls->key)
+				continue;
+
+			const ComplexSprite *target = _lSprites[i];
+			const int cnum = target->current;
+			if (cnum < 0 || cnum >= (int)target->objects.size())
+				continue;
+
+			const int spriteIdx = target->objects[cnum].spritenum;
+			if (spriteIdx < 0 || spriteIdx >= (int)_cSprites.size())
+				continue;
+
+			const Sprite *s = _cSprites[spriteIdx];
+			Common::Rect r = s->clip;
+			r.translate(target->xloc + target->objects[cnum].xloc,
+			            target->yloc + target->objects[cnum].yloc);
+
+			if (pt.x < r.left || pt.x > r.right || pt.y < r.top || pt.y > r.bottom)
+				continue;
+
+			const Image *mask = s->mask;
+			if (mask && mask->data) {
+				const int row = pt.y - r.top;
+				const int col = pt.x - r.left;
+				const int bitCol = (col + mask->align) * mask->bits;
+				const int maskIndex = row * mask->rowBytes + (bitCol / 8);
+
+				if (maskIndex < 0 || maskIndex >= mask->rowBytes * mask->height)
+					continue;
+
+				byte maskByte = mask->data[maskIndex];
+				if (mask->planes == 2)
+					maskByte |= mask->data[mask->rowBytes * mask->height + maskIndex];
+				maskByte >>= bitCol % 8;
+				if (!(maskByte & ((1 << mask->bits) - 1)))
+					continue;
+			}
+
+			return i;
+		}
+
+		return -1;
+	};
 
 	// Drag loop: track mouse while left button held.
 	// NOTE: The original DOS hides dragged sprites during drag (setObjectOnOff FALSE)
@@ -1566,6 +1619,32 @@ void ColonyEngine::moveObject(int index) {
 				_lSprites[linked[i]]->yloc += dy;
 			}
 
+			// Original MoveObject/KeylSprite: dragging a "key" sprite over a matching
+			// lock sprite toggles that target between its first two states.
+			const int keyTarget = findKeyTarget(cur);
+			if (keyTarget >= 0 && keyTarget != oldKeyTarget) {
+				ComplexSprite *target = _lSprites[keyTarget];
+				if (target->current == 1) {
+					target->current--;
+					if (ls->type != 2 && objectFrames > 1) {
+						ls->current++;
+						if (ls->current >= objectFrames)
+							ls->current = 0;
+					}
+				} else if (target->current == 0) {
+					target->current++;
+					if (ls->type != 2 && objectFrames > 1) {
+						ls->current++;
+						if (ls->current >= objectFrames)
+							ls->current = 0;
+					}
+				}
+				debugC(1, kColonyDebugAnimation,
+					"moveObject: key sprite %d toggled lock sprite %d to state %d",
+					index + 1, keyTarget + 1, target->current + 1);
+				oldKeyTarget = keyTarget;
+			}
+
 			old = cur;
 		}
 


Commit: e16e05ac1b47c6ecdc16a07ac4ce21a3331fc7bb
    https://github.com/scummvm/scummvm/commit/e16e05ac1b47c6ecdc16a07ac4ce21a3331fc7bb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:41+02:00

Commit Message:
COLONY: add robot eye and pupil 3D rendering

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 554aa7df038..cfe1a741715 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -208,7 +208,17 @@ enum ObjColor {
 	kColorHCore2 = 101,
 	kColorHCore3 = 102,
 	kColorHCore4 = 103,
-	kColorCCore = 104
+	kColorCCore = 104,
+	// Semantic robot colors that need platform- or level-specific mapping.
+	kColorEyeball = 105,
+	kColorEyeIris = 106,
+	kColorMiniEyeIris = 107,
+	kColorDroneEye = 108,
+	kColorSoldierBody = 109,
+	kColorSoldierEye = 110,
+	kColorQueenBody = 111,
+	kColorQueenEye = 112,
+	kColorQueenWingRed = 113
 };
 
 enum {
@@ -533,7 +543,7 @@ public:
 	};
 
 private:
-	void draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride = -1, bool accumulateBounds = false);
+	void draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride = -1, bool accumulateBounds = false, bool forceVisible = false);
 	void draw3DLeaf(const Thing &obj, const PrismPartDef &def);
 	void draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 	                  int pt1x, int pt1y, int pt1z, uint32 fillColor, uint32 outlineColor, bool accumulateBounds = false);
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 6d0ad506ace..c2c51b1c39a 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -138,7 +138,7 @@ static const DOSColorEntry g_dosColors[79] = {
 
 // Look up the DOS lsColor entry for a given ObjColor index.
 // Returns a fallback entry for out-of-range indices.
-static const DOSColorEntry &lookupDOSColor(int colorIdx) {
+static const DOSColorEntry &lookupDOSColor(int colorIdx, int level) {
 	// DOS pcycle for animated reactor/suit: WHITE,LTGRAY,GRAY,DKGRAY,BLACK (bounce)
 	static const DOSColorEntry kHCore1 = {0, 0, 15, 15, 15, 1}; // WHITE
 	static const DOSColorEntry kHCore2 = {1, 8,  8, 15,  8, 3}; // LTGRAY
@@ -155,6 +155,15 @@ static const DOSColorEntry &lookupDOSColor(int colorIdx) {
 	case kColorHCore3: return kHCore3;
 	case kColorHCore4: return kHCore4;
 	case kColorCCore:  return kCCoreEntry;
+	case kColorEyeball: return g_dosColors[kColorEye];
+	case kColorEyeIris:
+	case kColorMiniEyeIris:
+	case kColorQueenEye: return g_dosColors[kColorIris];
+	case kColorDroneEye:
+	case kColorSoldierEye: return g_dosColors[kColorEyes];
+	case kColorSoldierBody: return g_dosColors[kColorDrone];
+	case kColorQueenBody: return g_dosColors[(level == 7) ? 78 : kColorQueen];
+	case kColorQueenWingRed: return g_dosColors[63];
 	// Ring animation colors: cycle through DOS EGA colors
 	// DOS reactor rings use color=1+count%5 → values 1-5 → cColor[1..5]
 	case kColorRainbow1: return g_dosColors[2];  // BLUE
@@ -168,12 +177,12 @@ static const DOSColorEntry &lookupDOSColor(int colorIdx) {
 // Map ObjColor → Mac B&W dither pattern (from ROBOCOLR.C MONOCHROME field).
 // The monochrome field in the DOS table matches MacPattern enum values directly:
 // WHITE=0, LTGRAY=1, GRAY=2, DKGRAY=3, BLACK=4, CLEAR=5.
-static int lookupMacPattern(int colorIdx) {
-	return lookupDOSColor(colorIdx).monochrome;
+static int lookupMacPattern(int colorIdx, int level) {
+	return lookupDOSColor(colorIdx, level).monochrome;
 }
 
 // Map ObjColor constant → Mac Color256 index (cColor[] from colordef.h).
-static int mapObjColorToMacColor(int colorIdx) {
+static int mapObjColorToMacColor(int colorIdx, int level) {
 	switch (colorIdx) {
 	case kColorBath:      return 97;  // c_tub
 	case kColorWater:     return 102; // c_water
@@ -228,6 +237,15 @@ static int mapObjColorToMacColor(int colorIdx) {
 	case kColorPupil:     return 36;  // c_pupil
 	case kColorPyramid:   return 37;  // c_pyramid
 	case kColorQueen:     return 48;  // c_queenP
+	case kColorEyeball:   return 34;  // c_eyeball
+	case kColorEyeIris:   return 32;  // c_eye
+	case kColorMiniEyeIris: return 33; // c_meye
+	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 kColorQueenEye:  return 49;  // c_equeen
+	case kColorQueenWingRed: return 48; // unused in Mac mode
 	case kColorTopSnoop:  return 56;  // c_snooper1
 	case kColorBottomSnoop: return 57; // c_snooper2
 	case kColorUPyramid:  return 41;  // c_upyramid
@@ -243,6 +261,94 @@ static int mapObjColorToMacColor(int colorIdx) {
 	}
 }
 
+static void projectCorridorPointClamped(const Common::Rect &screenR, int look, int lookY,
+                                        const int *sint, const int *cost, int camX, int camY,
+                                        float worldX, float worldY, float worldZ,
+                                        int &screenX, int &screenY) {
+	const float dx = worldX - camX;
+	const float dy = worldY - camY;
+	const float dz = worldZ;
+
+	const float sinYaw = sint[look] / 128.0f;
+	const float cosYaw = cost[look] / 128.0f;
+	const float side = dx * sinYaw - dy * cosYaw;
+	const float forward = dx * cosYaw + dy * sinYaw;
+
+	const float pitchRad = lookY * 2.0f * (float)M_PI / 256.0f;
+	const float sinPitch = sinf(pitchRad);
+	const float cosPitch = cosf(pitchRad);
+
+	const float eyeX = side;
+	const float eyeY = dz * cosPitch + forward * sinPitch;
+	const float eyeZ = dz * sinPitch - forward * cosPitch;
+	const float depth = MAX(-eyeZ, 1.0f);
+
+	const float focal = (screenR.height() * 0.5f) / tanf(75.0f * (float)M_PI / 360.0f);
+	const float centerX = screenR.left + screenR.width() * 0.5f;
+	const float centerY = screenR.top + screenR.height() * 0.5f;
+
+	screenX = (int)roundf(centerX + (eyeX * focal / depth));
+	screenY = (int)roundf(centerY - (eyeY * focal / depth));
+}
+
+static bool isSurfaceVisible(const int *surface, int pointCount, const int *screenX, const int *screenY) {
+	if (pointCount < 3)
+		return false;
+
+	for (int i = 0; i < pointCount; ++i) {
+		const int cur = surface[i];
+		const int next = surface[(i + 1) % pointCount];
+		const int next2 = surface[(i + 2) % pointCount];
+		const long dx = screenX[cur] - screenX[next];
+		const long dy = screenY[cur] - screenY[next];
+		const long dxp = screenX[next2] - screenX[next];
+		const long dyp = screenY[next2] - screenY[next];
+
+		if (dx < 0) {
+			if (dy == 0) {
+				if (dyp > 0)
+					return false;
+				if (dyp < 0)
+					return true;
+			} else {
+				const long b = dy * dxp - dx * dyp;
+				if (b > 0)
+					return false;
+				if (b < 0)
+					return true;
+			}
+		} else if (dx > 0) {
+			if (dy == 0) {
+				if (dyp < 0)
+					return false;
+				if (dyp > 0)
+					return true;
+			} else {
+				const long b = dx * dyp - dy * dxp;
+				if (b < 0)
+					return false;
+				if (b > 0)
+					return true;
+			}
+		} else {
+			if (dy < 0) {
+				if (dxp > 0)
+					return true;
+				if (dxp < 0)
+					return false;
+			}
+			if (dy > 0) {
+				if (dxp < 0)
+					return true;
+				if (dxp > 0)
+					return false;
+			}
+		}
+	}
+
+	return false;
+}
+
 void ColonyEngine::quadrant() {
 	int remain;
 	int quad;
@@ -328,13 +434,47 @@ static bool projectCorridorPoint(const Common::Rect &screenR, uint8 look, int8 l
 	return true;
 }
 
-void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride, bool accumulateBounds) {
+void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride, bool accumulateBounds, bool forceVisible) {
 	// +32 compensates for the original sine table's 45° phase offset.
 	// Object angles from game data were stored assuming that offset.
 	const uint8 ang = (useLook ? obj.where.look : obj.where.ang) + 32;
 	const long rotCos = _cost[ang];
 	const long rotSin = _sint[ang];
 	const bool lit = (_corePower[_coreIndex] > 0);
+	float transformedX[32];
+	float transformedY[32];
+	float transformedZ[32];
+	int projectedX[32];
+	int projectedY[32];
+
+	assert(def.pointCount <= ARRAYSIZE(transformedX));
+
+	for (int i = 0; i < def.pointCount; ++i) {
+		const int ox = def.points[i][0];
+		const int oy = def.points[i][1];
+		const int oz = def.points[i][2];
+		const long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
+		const long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+
+		transformedX[i] = (float)(rx + obj.where.xloc);
+		transformedY[i] = (float)(ry + obj.where.yloc);
+		transformedZ[i] = (float)(oz - 160);
+		projectCorridorPointClamped(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+		                            transformedX[i], transformedY[i], transformedZ[i],
+		                            projectedX[i], projectedY[i]);
+
+		if (accumulateBounds) {
+			int sx = 0;
+			int sy = 0;
+			if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+			                         transformedX[i], transformedY[i], transformedZ[i], sx, sy)) {
+				obj.where.xmn = MIN(obj.where.xmn, sx);
+				obj.where.xmx = MAX(obj.where.xmx, sx);
+				obj.where.zmn = MIN(obj.where.zmn, sy);
+				obj.where.zmx = MAX(obj.where.zmx, sy);
+			}
+		}
+	}
 
 	for (int i = 0; i < def.surfaceCount; i++) {
 		const int colorIdx = (colorOverride >= 0) ? colorOverride : def.surfaces[i][0];
@@ -345,38 +485,23 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 		float px[8];
 		float py[8];
 		float pz[8];
+		int pointIdx[8];
 		int count = 0;
 
 		for (int j = 0; j < n; j++) {
 			const int cur = def.surfaces[i][j + 2];
 			if (cur < 0 || cur >= def.pointCount)
 				continue;
-
-			int ox = def.points[cur][0];
-			int oy = def.points[cur][1];
-			int oz = def.points[cur][2];
-
-			// World relative rotation
-			long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
-			long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
-
-			px[count] = (float)(rx + obj.where.xloc);
-			py[count] = (float)(ry + obj.where.yloc);
-			pz[count] = (float)(oz - 160); // Shift from floor-relative (z=0) to world (z=-160)
-			if (accumulateBounds) {
-				int sx = 0;
-				int sy = 0;
-				if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
-				                         px[count], py[count], pz[count], sx, sy)) {
-					obj.where.xmn = MIN(obj.where.xmn, sx);
-					obj.where.xmx = MAX(obj.where.xmx, sx);
-					obj.where.zmn = MIN(obj.where.zmn, sy);
-					obj.where.zmx = MAX(obj.where.zmx, sy);
-				}
-			}
+			pointIdx[count] = cur;
+			px[count] = transformedX[cur];
+			py[count] = transformedY[cur];
+			pz[count] = transformedZ[cur];
 			count++;
 		}
 
+		if (!forceVisible && count >= 3 && !isSurfaceVisible(pointIdx, count, projectedX, projectedY))
+			continue;
+
 		if (count >= 3) {
 			if (colorIdx == kColorClear) {
 				// CLEAR = no fill, but still draw wireframe outline.
@@ -412,7 +537,7 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 					debugC(5, kColonyDebugRender, "draw3DPrism Mac corridor wall: fg=0x%08X bg=0x%08X lit=%d",
 					      fg, bg, lit);
 				} else {
-					int mIdx = mapObjColorToMacColor(colorIdx);
+					int mIdx = mapObjColorToMacColor(colorIdx, _level);
 					pattern = _macColors[mIdx].pattern;
 					fg = packMacColor(_macColors[mIdx].fg);
 					bg = packMacColor(_macColors[mIdx].bg);
@@ -445,7 +570,7 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 			} else if (lit) {
 				if (_renderMode == Common::kRenderMacintosh) {
 					// Mac B&W: stipple dither pattern fill + black outline
-					int pattern = lookupMacPattern(colorIdx);
+					int pattern = lookupMacPattern(colorIdx, _level);
 					if (pattern == kPatternClear)
 						continue;
 					if (!_wireframe) {
@@ -458,7 +583,7 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 					// EGA: per-surface colors from DOS lsColor table.
 					// polyfill ON  → B&W fill (from MONOCHROME field), colored LINECOLOR outline.
 					// polyfill OFF → outline only with LINECOLOR.
-					const DOSColorEntry &dc = lookupDOSColor(colorIdx);
+					const DOSColorEntry &dc = lookupDOSColor(colorIdx, _level);
 					if (!_wireframe) {
 						// Polyfill mode: B&W fill + colored LINECOLOR outline.
 						// LINECOLOR (not LINEFILLCOLOR)  has proper contrast against B&W fills.
@@ -529,8 +654,10 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
                                 int pt1x, int pt1y, int pt1z,
                                 uint32 fillColor, uint32 outlineColor, bool accumulateBounds) {
-	// Sphere defined by two object-local points: pt0 (center bottom) and pt1 (center top).
-	// Center is midpoint, radius is half the distance (along z typically).
+	// Original Colony eye/ball primitives store the bottom pole in pt0 and the
+	// sphere center in pt1. The classic renderer builds the oval from the
+	// screen-space delta between those two projected points, so the world-space
+	// radius is the full pt0↔pt1 distance, not half.
 	// Rendered as a billboard polygon facing the camera.
 	const uint8 ang = obj.where.ang + 32;
 	const long rotCos = _cost[ang];
@@ -550,12 +677,12 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 	float wy1 = (float)(ry1 + obj.where.yloc);
 	float wz1 = (float)(pt1z - 160);
 
-	// Center and radius
-	float cx = (wx0 + wx1) * 0.5f;
-	float cy = (wy0 + wy1) * 0.5f;
-	float cz = (wz0 + wz1) * 0.5f;
+	// Center and radius (pt1 is the center, pt0 is the bottom pole).
+	float cx = wx1;
+	float cy = wy1;
+	float cz = wz1;
 	float dx = wx1 - wx0, dy = wy1 - wy0, dz = wz1 - wz0;
-	float radius = sqrtf(dx * dx + dy * dy + dz * dz) * 0.5f;
+	float radius = sqrtf(dx * dx + dy * dy + dz * dz);
 
 	// Billboard: create a polygon perpendicular to the camera direction.
 	// Camera is at (_me.xloc, _me.yloc, 0).
@@ -600,41 +727,47 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
 		// Mac color: map fillColor to Mac color index and use RGB
 		// fillColor is an ObjColor enum value passed by the caller
-		int mIdx = mapObjColorToMacColor((int)fillColor);
-		int pattern = _macColors[mIdx].pattern;
-		uint32 fg = packMacColor(_macColors[mIdx].fg);
-		uint32 bg = packMacColor(_macColors[mIdx].bg);
+		const int fillIdx = mapObjColorToMacColor((int)fillColor, _level);
+		const int outlineIdx = mapObjColorToMacColor((int)outlineColor, _level);
+		int pattern = _macColors[fillIdx].pattern;
+		uint32 fg = packMacColor(_macColors[fillIdx].fg);
+		uint32 bg = packMacColor(_macColors[fillIdx].bg);
+		uint32 line = packMacColor(_macColors[outlineIdx].fg);
 		if (!lit) {
 			fg = 0xFF000000;
 			bg = 0xFF000000;
+			line = 0xFF000000;
 			pattern = 4;
 		}
 		const byte *stipple = setupMacPattern(_gfx, pattern, fg, bg);
-		_gfx->draw3DPolygon(px, py, pz, N, fg);
+		_gfx->draw3DPolygon(px, py, pz, N, line);
 		if (stipple)
 			_gfx->setStippleData(nullptr);
 	} else if (lit) {
 		if (_renderMode == Common::kRenderMacintosh) {
-			int pattern = (fillColor == 15) ? kPatternWhite : kPatternGray;
+			int pattern = lookupMacPattern((int)fillColor, _level);
+			if (pattern == kPatternClear)
+				pattern = kPatternGray;
 			if (!_wireframe) {
 				_gfx->setWireframe(true, pattern == kPatternBlack ? 0 : 255);
 			}
 			_gfx->setStippleData(kMacStippleData[pattern]);
-			_gfx->draw3DPolygon(px, py, pz, N, 0);
+			_gfx->draw3DPolygon(px, py, pz, N, lookupDOSColor((int)outlineColor, _level).lineColor);
 			_gfx->setStippleData(nullptr);
 		} else {
-			// EGA: per-surface fill + outline.
+			const DOSColorEntry &fill = lookupDOSColor((int)fillColor, _level);
+			const DOSColorEntry &outline = lookupDOSColor((int)outlineColor, _level);
 			if (!_wireframe) {
-				_gfx->setWireframe(true, fillColor);
+				_gfx->setWireframe(true, fill.fillColor);
 			}
-			_gfx->draw3DPolygon(px, py, pz, N, outlineColor);
+			_gfx->draw3DPolygon(px, py, pz, N, outline.lineColor);
 		}
 	} else {
 		// Unlit: black fill, white outline.
 		if (!_wireframe) {
 			_gfx->setWireframe(true, 0);
 		}
-		_gfx->draw3DPolygon(px, py, pz, N, outlineColor);
+		_gfx->draw3DPolygon(px, py, pz, N, 15);
 	}
 }
 
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 6eb6c359fc0..3df0d74292b 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -752,7 +752,7 @@ static const Colony::ColonyEngine::PrismPartDef kUPShadowDef = {4, kUPShadowPts,
 static const int kEyeIrisPts[4][3] = {
 	{60, 0, 140}, {60, 60, 200}, {60, 0, 260}, {60, -60, 200}
 };
-static const int kEyeIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
+static const int kEyeIrisSurf[1][8] = {{kColorEyeIris, 4, 0, 1, 2, 3, 0, 0}};
 static const int kEyePupilPts[4][3] = {
 	{66, 0, 175}, {66, 25, 200}, {66, 0, 225}, {66, -25, 200}
 };
@@ -850,7 +850,7 @@ static const Colony::ColonyEngine::PrismPartDef kMEyePupilDef = {4, kMEyePupilPt
 static const int kQIrisPts[4][3] = {
 	{15, 0, 140}, {15, 15, 155}, {15, 0, 170}, {15, -15, 155}
 };
-static const int kQIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
+static const int kQIrisSurf[1][8] = {{kColorQueenEye, 4, 0, 1, 2, 3, 0, 0}};
 static const int kQPupilPts[4][3] = {
 	{16, 0, 148}, {16, 6, 155}, {16, 0, 161}, {16, -6, 155}
 };
@@ -861,10 +861,10 @@ static const int kQAbdomenPts[9][3] = {
 	{0, 0, 150}, {0, 0, 110}, {0, 25, 130}, {0, -25, 130}
 };
 static const int kQAbdomenSurf[9][8] = {
-	{kColorQueen, 3, 0, 3, 1, 0, 0, 0}, {kColorQueen, 3, 0, 1, 4, 0, 0, 0},
-	{kColorQueen, 3, 0, 2, 3, 0, 0, 0}, {kColorQueen, 3, 0, 4, 2, 0, 0, 0},
-	{kColorQueen, 4, 1, 5, 8, 4, 1, 0}, {kColorQueen, 4, 1, 3, 7, 5, 1, 0},
-	{kColorQueen, 4, 2, 4, 8, 6, 2, 0}, {kColorQueen, 4, 2, 6, 7, 3, 2, 0},
+	{kColorQueenBody, 3, 0, 3, 1, 0, 0, 0}, {kColorQueenBody, 3, 0, 1, 4, 0, 0, 0},
+	{kColorQueenBody, 3, 0, 2, 3, 0, 0, 0}, {kColorQueenBody, 3, 0, 4, 2, 0, 0, 0},
+	{kColorQueenBody, 4, 1, 5, 8, 4, 1, 0}, {kColorQueenBody, 4, 1, 3, 7, 5, 1, 0},
+	{kColorQueenBody, 4, 2, 4, 8, 6, 2, 0}, {kColorQueenBody, 4, 2, 6, 7, 3, 2, 0},
 	{kColorClear, 4, 5, 7, 6, 8, 5, 0}
 };
 // Queen thorax
@@ -873,10 +873,10 @@ static const int kQThoraxPts[9][3] = {
 	{0, 0, 150}, {0, 0, 110}, {0, 25, 130}, {0, -25, 130}
 };
 static const int kQThoraxSurf[8][8] = {
-	{kColorQueen, 3, 0, 1, 3, 0, 0, 0}, {kColorQueen, 3, 0, 4, 1, 0, 0, 0},
-	{kColorQueen, 3, 0, 3, 2, 0, 0, 0}, {kColorQueen, 3, 0, 2, 4, 0, 0, 0},
-	{kColorQueen, 4, 1, 4, 8, 5, 1, 0}, {kColorQueen, 4, 1, 5, 7, 3, 1, 0},
-	{kColorQueen, 4, 2, 6, 8, 4, 2, 0}, {kColorQueen, 4, 2, 3, 7, 6, 2, 0}
+	{kColorQueenBody, 3, 0, 1, 3, 0, 0, 0}, {kColorQueenBody, 3, 0, 4, 1, 0, 0, 0},
+	{kColorQueenBody, 3, 0, 3, 2, 0, 0, 0}, {kColorQueenBody, 3, 0, 2, 4, 0, 0, 0},
+	{kColorQueenBody, 4, 1, 4, 8, 5, 1, 0}, {kColorQueenBody, 4, 1, 5, 7, 3, 1, 0},
+	{kColorQueenBody, 4, 2, 6, 8, 4, 2, 0}, {kColorQueenBody, 4, 2, 3, 7, 6, 2, 0}
 };
 // Queen wings
 static const int kQLWingPts[4][3] = {
@@ -925,13 +925,13 @@ static const int kDLEyePts[3][3] = {
 	{60, 0, 150}, {60, 50, 130}, {60, 25, 150}
 };
 static const int kDLEyeSurf[2][8] = {
-	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
+	{kColorDroneEye, 3, 0, 1, 2, 0, 0, 0}, {kColorDroneEye, 3, 0, 2, 1, 0, 0, 0}
 };
 static const int kDREyePts[3][3] = {
 	{60, 0, 150}, {60, -50, 130}, {60, -25, 150}
 };
 static const int kDREyeSurf[2][8] = {
-	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
+	{kColorDroneEye, 3, 0, 1, 2, 0, 0, 0}, {kColorDroneEye, 3, 0, 2, 1, 0, 0, 0}
 };
 
 static const Colony::ColonyEngine::PrismPartDef kDAbdomenDef = {6, kDAbdomenPts, 8, kDAbdomenSurf};
@@ -958,6 +958,19 @@ static const int kSnoopHeadSurf[3][8] = {
 static const Colony::ColonyEngine::PrismPartDef kSnoopAbdomenDef = {4, kSnoopAbdomenPts, 2, kSnoopAbdomenSurf};
 static const Colony::ColonyEngine::PrismPartDef kSnoopHeadDef = {4, kSnoopHeadPts, 3, kSnoopHeadSurf};
 
+static int wrapAngle256(int angle) {
+	angle %= 256;
+	return (angle < 0) ? (angle + 256) : angle;
+}
+
+static void rotatePoint(int angle, const int src[3], int dst[3], const int *cost, const int *sint) {
+	const long tcos = cost[angle];
+	const long tsin = sint[angle];
+	dst[0] = (int)(((long)src[0] * tcos - (long)src[1] * tsin) >> 7);
+	dst[1] = (int)(((long)src[0] * tsin + (long)src[1] * tcos) >> 7);
+	dst[2] = src[2];
+}
+
 static void resetObjectBounds(const Common::Rect &screenR, Locate &loc) {
 	loc.xmn = screenR.right;
 	loc.xmx = screenR.left;
@@ -980,13 +993,32 @@ void ColonyEngine::drawStaticObjects() {
 }
 
 bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
-	const auto drawPrism = [&](const PrismPartDef &def, bool useLook, int colorOverride = -1) {
-		draw3DPrism(obj, def, useLook, colorOverride, true);
+	const auto drawPrism = [&](const PrismPartDef &def, bool useLook, int colorOverride = -1, bool forceVisible = false) {
+		draw3DPrism(obj, def, useLook, colorOverride, true, forceVisible);
 	};
 	const auto drawSphere = [&](int pt0x, int pt0y, int pt0z, int pt1x, int pt1y, int pt1z,
 	                            uint32 fillColor, uint32 outlineColor) {
 		draw3DSphere(obj, pt0x, pt0y, pt0z, pt1x, pt1y, pt1z, fillColor, outlineColor, true);
 	};
+	const auto drawPrismFor = [&](Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride = -1, bool forceVisible = false) {
+		draw3DPrism(thing, def, useLook, colorOverride, true, forceVisible);
+	};
+	const auto drawSphereFor = [&](Thing &thing, int pt0x, int pt0y, int pt0z, int pt1x, int pt1y, int pt1z,
+	                               uint32 fillColor, uint32 outlineColor) {
+		draw3DSphere(thing, pt0x, pt0y, pt0z, pt1x, pt1y, pt1z, fillColor, outlineColor, true);
+	};
+	const auto mergeBounds = [&](const Thing &thing) {
+		if (thing.where.xmn < obj.where.xmn)
+			obj.where.xmn = thing.where.xmn;
+		if (thing.where.xmx > obj.where.xmx)
+			obj.where.xmx = thing.where.xmx;
+		if (thing.where.zmn < obj.where.zmn)
+			obj.where.zmn = thing.where.zmn;
+		if (thing.where.zmx > obj.where.zmx)
+			obj.where.zmx = thing.where.zmx;
+	};
+	const int eyeballColor = (_level == 1 || _level == 7) ? kColorPupil : kColorEyeball;
+	const int pupilColor = (_level == 1 || _level == 7) ? kColorEyeball : kColorPupil;
 
 	switch (obj.type) {
 	case kObjConsole:
@@ -1247,16 +1279,20 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	// === Robot types (1-20) ===
 	case kRobEye:
-		drawSphere(0, 0, 100, 0, 0, 200, 15, 15); // ball: white
+		if ((obj.where.xloc - _me.xloc) * (obj.where.xloc - _me.xloc) +
+		    (obj.where.yloc - _me.yloc) * (obj.where.yloc - _me.yloc) <= 64 * 64) {
+			break;
+		}
+		drawSphere(0, 0, 100, 0, 0, 200, eyeballColor, kColorBlack);
 		drawPrism(kEyeIrisDef, false);
-		drawPrism(kEyePupilDef, false);
+		drawPrism(kEyePupilDef, false, pupilColor);
 		break;
 	case kRobPyramid:
 		drawPrism(kPShadowDef, false);
 		drawPrism(kPyramidBodyDef, false);
-		drawSphere(0, 0, 175, 0, 0, 200, 15, 15); // ball on top
+		drawSphere(0, 0, 175, 0, 0, 200, eyeballColor, kColorBlack);
 		drawPrism(kPIrisDef, false);
-		drawPrism(kPPupilDef, false);
+		drawPrism(kPPupilDef, false, pupilColor);
 		break;
 	case kRobCube:
 		drawPrism(kCubeBodyDef, false);
@@ -1266,9 +1302,9 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		drawPrism(kUPyramidBodyDef, false);
 		break;
 	case kRobFEye:
-		drawSphere(0, 0, 0, 0, 0, 100, 15, 15);
+		drawSphere(0, 0, 0, 0, 0, 100, eyeballColor, kColorBlack);
 		drawPrism(kFEyeIrisDef, false);
-		drawPrism(kFEyePupilDef, false);
+		drawPrism(kFEyePupilDef, false, pupilColor);
 		break;
 	case kRobFPyramid:
 		drawPrism(kFPyramidBodyDef, false);
@@ -1280,9 +1316,9 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		drawPrism(kFUPyramidBodyDef, false);
 		break;
 	case kRobSEye:
-		drawSphere(0, 0, 0, 0, 0, 50, 15, 15);
+		drawSphere(0, 0, 0, 0, 0, 50, eyeballColor, kColorBlack);
 		drawPrism(kSEyeIrisDef, false);
-		drawPrism(kSEyePupilDef, false);
+		drawPrism(kSEyePupilDef, false, pupilColor);
 		break;
 	case kRobSPyramid:
 		drawPrism(kSPyramidBodyDef, false);
@@ -1294,9 +1330,9 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		drawPrism(kSUPyramidBodyDef, false);
 		break;
 	case kRobMEye:
-		drawSphere(0, 0, 0, 0, 0, 25, 15, 15);
-		drawPrism(kMEyeIrisDef, false);
-		drawPrism(kMEyePupilDef, false);
+		drawSphere(0, 0, 0, 0, 0, 25, eyeballColor, kColorBlack);
+		drawPrism(kMEyeIrisDef, false, kColorMiniEyeIris);
+		drawPrism(kMEyePupilDef, false, pupilColor);
 		break;
 	case kRobMPyramid:
 		drawPrism(kMPyramidBodyDef, false);
@@ -1308,22 +1344,83 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		drawPrism(kMUPyramidBodyDef, false);
 		break;
 	case kRobQueen:
-		drawPrism(kQThoraxDef, false);
-		drawPrism(kQAbdomenDef, false);
-		drawPrism(kQLWingDef, false);
-		drawPrism(kQRWingDef, false);
-		drawSphere(0, 0, 130, 0, 0, 155, 15, 15); // queen ball
-		drawPrism(kQIrisDef, false);
-		drawPrism(kQPupilDef, false);
+		{
+			const long s1 = _sint[obj.where.ang] >> 1;
+			const long c1 = _cost[obj.where.ang] >> 1;
+			const long s2 = s1 >> 1;
+			const long c2 = c1 >> 1;
+			const long eyeBaseX = obj.where.xloc + c1;
+			const long eyeBaseY = obj.where.yloc + s1;
+
+			Thing leftEye = obj;
+			leftEye.where.xloc = (int)(eyeBaseX - s2);
+			leftEye.where.yloc = (int)(eyeBaseY + c2);
+			resetObjectBounds(_screenR, leftEye.where);
+
+			Thing rightEye = obj;
+			rightEye.where.xloc = (int)(eyeBaseX + s2);
+			rightEye.where.yloc = (int)(eyeBaseY - c2);
+			resetObjectBounds(_screenR, rightEye.where);
+
+			const long leftDist = (leftEye.where.xloc - _me.xloc) * (leftEye.where.xloc - _me.xloc) +
+			                      (leftEye.where.yloc - _me.yloc) * (leftEye.where.yloc - _me.yloc);
+			const long rightDist = (rightEye.where.xloc - _me.xloc) * (rightEye.where.xloc - _me.xloc) +
+			                       (rightEye.where.yloc - _me.yloc) * (rightEye.where.yloc - _me.yloc);
+			const bool leftFirst = leftDist >= rightDist;
+			Thing &farEye = leftFirst ? leftEye : rightEye;
+			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;
+
+			drawPrism(farWing, false, wingColor, true);
+			drawSphereFor(farEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack);
+			drawPrismFor(farEye, kQIrisDef, true);
+			drawPrismFor(farEye, kQPupilDef, true, pupilColor);
+			mergeBounds(farEye);
+
+			drawPrism(kQThoraxDef, false);
+			drawPrism(kQAbdomenDef, false);
+
+			drawPrism(nearWing, false, wingColor, true);
+			drawSphereFor(nearEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack);
+			drawPrismFor(nearEye, kQIrisDef, true);
+			drawPrismFor(nearEye, kQPupilDef, true, pupilColor);
+			mergeBounds(nearEye);
+		}
 		break;
 	case kRobDrone:
-	case kRobSoldier:
 		drawPrism(kDAbdomenDef, false);
 		drawPrism(kDLLPincerDef, false);
 		drawPrism(kDRRPincerDef, false);
 		drawPrism(kDLEyeDef, false);
 		drawPrism(kDREyeDef, false);
 		break;
+	case kRobSoldier:
+		{
+			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);
+
+			for (int i = 0; i < 4; ++i) {
+				rotatePoint(leftLook, kDLLPincerPts[i], leftPincerPts[i], _cost, _sint);
+				leftPincerPts[i][0] += 120;
+				rotatePoint(rightLook, kDRRPincerPts[i], rightPincerPts[i], _cost, _sint);
+				rightPincerPts[i][0] += 120;
+			}
+
+			const PrismPartDef leftPincerDef = {4, leftPincerPts, 4, kDLPincerSurf};
+			const PrismPartDef rightPincerDef = {4, rightPincerPts, 4, kDRPincerSurf};
+
+			drawPrism(kDAbdomenDef, false, kColorSoldierBody);
+			drawPrism(leftPincerDef, false);
+			drawPrism(rightPincerDef, false);
+			drawPrism(kDLEyeDef, false, kColorSoldierEye);
+			drawPrism(kDREyeDef, false, kColorSoldierEye);
+		}
+		break;
 	case kRobSnoop:
 		drawPrism(kSnoopAbdomenDef, false);
 		drawPrism(kSnoopHeadDef, false);


Commit: 74cc722e648f22ca01e67bcd48a888a6430ab298
    https://github.com/scummvm/scummvm/commit/74cc722e648f22ca01e67bcd48a888a6430ab298
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:41+02:00

Commit Message:
COLONY: avoid enemies to multiply out of control

Changed paths:
    engines/colony/colony.h
    engines/colony/interaction.cpp
    engines/colony/map.cpp
    engines/colony/movement.cpp
    engines/colony/savegame.cpp
    engines/colony/think.cpp


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index cfe1a741715..a11a3bf23b5 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -579,7 +579,7 @@ private:
 
 	// PATCH.C: object relocation + wall state persistence
 	void resetObjectSlot(int slot, int type, int xloc, int yloc, uint8 ang);
-	void createObject(int type, int xloc, int yloc, uint8 ang);
+	bool createObject(int type, int xloc, int yloc, uint8 ang);
 	void saveLevelState();
 	void doPatch();
 	void saveWall(int x, int y, int direction);
@@ -690,6 +690,8 @@ private:
 	void droneThink(int num);
 	void snoopThink(int num);
 	void eggThink(int num);
+	int getColonyActiveRobotLimit() const;
+	void copyOverflowObjectToSlot(int num);
 	bool layEgg(int type, int xindex, int yindex);
 	void moveThink(int num);
 	void bigGrow(int num);
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 1b41120bb86..ee6511cd5a3 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -428,10 +428,11 @@ void ColonyEngine::destroyRobot(int num) {
 				} else if (_robotArray[gx][gy] == num) {
 					_robotArray[gx][gy] = 0;
 				}
-			}
-			_sound->play(Sound::kExplode);
-			debugC(1, kColonyDebugAnimation, "Robot %d destroyed!", num);
-		} else {
+				}
+				_sound->play(Sound::kExplode);
+				copyOverflowObjectToSlot(num);
+				debugC(1, kColonyDebugAnimation, "Robot %d destroyed!", num);
+			} else {
 			// Robot regresses to egg form
 			obj.where.power[1] = 10 + ((_randomSource.getRandomNumber(15)) << _level);
 			obj.grow = -1;
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index e7691453307..2f9ea66689f 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -49,12 +49,30 @@ static const int kRobotTypeOrder[] = {
 	kRobSCube
 };
 
-static void reservePlayerObjectSlot(Common::Array<Thing> &objects) {
-	if ((int)objects.size() == kMeNum - 1) {
-		Thing reserved;
-		memset(&reserved, 0, sizeof(reserved));
-		objects.push_back(reserved);
-	}
+static const int kDynamicObjectLimit = kMeNum - 1;
+static const int kReservedPlayerSlotIndex = kMeNum - 1;
+static const int kStaticObjectStartIndex = kMeNum;
+static const int kMaxObjectSlots = 255;
+
+static void clearThing(Thing &thing) {
+	memset(&thing, 0, sizeof(thing));
+}
+
+static void ensureObjectLayout(Common::Array<Thing> &objects) {
+	const int oldSize = (int)objects.size();
+	if (oldSize >= kMeNum)
+		return;
+
+	objects.resize(kMeNum);
+	for (int i = oldSize; i < kMeNum; i++)
+		clearThing(objects[i]);
+}
+
+static void resetObjectLayout(Common::Array<Thing> &objects) {
+	objects.clear();
+	objects.resize(kMeNum);
+	for (int i = 0; i < kMeNum; i++)
+		clearThing(objects[i]);
 }
 
 void ColonyEngine::loadMap(int mnum) {
@@ -91,7 +109,7 @@ void ColonyEngine::loadMap(int mnum) {
 	memset(_robotArray, 0, sizeof(_robotArray));
 	memset(_foodArray, 0, sizeof(_foodArray));
 	memset(_dirXY, 0, sizeof(_dirXY));
-	_objects.clear();
+	resetObjectLayout(_objects);
 
 	// expand logic
 	int c = 0;
@@ -105,25 +123,29 @@ void ColonyEngine::loadMap(int mnum) {
 						for (int l = 0; l < 5; l++) {
 							_mapData[i][j][k][l] = buffer[c++];
 						}
-						// PACKIT.C: center feature type 6 marks static map objects.
-						if (k == 4 && _mapData[i][j][4][0] == 6 && i < 31 && j < 31) {
-							Thing obj;
-							memset(&obj, 0, sizeof(obj));
-							obj.alive = 1;
-							obj.visible = 0;
-							obj.type = _mapData[i][j][4][1] + kBaseObject;
-							obj.where.xloc = (i << 8) + 128;
-							obj.where.yloc = (j << 8) + 128;
-							obj.where.xindex = i;
-							obj.where.yindex = j;
-							obj.where.ang = (uint8)(_mapData[i][j][4][2] + 32);
-							obj.where.look = obj.where.ang;
-							reservePlayerObjectSlot(_objects);
-							_objects.push_back(obj);
-							const int objNum = (int)_objects.size(); // 1-based, DOS-style robot slots
-							if (objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
-								_robotArray[i][j] = (uint8)objNum;
-						}
+							// PACKIT.C: center feature type 6 marks static map objects.
+							if (k == 4 && _mapData[i][j][4][0] == 6 && i < 31 && j < 31) {
+								Thing obj;
+								clearThing(obj);
+								obj.alive = 1;
+								obj.visible = 0;
+								obj.type = _mapData[i][j][4][1] + kBaseObject;
+								obj.where.xloc = (i << 8) + 128;
+								obj.where.yloc = (j << 8) + 128;
+								obj.where.xindex = i;
+								obj.where.yindex = j;
+								obj.where.ang = (uint8)(_mapData[i][j][4][2] + 32);
+								obj.where.look = obj.where.ang;
+								if ((int)_objects.size() >= kMaxObjectSlots) {
+									warning("loadMap: object table full on level %d, skipping static object type %d at (%d,%d)",
+										mnum, obj.type, i, j);
+									continue;
+								}
+								_objects.push_back(obj);
+								const int objNum = (int)_objects.size(); // 1-based, DOS-style robot slots
+								if (objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
+									_robotArray[i][j] = (uint8)objNum;
+							}
 					} else {
 						_mapData[i][j][k][0] = 0;
 					}
@@ -132,7 +154,8 @@ void ColonyEngine::loadMap(int mnum) {
 		}
 	}
 	free(buffer);
-	_dynamicObjectBase = (int)_objects.size();
+	_dynamicObjectBase = kStaticObjectStartIndex;
+	_robotNum = MAX<int>(_robotNum, (int)_objects.size() + 1);
 	_level = mnum;
 	_me.type = kMeNum;
 
@@ -148,9 +171,13 @@ void ColonyEngine::loadMap(int mnum) {
 void ColonyEngine::resetObjectSlot(int slot, int type, int xloc, int yloc, uint8 ang) {
 	if (slot < 0 || slot >= (int)_objects.size())
 		return;
+	if (slot == kReservedPlayerSlotIndex) {
+		warning("resetObjectSlot: refusing to use reserved player slot for type %d on level %d", type, _level);
+		return;
+	}
 
 	Thing &obj = _objects[slot];
-	memset(&obj, 0, sizeof(obj));
+	clearThing(obj);
 	const int lvl = MIN<int>(MAX<int>(_level - 1, 0), 5);
 	while (ang > 255)
 		ang -= 256;
@@ -196,34 +223,53 @@ void ColonyEngine::resetObjectSlot(int slot, int type, int xloc, int yloc, uint8
 		else
 			_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)objNum;
 	}
-	if (objNum > _robotNum)
-		_robotNum = objNum;
+	if (objNum >= _robotNum)
+		_robotNum = objNum + 1;
 }
 
 // PATCH.C: Create a new object in _objects and register in the proper grid.
-void ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
-	if (_dynamicObjectBase < 0 || _dynamicObjectBase > (int)_objects.size())
-		_dynamicObjectBase = (int)_objects.size();
+bool ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
+	ensureObjectLayout(_objects);
+	if (_dynamicObjectBase < kStaticObjectStartIndex || _dynamicObjectBase > (int)_objects.size())
+		_dynamicObjectBase = kStaticObjectStartIndex;
+	if (_robotNum < kMeNum + 1)
+		_robotNum = kMeNum + 1;
 
 	int slot = -1;
-	for (int j = _dynamicObjectBase; j < (int)_objects.size(); j++) {
-		if (j == kMeNum - 1)
-			continue;
-		if (!_objects[j].alive) {
-			slot = j;
-			break;
+	if (type <= kBaseObject) {
+		for (int j = 0; j < kDynamicObjectLimit; j++) {
+			if (!_objects[j].alive) {
+				slot = j;
+				break;
+			}
+		}
+		if (slot < 0) {
+			warning("createObject: no free dynamic slot for type %d on level %d", type, _level);
+			return false;
 		}
-	}
-	if (slot >= 0) {
-		resetObjectSlot(slot, type, xloc, yloc, ang);
 	} else {
-		Thing obj;
-		memset(&obj, 0, sizeof(obj));
-		reservePlayerObjectSlot(_objects);
-		_objects.push_back(obj);
-		slot = (int)_objects.size() - 1;
-		resetObjectSlot(slot, type, xloc, yloc, ang);
+		const int searchEnd = MIN<int>((int)_objects.size(), kMaxObjectSlots);
+		for (int j = _dynamicObjectBase; j < searchEnd; j++) {
+			if (!_objects[j].alive) {
+				slot = j;
+				break;
+			}
+		}
+		if (slot < 0) {
+			if ((int)_objects.size() >= kMaxObjectSlots) {
+				warning("createObject: object table full, cannot place type %d on level %d", type, _level);
+				return false;
+			}
+
+			Thing obj;
+			clearThing(obj);
+			_objects.push_back(obj);
+			slot = (int)_objects.size() - 1;
+		}
 	}
+
+	resetObjectSlot(slot, type, xloc, yloc, ang);
+	return true;
 }
 
 void ColonyEngine::saveLevelState() {
@@ -235,7 +281,8 @@ void ColonyEngine::saveLevelState() {
 	ld.queen = _allGrow ? 1 : 0;
 	memset(ld.object, 0, sizeof(ld.object));
 
-	for (uint i = 0; i < _objects.size(); i++) {
+	const int dynamicCount = MIN<int>((int)_objects.size(), kDynamicObjectLimit);
+	for (int i = 0; i < dynamicCount; i++) {
 		const Thing &obj = _objects[i];
 		if (!obj.alive || obj.type <= 0 || obj.type > kBaseObject)
 			continue;
@@ -260,11 +307,14 @@ void ColonyEngine::doPatch() {
 	// Pass 2: install objects that were moved to this level
 	for (uint i = 0; i < _patches.size(); i++) {
 		if (_level == _patches[i].to.level) {
-			createObject(
+			if (!createObject(
 				(int)_patches[i].type,
 				(int)_patches[i].to.xloc,
 				(int)_patches[i].to.yloc,
-				_patches[i].to.ang);
+				_patches[i].to.ang)) {
+				warning("doPatch: failed to restore patched object type %d on level %d",
+					(int)_patches[i].type, _level);
+			}
 		}
 	}
 }
@@ -421,7 +471,11 @@ void ColonyEngine::initRobots() {
 		// Convert grid coords to world coords (center of cell)
 		int wxloc = (xloc << 8) + 128;
 		int wyloc = (yloc << 8) + 128;
-		createObject(type, wxloc, wyloc, ang);
+		if (!createObject(type, wxloc, wyloc, ang)) {
+			warning("initRobots: failed to spawn type %d on level %d", type, _level);
+			return false;
+		}
+		return true;
 	};
 
 	if (ld.visit) {
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index a52f8e2e18c..d71723c08fd 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -1065,7 +1065,10 @@ void ColonyEngine::exitForklift() {
 	yloc = (yloc >> 8);
 	yloc = (yloc << 8) + 128;
 
-	createObject(kObjForkLift, xloc, yloc, _me.ang);
+	if (!createObject(kObjForkLift, xloc, yloc, _me.ang)) {
+		warning("exitForklift: failed to place forklift on level %d at (%d,%d)", _level, xindex, yindex);
+		return;
+	}
 
 	PassPatch to;
 	to.level = _level;
@@ -1128,7 +1131,11 @@ void ColonyEngine::dropCarriedObject() {
 	yloc = (yloc >> 8);
 	yloc = (yloc << 8) + 128;
 
-	createObject(_carryType, xloc, yloc, ang);
+	if (!createObject(_carryType, xloc, yloc, ang)) {
+		warning("dropCarriedObject: failed to place type %d on level %d at (%d,%d)",
+			_carryType, _level, xindex, yindex);
+		return;
+	}
 
 	PassPatch to;
 	to.level = _level;
diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
index b4ca2d39f88..27787fb3e41 100644
--- a/engines/colony/savegame.cpp
+++ b/engines/colony/savegame.cpp
@@ -23,7 +23,7 @@ namespace Colony {
 namespace {
 
 static const uint32 kSaveVersion = 1;
-static const uint32 kMaxSaveObjects = 255;
+static const uint32 kMaxSaveObjects = 4096;
 static const uint32 kMaxSavePatches = 100;
 
 Common::Error makeReadError(const char *msg) {
@@ -34,6 +34,11 @@ Common::Error makeWriteError(const char *msg) {
 	return Common::Error(Common::kWritingFailed, msg);
 }
 
+Common::Error makeCorruptSaveError(const char *reason) {
+	warning("Colony savegame load failed: %s", reason);
+	return makeReadError("Invalid or corrupt Colony savegame");
+}
+
 void writeRect(Common::WriteStream *stream, const Common::Rect &rect) {
 	stream->writeSint32LE(rect.left);
 	stream->writeSint32LE(rect.top);
@@ -226,6 +231,41 @@ bool validateGridReferences(const uint8 grid[32][32], uint32 objectCount, bool a
 	return true;
 }
 
+bool findInvalidGridReference(const uint8 grid[32][32], uint32 objectCount, bool allowPlayerMarker, int &invalidX, int &invalidY, uint8 &invalidValue) {
+	for (int y = 0; y < 32; y++) {
+		for (int x = 0; x < 32; x++) {
+			const uint8 value = grid[x][y];
+			if (value == 0)
+				continue;
+			if (allowPlayerMarker && value == kMeNum)
+				continue;
+			if (value > objectCount) {
+				invalidX = x;
+				invalidY = y;
+				invalidValue = value;
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+bool findInvalidActiveObjectSlot(const Common::Array<Thing> &objects, uint32 &invalidObjectNum, int &invalidType) {
+	for (uint i = 0; i < objects.size(); i++) {
+		if (!objects[i].alive)
+			continue;
+
+		const uint32 objectNum = i + 1;
+		if (objectNum == (uint32)kMeNum || objectNum > 255) {
+			invalidObjectNum = objectNum;
+			invalidType = objects[i].type;
+			return true;
+		}
+	}
+
+	return false;
+}
+
 } // namespace
 
 bool ColonyEngine::hasFeature(EngineFeature f) const {
@@ -357,8 +397,10 @@ Common::Error ColonyEngine::loadGameStream(Common::SeekableReadStream *stream) {
 		return makeReadError("Could not open savegame stream");
 
 	const uint32 version = stream->readUint32LE();
-	if (version != kSaveVersion)
+	if (version != kSaveVersion) {
+		warning("Colony savegame load failed: unsupported version %u, expected %u", version, kSaveVersion);
 		return makeReadError("Unsupported Colony savegame version");
+	}
 
 	const int savedGameMode = stream->readSint32LE();
 	const int savedLevel = stream->readSint32LE();
@@ -367,7 +409,8 @@ Common::Error ColonyEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	const uint32 savedSeed = stream->readUint32LE();
 
 	if ((savedGameMode != kModeColony && savedGameMode != kModeBattle) || savedLevel < 1 || savedLevel > 7)
-		return makeReadError("Invalid or corrupt Colony savegame");
+		return makeCorruptSaveError(Common::String::format("invalid header values: mode=%d level=%d robotNum=%d dynamicObjectBase=%d seed=%u",
+			savedGameMode, savedLevel, savedRobotNum, savedDynamicObjectBase, savedSeed).c_str());
 
 	_gameMode = savedGameMode;
 	_level = savedLevel;
@@ -404,7 +447,7 @@ Common::Error ColonyEngine::loadGameStream(Common::SeekableReadStream *stream) {
 
 	const uint32 patchCount = stream->readUint32LE();
 	if (patchCount > kMaxSavePatches)
-		return makeReadError("Invalid or corrupt Colony savegame");
+		return makeCorruptSaveError(Common::String::format("patch count %u exceeds max %u", patchCount, kMaxSavePatches).c_str());
 	_patches.resize(patchCount);
 	for (uint i = 0; i < patchCount; i++)
 		_patches[i] = readPatchEntry(stream);
@@ -434,7 +477,7 @@ Common::Error ColonyEngine::loadGameStream(Common::SeekableReadStream *stream) {
 
 	const uint32 objectCount = stream->readUint32LE();
 	if (objectCount > kMaxSaveObjects)
-		return makeReadError("Invalid or corrupt Colony savegame");
+		return makeCorruptSaveError(Common::String::format("object count %u exceeds max %u", objectCount, kMaxSaveObjects).c_str());
 	_objects.resize(objectCount);
 	for (uint i = 0; i < objectCount; i++)
 		_objects[i] = readThing(stream);
@@ -457,24 +500,46 @@ Common::Error ColonyEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	}
 
 	if (stream->err())
-		return makeReadError("Invalid or corrupt Colony savegame");
+		return makeCorruptSaveError("stream read error while decoding save payload");
 
 	if (_coreIndex < 0 || _coreIndex > 1)
-		return makeReadError("Invalid or corrupt Colony savegame");
+		return makeCorruptSaveError(Common::String::format("core index out of range: %d", _coreIndex).c_str());
 	if (_weapons < 0 || _weapons > 3 || _armor < 0 || _armor > 3)
-		return makeReadError("Invalid or corrupt Colony savegame");
+		return makeCorruptSaveError(Common::String::format("equipment out of range: weapons=%d armor=%d", _weapons, _armor).c_str());
 	if (_fl < 0 || _fl > 2 || _orbit < 0 || _orbit > 1)
-		return makeReadError("Invalid or corrupt Colony savegame");
+		return makeCorruptSaveError(Common::String::format("status out of range: fl=%d orbit=%d", _fl, _orbit).c_str());
 	for (uint i = 0; i < ARRAYSIZE(_levelData); i++) {
 		if (_levelData[i].size > 10)
-			return makeReadError("Invalid or corrupt Colony savegame");
+			return makeCorruptSaveError(Common::String::format("levelData[%u].size out of range: %u", i, _levelData[i].size).c_str());
 	}
 	if (_dynamicObjectBase < 0 || _dynamicObjectBase > (int)_objects.size())
-		return makeReadError("Invalid or corrupt Colony savegame");
+		return makeCorruptSaveError(Common::String::format("dynamicObjectBase out of range: %d (objects=%u)", _dynamicObjectBase, objectCount).c_str());
 	if (_robotNum < kMeNum + 1 || _robotNum > 255)
-		return makeReadError("Invalid or corrupt Colony savegame");
-	if (!validateGridReferences(_robotArray, objectCount, true) || !validateGridReferences(_foodArray, objectCount, false))
-		return makeReadError("Invalid or corrupt Colony savegame");
+		return makeCorruptSaveError(Common::String::format("robotNum out of range: %d", _robotNum).c_str());
+	{
+		uint32 invalidObjectNum = 0;
+		int invalidType = 0;
+		if (findInvalidActiveObjectSlot(_objects, invalidObjectNum, invalidType)) {
+			return makeCorruptSaveError(Common::String::format("active object slot %u is not addressable by byte-sized grids (type=%d)",
+				invalidObjectNum, invalidType).c_str());
+		}
+	}
+	if (!validateGridReferences(_robotArray, objectCount, true)) {
+		int invalidX = -1;
+		int invalidY = -1;
+		uint8 invalidValue = 0;
+		findInvalidGridReference(_robotArray, objectCount, true, invalidX, invalidY, invalidValue);
+		return makeCorruptSaveError(Common::String::format("robot grid reference out of range at (%d,%d): value=%u objectCount=%u",
+			invalidX, invalidY, invalidValue, objectCount).c_str());
+	}
+	if (!validateGridReferences(_foodArray, objectCount, false)) {
+		int invalidX = -1;
+		int invalidY = -1;
+		uint8 invalidValue = 0;
+		findInvalidGridReference(_foodArray, objectCount, false, invalidX, invalidY, invalidValue);
+		return makeCorruptSaveError(Common::String::format("food grid reference out of range at (%d,%d): value=%u objectCount=%u",
+			invalidX, invalidY, invalidValue, objectCount).c_str());
+	}
 
 	deleteAnimation();
 	_animationName.clear();
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index 7b5e3633155..076797ca8d2 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -42,6 +42,54 @@ static int trailTargetAngle(uint8 code) {
 	}
 }
 
+int ColonyEngine::getColonyActiveRobotLimit() const {
+	switch (_level) {
+	case 2:
+		return 25;
+	case 3:
+	case 4:
+		return 30;
+	case 5:
+	case 6:
+	case 7:
+		return 35;
+	default:
+		return 0;
+	}
+}
+
+void ColonyEngine::copyOverflowObjectToSlot(int num) {
+	const int activeLimit = getColonyActiveRobotLimit();
+	if (num <= 0 || num > activeLimit || num > kMeNum - 1 || num > (int)_objects.size())
+		return;
+
+	const int searchEnd = MIN<int>((int)_objects.size(), kMeNum - 1);
+	for (int objectNum = searchEnd; objectNum > activeLimit; --objectNum) {
+		if (objectNum == num)
+			continue;
+
+		Thing &source = _objects[objectNum - 1];
+		if (!source.alive)
+			continue;
+
+		if (source.where.xindex >= 0 && source.where.xindex < 32 &&
+		    source.where.yindex >= 0 && source.where.yindex < 32) {
+			if (isEggType(source.type)) {
+				if (_foodArray[source.where.xindex][source.where.yindex] == objectNum)
+					_foodArray[source.where.xindex][source.where.yindex] = (uint8)num;
+			} else if (_robotArray[source.where.xindex][source.where.yindex] == objectNum) {
+				_robotArray[source.where.xindex][source.where.yindex] = (uint8)num;
+			}
+		}
+
+		Thing replacement = source;
+		replacement.visible = 0;
+		_objects[num - 1] = replacement;
+		memset(&source, 0, sizeof(source));
+		return;
+	}
+}
+
 void ColonyEngine::respawnObject(int num, int type) {
 	if (num <= 0 || num > (int)_objects.size())
 		return;
@@ -75,11 +123,8 @@ void ColonyEngine::cThink() {
 	if (_gameMode != kModeColony)
 		return;
 
-	const int objectCount = (int)_objects.size();
-	for (int num = 1; num <= objectCount; ++num) {
-		if (num > (int)_objects.size())
-			break;
-
+	const int activeLimit = MIN<int>(getColonyActiveRobotLimit(), (int)_objects.size());
+	for (int num = 1; num <= activeLimit; ++num) {
 		const Thing &obj = _objects[num - 1];
 		if (!obj.alive || obj.type <= 0 || obj.type > kBaseObject)
 			continue;
@@ -264,6 +309,7 @@ void ColonyEngine::droneThink(int num) {
 			_robotArray[obj.where.xindex][obj.where.yindex] = 0;
 		obj.alive = 0;
 		_sound->play(Sound::kExplode);
+		copyOverflowObjectToSlot(num);
 	} else {
 		obj.type = kRobDrone;
 		obj.grow = 0;
@@ -307,8 +353,7 @@ bool ColonyEngine::layEgg(int type, int xindex, int yindex) {
 	    hasFood(xindex, yindex - 1))
 		return false;
 
-	createObject(type, (xindex << 8) + 128, (yindex << 8) + 128, _randomSource.getRandomNumber(255));
-	return true;
+	return createObject(type, (xindex << 8) + 128, (yindex << 8) + 128, _randomSource.getRandomNumber(255));
 }
 
 void ColonyEngine::moveThink(int num) {
@@ -768,6 +813,8 @@ void ColonyEngine::meEat() {
 	_foodArray[_me.xindex][_me.yindex] = 0;
 	obj.alive = 0;
 	_sound->play(Sound::kEat);
+	if (foodNum <= getColonyActiveRobotLimit())
+		copyOverflowObjectToSlot(foodNum);
 
 	switch (obj.type) {
 	case kRobMUPyramid:


Commit: b9e130dd2f315fba0fddc706fc8279f5ce7f2b61
    https://github.com/scummvm/scummvm/commit/b9e130dd2f315fba0fddc706fc8279f5ce7f2b61
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:42+02:00

Commit Message:
COLONY: drawMacTextPopup

Changed paths:
    engines/colony/movement.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index d71723c08fd..254865227a0 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -284,10 +284,19 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 
 	// tryPassThroughFeature returns: 0=blocked, 1=pass through, 2=teleported (position already set)
 	auto tryFeature = [&](int dir) -> int {
+		const uint8 *feature = mapFeatureAt(pobject->xindex, pobject->yindex, dir);
 		int r = tryPassThroughFeature(pobject->xindex, pobject->yindex, dir, pobject);
 		if (r == 2)
 			return 0; // teleported  position already updated by the feature
 		if (r == 1) {
+			const int rnum = occupied();
+			if (rnum) {
+				const bool showDoorText = (pobject == &_me && feature &&
+					(feature[0] == kWallFeatureDoor || feature[0] == kWallFeatureAirlock));
+				if (showDoorText)
+					doText(75, 0);
+				return rnum;
+			}
 			uint8 trailCode = 0;
 			switch (dir) {
 			case kDirNorth:
@@ -305,7 +314,18 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 			default:
 				break;
 			}
-			return moveTo(trailCode);
+			if (trailCode != 0 && pobject->type == kMeNum &&
+			    pobject->xindex >= 0 && pobject->xindex < 32 &&
+			    pobject->yindex >= 0 && pobject->yindex < 32)
+				_dirXY[pobject->xindex][pobject->yindex] = trailCode;
+			pobject->yindex = yind2;
+			pobject->xindex = xind2;
+			pobject->dx = xnew - pobject->xloc;
+			pobject->dy = ynew - pobject->yloc;
+			pobject->xloc = xnew;
+			pobject->yloc = ynew;
+			clampToWalls(pobject);
+			return 0;
 		}
 		return -2; // blocked, caller handles
 	};
@@ -533,8 +553,11 @@ int ColonyEngine::goToDestination(const uint8 *map, Locate *pobject) {
 	}
 
 	if (targetX > 0 && targetX < 31 && targetY > 0 && targetY < 31) {
-		if (targetMap == 0 && _robotArray[targetX][targetY] != 0)
+		if (targetMap == 0 && _robotArray[targetX][targetY] != 0) {
+			if (pobject == &_me)
+				doText(75, 0);
 			return 0;
+		}
 
 		const int xmod = pobject->xloc - (pobject->xindex << 8);
 		const int ymod = pobject->yloc - (pobject->yindex << 8);
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index b5a6abf9e4f..9fd5f708284 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -27,7 +27,10 @@
 #include "common/file.h"
 #include "common/events.h"
 #include "graphics/palette.h"
+#include "graphics/fontman.h"
 #include "graphics/fonts/dosfont.h"
+#include "graphics/macgui/macfontmanager.h"
+#include "graphics/macgui/macwindowborder.h"
 #include "image/pict.h"
 #include <math.h>
 
@@ -43,6 +46,122 @@ static uint32 packMacColorUI(const uint16 rgb[3]) {
 	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
 }
 
+static bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
+		int screenWidth, int screenHeight, int centerX, int centerY,
+		const Common::Array<Common::String> &lines, Graphics::TextAlign align, bool macColor) {
+	if (!gfx || lines.empty())
+		return false;
+
+	Graphics::MacFont systemFont(Graphics::kMacFontSystem, 12);
+	const Graphics::Font *font = (wm && wm->_fontMan) ? wm->_fontMan->getFont(systemFont) : nullptr;
+	if (!font)
+		font = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont);
+	if (!font)
+		font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
+	if (!font)
+		return false;
+
+	int textWidth = 0;
+	for (uint i = 0; i < lines.size(); ++i)
+		textWidth = MAX<int>(textWidth, font->getStringWidth(lines[i]));
+
+	const int fontHeight = MAX<int>(1, font->getFontHeight());
+	const int fontLeading = MAX<int>(0, font->getFontLeading());
+	const int topPad = 8;
+	const int bottomPad = 8;
+	const int sidePad = 12;
+	const int lineGap = MAX<int>(2, fontLeading);
+	const int lineStep = fontHeight + lineGap;
+	int popupWidth = CLIP<int>(textWidth + sidePad * 2, 96, MAX<int>(96, screenWidth - 16));
+	int popupHeight = topPad + bottomPad + fontHeight +
+		MAX<int>(0, (int)lines.size() - 1) * lineStep;
+	Common::Rect bounds(8, 24, screenWidth - 8, screenHeight - 8);
+	if (wm) {
+		Graphics::MacWindowBorder border;
+		border.setWindowManager(wm);
+		border.setBorderType(Graphics::kWindowWindow);
+		if (border.hasBorder(Graphics::kWindowBorderActive) && border.hasOffsets()) {
+			const Graphics::BorderOffsets &offsets = border.getOffset();
+			popupWidth = MAX<int>(border.getMinWidth(Graphics::kWindowBorderActive),
+				textWidth + sidePad * 2 + offsets.left + offsets.right);
+			popupHeight = MAX<int>(border.getMinHeight(Graphics::kWindowBorderActive),
+				topPad + bottomPad + fontHeight + MAX<int>(0, (int)lines.size() - 1) * lineStep +
+				offsets.top + offsets.bottom);
+
+			Common::Rect r(centerX - popupWidth / 2, centerY - popupHeight / 2,
+				centerX - popupWidth / 2 + popupWidth, centerY - popupHeight / 2 + popupHeight);
+			if (r.left < bounds.left)
+				r.translate(bounds.left - r.left, 0);
+			if (r.right > bounds.right)
+				r.translate(bounds.right - r.right, 0);
+			if (r.top < bounds.top)
+				r.translate(0, bounds.top - r.top);
+			if (r.bottom > bounds.bottom)
+				r.translate(0, bounds.bottom - r.bottom);
+
+			Graphics::ManagedSurface popup;
+			popup.create(popupWidth, popupHeight, wm->_pixelformat);
+			popup.fillRect(Common::Rect(0, 0, popupWidth, popupHeight), popup.format.ARGBToColor(0, 0, 0, 0));
+			Common::Rect inner(offsets.left, offsets.top, popupWidth - offsets.right, popupHeight - offsets.bottom);
+			inner.clip(Common::Rect(0, 0, popupWidth, popupHeight));
+			if (!inner.isEmpty())
+				popup.fillRect(inner, wm->_colorWhite);
+			border.blitBorderInto(popup, Graphics::kWindowBorderActive);
+
+			const int textX = inner.left + sidePad;
+			const int textY = inner.top + topPad;
+			const int textW = MAX<int>(1, inner.width() - sidePad * 2);
+			for (uint i = 0; i < lines.size(); ++i)
+				font->drawString(&popup, lines[i], textX, textY + (int)i * lineStep, textW, wm->_colorBlack, align);
+
+			gfx->drawSurface(&popup.rawSurface(), r.left, r.top);
+			gfx->copyToScreen();
+			popup.free();
+			return true;
+		}
+	}
+
+	Common::Rect r(centerX - popupWidth / 2, centerY - popupHeight / 2,
+		centerX - popupWidth / 2 + popupWidth, centerY - popupHeight / 2 + popupHeight);
+	if (r.left < bounds.left)
+		r.translate(bounds.left - r.left, 0);
+	if (r.right > bounds.right)
+		r.translate(bounds.right - r.right, 0);
+	if (r.top < bounds.top)
+		r.translate(0, bounds.top - r.top);
+	if (r.bottom > bounds.bottom)
+		r.translate(0, bounds.bottom - r.bottom);
+
+	const uint32 colBlack = macColor ? packRGB(0, 0, 0) : 0;
+	const uint32 colWhite = macColor ? packRGB(255, 255, 255) : 15;
+	const uint32 colShadow = macColor ? packRGB(96, 96, 96) : 0;
+
+	Common::Rect shadow = r;
+	shadow.translate(2, 2);
+	gfx->fillRect(shadow, colShadow);
+	gfx->fillRect(r, colWhite);
+	gfx->drawRect(r, colBlack);
+	Common::Rect inner = r;
+	inner.grow(-2);
+	if (!inner.isEmpty())
+		gfx->drawRect(inner, colBlack);
+
+	const int textLeft = r.left + sidePad;
+	const int textRight = r.right - sidePad;
+	const int textCenter = (textLeft + textRight) / 2;
+	const int startY = r.top + topPad;
+	for (uint i = 0; i < lines.size(); ++i) {
+		const int y = startY + (int)i * lineStep;
+		if (align == Graphics::kTextAlignCenter)
+			gfx->drawString(font, lines[i], textCenter, y, colBlack, Graphics::kTextAlignCenter);
+		else
+			gfx->drawString(font, lines[i], textLeft, y, colBlack, Graphics::kTextAlignLeft);
+	}
+
+	gfx->copyToScreen();
+	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.
 // Caller owns the returned surface. Returns nullptr on failure.
@@ -611,14 +730,23 @@ void ColonyEngine::printMessage(const char *text[], bool hold) {
 	int numLines = 0;
 	int width = 0;
 	Graphics::DosFont font;
+	Common::Array<Common::String> lines;
 
 	while (text[numLines] != nullptr) {
+		lines.push_back(text[numLines]);
 		int w = font.getStringWidth(text[numLines]);
 		if (w > width)
 			width = w;
 		numLines++;
 	}
 
+	if (_renderMode == Common::kRenderMacintosh && drawMacTextPopup(_wm, _gfx,
+			_width, _height, _centerX, _centerY, lines, Graphics::kTextAlignCenter, _hasMacColors)) {
+		if (hold)
+			waitForInput();
+		return;
+	}
+
 	int pxPerInchX = 72;
 	int pxPerInchY = 72;
 
@@ -719,6 +847,7 @@ void ColonyEngine::doText(int entry, int center) {
 			width = w;
 	}
 	const char *kpress = "-Press Any Key to Continue-";
+	const char *kmore = "-More-";
 	int kw = font.getStringWidth(kpress);
 	if (kw > width)
 		width = kw;
@@ -729,6 +858,19 @@ void ColonyEngine::doText(int entry, int center) {
 	if (maxlines > (int)lineArray.size())
 		maxlines = lineArray.size();
 
+	if (_renderMode == Common::kRenderMacintosh) {
+		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)) {
+			waitForInput();
+			free(page);
+			return;
+		}
+	}
+
 	Common::Rect r;
 	r.top = _centerY - (((maxlines + 1) * lineheight / 2) + 4);
 	r.bottom = _centerY + (((maxlines + 1) * lineheight / 2) + 4);
@@ -753,7 +895,7 @@ void ColonyEngine::doText(int entry, int center) {
 		}
 	}
 
-	_gfx->drawString(&font, (int)lineArray.size() > maxlines ? "-More-" : kpress, (r.left + r.right) / 2, r.top + 6 + maxlines * lineheight, 0, Graphics::kTextAlignCenter);
+	_gfx->drawString(&font, (int)lineArray.size() > maxlines ? kmore : kpress, (r.left + r.right) / 2, r.top + 6 + maxlines * lineheight, 0, Graphics::kTextAlignCenter);
 	_gfx->copyToScreen();
 
 	// Wait for key


Commit: b1d4c6bca7731823452d76bfa975fd655df3ec3b
    https://github.com/scummvm/scummvm/commit/b1d4c6bca7731823452d76bfa975fd655df3ec3b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:42+02:00

Commit Message:
COLONY: restart game popup fixes

Changed paths:
    engines/colony/colony.h
    engines/colony/intro.cpp
    graphics/macgui/macdialog.cpp


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index a11a3bf23b5..ccef772068e 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -398,6 +398,7 @@ public:
 	void inform(const char *text, bool hold);
 	void printMessage(const char *text[], bool hold);
 	void makeMessageRect(Common::Rect &r);
+	int runMacEndgameDialog(const Common::String &message);
 
 private:
 	const ADGameDescription *_gameDescription;
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 41710ded8ef..9ef8e3cc627 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -29,12 +29,129 @@
 #include "graphics/fonts/dosfont.h"
 #include "graphics/fonts/macfont.h"
 #include "graphics/cursorman.h"
+#include "graphics/macgui/macdialog.h"
+#include "graphics/macgui/mactext.h"
 #include "gui/message.h"
 #include "image/pict.h"
 #include <math.h>
 
 namespace Colony {
 
+class ColonyMacDialog : public Graphics::MacDialog {
+public:
+	using Graphics::MacDialog::MacDialog;
+
+	int runWithRenderer(Renderer *gfx) {
+		if (!_screen || !gfx)
+			return Graphics::kMacDialogQuitRequested;
+
+		bool shouldQuitEngine = false;
+		bool shouldQuit = false;
+
+		_tempSurface->copyRectToSurface(_screen->getBasePtr(_bbox.left, _bbox.top), _screen->pitch,
+			0, 0, _bbox.width() + 1, _bbox.height() + 1);
+		_wm->pushCursor(Graphics::kMacCursorArrow, nullptr);
+		g_system->showMouse(true);
+		CursorMan.showMouse(true);
+
+		while (!shouldQuit) {
+			Common::Event event;
+
+			while (g_system->getEventManager()->pollEvent(event)) {
+				if (processEvent(event))
+					continue;
+
+				switch (event.type) {
+				case Common::EVENT_QUIT:
+					shouldQuitEngine = true;
+					shouldQuit = true;
+					break;
+				case Common::EVENT_MOUSEMOVE:
+					mouseMove(event.mouse.x, event.mouse.y);
+					break;
+				case Common::EVENT_LBUTTONDOWN:
+					mouseClick(event.mouse.x, event.mouse.y);
+					break;
+				case Common::EVENT_LBUTTONUP:
+					shouldQuit = mouseRaise(event.mouse.x, event.mouse.y);
+					break;
+				case Common::EVENT_KEYDOWN:
+					if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
+						_pressedButton = -1;
+						shouldQuit = true;
+					}
+					break;
+				default:
+					break;
+				}
+			}
+
+			if (_needsRedraw) {
+				paint();
+				gfx->drawSurface(&_screen->rawSurface(), 0, 0);
+				gfx->copyToScreen();
+			}
+
+			g_system->updateScreen();
+			g_system->delayMillis(10);
+		}
+
+		_screen->copyRectToSurface(_tempSurface->getBasePtr(0, 0), _tempSurface->pitch,
+			_bbox.left, _bbox.top, _bbox.width() + 1, _bbox.height() + 1);
+		gfx->drawSurface(&_screen->rawSurface(), 0, 0);
+		gfx->copyToScreen();
+		_wm->popCursor();
+
+		if (shouldQuitEngine)
+			return Graphics::kMacDialogQuitRequested;
+
+		return _pressedButton;
+	}
+};
+
+int ColonyEngine::runMacEndgameDialog(const Common::String &message) {
+	if (_renderMode != Common::kRenderMacintosh || !_wm || !_menuSurface || !_gfx)
+		return Graphics::kMacDialogQuitRequested;
+
+	if (_macMenu && _wm->isMenuActive())
+		_macMenu->closeMenu();
+
+	const uint32 black = _menuSurface->format.ARGBToColor(255, 0, 0, 0);
+	_menuSurface->fillRect(Common::Rect(0, 0, _menuSurface->w, _menuSurface->h), black);
+	_gfx->drawSurface(&_menuSurface->rawSurface(), 0, 0);
+	_gfx->copyToScreen();
+
+	const Common::String newGameLabel = _("New Game");
+	const Common::String loadGameLabel = _("Load Game");
+	const Common::String quitLabel = _("Quit");
+	Graphics::MacFont systemFont(Graphics::kMacFontSystem, 12);
+	const Graphics::Font *dialogFont = (_wm->_fontMan) ? _wm->_fontMan->getFont(systemFont) : nullptr;
+
+	const int buttonGap = 12;
+	const int buttonH = 28;
+	const int buttonPad = 26;
+	const int minButtonW = 68;
+	const int buttonW1 = MAX<int>(minButtonW, dialogFont ? dialogFont->getStringWidth(newGameLabel) + buttonPad : 80);
+	const int buttonW2 = MAX<int>(minButtonW, dialogFont ? dialogFont->getStringWidth(loadGameLabel) + buttonPad : 80);
+	const int buttonW3 = MAX<int>(minButtonW, dialogFont ? dialogFont->getStringWidth(quitLabel) + buttonPad : 80);
+	const int totalButtonsW = buttonW1 + buttonW2 + buttonW3 + buttonGap * 2;
+	const int maxTextWidth = CLIP<int>(_width - 48, 180, 280);
+
+	Graphics::MacText prompt(Common::U32String(message), _wm, &systemFont,
+		_wm->_colorBlack, _wm->_colorWhite, maxTextWidth, Graphics::kTextAlignCenter);
+
+	const int dialogW = MAX<int>(MAX<int>(220, totalButtonsW + 20), maxTextWidth + 20);
+	const int buttonY = prompt.getTextHeight() + 30;
+	const int startX = (dialogW - totalButtonsW) / 2;
+	Graphics::MacDialogButtonArray buttons;
+	buttons.push_back(new Graphics::MacDialogButton(newGameLabel.c_str(), startX, buttonY, buttonW1, buttonH));
+	buttons.push_back(new Graphics::MacDialogButton(loadGameLabel.c_str(), startX + buttonW1 + buttonGap, buttonY, buttonW2, buttonH));
+	buttons.push_back(new Graphics::MacDialogButton(quitLabel.c_str(), startX + buttonW1 + buttonGap + buttonW2 + buttonGap, buttonY, buttonW3, buttonH));
+
+	ColonyMacDialog dialog(_menuSurface, _wm, dialogW, &prompt, maxTextWidth, &buttons, 0);
+	return dialog.runWithRenderer(_gfx);
+}
+
 void ColonyEngine::playIntro() {
 	if (getPlatform() == Common::kPlatformMacintosh) {
 		// Load the Mac "Commando" font (FOND 190, 12pt) from Colony resources.
@@ -740,6 +857,31 @@ void ColonyEngine::terminateGame(bool blowup) {
 	_clip = savedClip;
 	_centerX = savedCenterX;
 	_centerY = savedCenterY;
+
+	if (_renderMode == Common::kRenderMacintosh) {
+		while (!shouldQuit()) {
+			switch (runMacEndgameDialog(_("You have been terminated."))) {
+			case 0:
+				startNewGame();
+				_mouseLocked = savedMouseLocked;
+				updateMouseCapture(true);
+				return;
+			case 1:
+				if (loadGameDialog()) {
+					_mouseLocked = savedMouseLocked;
+					updateMouseCapture(true);
+					return;
+				}
+				break;
+			case Graphics::kMacDialogQuitRequested:
+			default:
+				quitGame();
+				return;
+			}
+		}
+		return;
+	}
+
 	while (!shouldQuit()) {
 		Common::U32StringArray altButtons;
 		altButtons.push_back(_("Load Game"));
diff --git a/graphics/macgui/macdialog.cpp b/graphics/macgui/macdialog.cpp
index 51d6df1e1ba..9ab5b28bed2 100644
--- a/graphics/macgui/macdialog.cpp
+++ b/graphics/macgui/macdialog.cpp
@@ -83,7 +83,7 @@ MacDialog::MacDialog(ManagedSurface *screen, MacWindowManager *wm, int width, Ma
 	_font = getDialogFont();
 
 	_tempSurface = new ManagedSurface();
-	_tempSurface->create(width + 1, height + 1, Graphics::PixelFormat::createFormatCLUT8());
+	_tempSurface->create(width + 1, height + 1, _screen ? _screen->format : _wm->_pixelformat);
 
 	_bbox.left = (_screen->w - width) / 2;
 	_bbox.top = (_screen->h - height) / 2;
@@ -119,7 +119,7 @@ void MacDialog::paint() {
 	Primitives &primitives = _wm->getDrawPrimitives();
 
 	MacPlotData pd(_screen, nullptr, &_wm->getPatterns(), 1, 0, 0, 1, _wm->_colorBlack, false);
-	primitives.drawFilledRect1(_bbox, kColorWhite, &pd);
+	primitives.drawFilledRect1(_bbox, _wm->_colorWhite, &pd);
 	_mactext->drawToPoint(_screen, Common::Point(_bbox.left + (_bbox.width() - _maxTextWidth)/2, _bbox.top + 16));
 	static int boxOutline[] = {1, 0, 0, 1, 1};
 	drawOutline(_bbox, boxOutline, ARRAYSIZE(boxOutline));
@@ -134,15 +134,15 @@ void MacDialog::paint() {
 			buttonOutline[0] = buttonOutline[1] = 0;
 		}
 
-		int color = kColorBlack;
+		uint32 color = _wm->_colorBlack;
 
 		if ((int)i == _pressedButton && _mouseOverPressedButton) {
 			Common::Rect bb(button->bounds.left + 5, button->bounds.top + 5,
 							button->bounds.right - 5, button->bounds.bottom - 5);
 
-			primitives.drawFilledRect1(bb, kColorBlack, &pd);
+			primitives.drawFilledRect1(bb, _wm->_colorBlack, &pd);
 
-			color = kColorWhite;
+			color = _wm->_colorWhite;
 		}
 		int w = _font->getStringWidth(button->text);
 		int x = button->bounds.left + (button->bounds.width() - w) / 2;
@@ -169,7 +169,7 @@ void MacDialog::drawOutline(Common::Rect &bounds, int *spec, int speclen) {
 	for (int i = 0; i < speclen; i++)
 		if (spec[i] != 0) {
 			Common::Rect r(bounds.left + i, bounds.top + i, bounds.right - i, bounds.bottom - i);
-			primitives.drawRect1(r, kColorBlack, &pd);
+			primitives.drawRect1(r, _wm->_colorBlack, &pd);
 		}
 }
 


Commit: b1a8eb66eaa8a975f542097c1dcc2f4fe5e48b38
    https://github.com/scummvm/scummvm/commit/b1a8eb66eaa8a975f542097c1dcc2f4fe5e48b38
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:42+02:00

Commit Message:
GRAPHICS: MACGUI: allow to query cursor data

Changed paths:
    graphics/macgui/macwindowmanager.cpp
    graphics/macgui/macwindowmanager.h


diff --git a/graphics/macgui/macwindowmanager.cpp b/graphics/macgui/macwindowmanager.cpp
index 1e6567bb9a7..a6ef4492d13 100644
--- a/graphics/macgui/macwindowmanager.cpp
+++ b/graphics/macgui/macwindowmanager.cpp
@@ -1382,6 +1382,52 @@ void MacWindowManager::replaceCursor(MacCursorType type, Cursor *cursor) {
 	replaceCursorType(type);
 }
 
+bool MacWindowManager::getBuiltInCursorData(MacCursorType type, const byte *&data, const byte *&paletteData,
+		const byte *&mask, int &w, int &h, int &hotspotX, int &hotspotY, int &transColor) {
+	data = nullptr;
+	paletteData = cursorPalette;
+	mask = nullptr;
+	w = 11;
+	h = 16;
+	hotspotX = 0;
+	hotspotY = 0;
+	transColor = 3;
+
+	switch (type) {
+	case kMacCursorArrow:
+		data = macCursorArrow;
+		hotspotX = 1;
+		hotspotY = 1;
+		return true;
+	case kMacCursorBeam:
+		data = macCursorBeam;
+		mask = macCursorBeamMask;
+		hotspotX = 3;
+		hotspotY = 8;
+		return true;
+	case kMacCursorCrossHair:
+		data = macCursorCrossHair;
+		hotspotX = 5;
+		hotspotY = 5;
+		return true;
+	case kMacCursorCrossBar:
+		data = macCursorCrossBar;
+		hotspotX = 4;
+		hotspotY = 4;
+		return true;
+	case kMacCursorWatch:
+		data = macCursorWatch;
+		hotspotX = 5;
+		hotspotY = 8;
+		return true;
+	case kMacCursorCustom:
+	case kMacCursorOff:
+		return false;
+	}
+
+	return false;
+}
+
 void MacWindowManager::pushCustomCursor(const byte *data, int w, int h, int hx, int hy, int transcolor) {
 	CursorMan.pushCursor(data, w, h, hx, hy, transcolor);
 	CursorMan.pushCursorPalette(cursorPalette, 0, 2);
diff --git a/graphics/macgui/macwindowmanager.h b/graphics/macgui/macwindowmanager.h
index 323fb006796..13c1f6cff72 100644
--- a/graphics/macgui/macwindowmanager.h
+++ b/graphics/macgui/macwindowmanager.h
@@ -333,6 +333,8 @@ private:
 
 public:
 	MacCursorType getCursorType() const;
+	static bool getBuiltInCursorData(MacCursorType type, const byte *&data, const byte *&palette,
+		const byte *&mask, int &w, int &h, int &hotspotX, int &hotspotY, int &transColor);
 
 	void pushCursor(MacCursorType type, Cursor *cursor = nullptr);
 	void replaceCursor(MacCursorType type, Cursor *cursor = nullptr);


Commit: 0053cc5d0f73296f166472b0b46ece9e051b830f
    https://github.com/scummvm/scummvm/commit/0053cc5d0f73296f166472b0b46ece9e051b830f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:43+02:00

Commit Message:
COLONY: mouse pointer fixes

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


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 48f16d7fce6..33f7be6dc52 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -30,6 +30,40 @@
 
 namespace Colony {
 
+static void responsiveAnimationDelay(OSystem *system, uint32 delayMs) {
+	if (!system || delayMs == 0)
+		return;
+
+	Common::Array<Common::Event> deferredEvents;
+	uint mouseMoveCount = 0;
+	const uint32 start = system->getMillis();
+	uint32 elapsed = 0;
+	while (elapsed < delayMs) {
+		Common::Event event;
+		while (system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_MOUSEMOVE) {
+				mouseMoveCount++;
+			} else {
+				deferredEvents.push_back(event);
+			}
+		}
+
+		system->updateScreen();
+		const uint32 slice = MIN<uint32>(2, delayMs - elapsed);
+		system->delayMillis(slice);
+		elapsed = system->getMillis() - start;
+	}
+
+	for (uint i = 0; i < deferredEvents.size(); ++i)
+		system->getEventManager()->pushEvent(deferredEvents[i]);
+
+	if (mouseMoveCount || !deferredEvents.empty()) {
+		debugC(5, kColonyDebugAnimation,
+			"responsiveAnimationDelay(%u): mouseMoves=%u deferred=%u",
+			delayMs, mouseMoveCount, (uint)deferredEvents.size());
+	}
+}
+
 static bool isBackdoorCode111111(const uint8 display[6]) {
 	for (int i = 0; i < 6; i++) {
 		if (display[i] != 3)
@@ -352,9 +386,18 @@ void ColonyEngine::playAnimation() {
 	_system->lockMouse(false);
 	_system->showMouse(true);
 	_system->warpMouse(_centerX, _centerY);
-	CursorMan.setDefaultArrowCursor(true);
+	const char *cursorName = "default arrow cursor";
+	if (_renderMode == Common::kRenderMacintosh && _macArrowCursor) {
+		cursorName = "Mac arrow cursor";
+		CursorMan.replaceCursor(_macArrowCursor);
+	} else {
+		CursorMan.setDefaultArrowCursor(true);
+	}
 	CursorMan.showMouse(true);
 	_system->updateScreen();
+	warning(
+		"Colony animation cursor: %s uses %s at center=(%d,%d)",
+		_animationName.c_str(), cursorName, _centerX, _centerY);
 
 	if (_animationName == "reactor" || _animationName == "security") {
 		for (int i = 0; i < 6; i++) {
@@ -482,10 +525,6 @@ void ColonyEngine::playAnimation() {
 	}
 
 	while (_animationRunning && !shouldQuit()) {
-		updateAnimation();
-		drawAnimation();
-		_gfx->copyToScreen();
-
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
 			if (event.type == Common::EVENT_LBUTTONDOWN) {
@@ -517,7 +556,11 @@ void ColonyEngine::playAnimation() {
 				}
 			}
 		}
-		_system->delayMillis(20);
+
+		updateAnimation();
+		drawAnimation();
+		_gfx->copyToScreen();
+		responsiveAnimationDelay(_system, 4);
 	}
 
 	_system->lockMouse(true);
@@ -969,7 +1012,7 @@ void ColonyEngine::handleVanityClick(int item) {
 				setObjectState(12, i);
 				drawAnimation();
 				_gfx->copyToScreen();
-				_system->delayMillis(50);
+				responsiveAnimationDelay(_system, 50);
 			}
 			_doorOpen = true;
 		}
@@ -1103,14 +1146,14 @@ void ColonyEngine::handleSuitClick(int item) {
 				setObjectState(3, i / 2 + 1);
 				drawAnimation();
 				_gfx->copyToScreen();
-				_system->delayMillis(30);
+				responsiveAnimationDelay(_system, 30);
 			}
 			_armor = 0;
 		} else {
 			setObjectState(1, (_armor * 2 + 1) + 1); // intermediate/pressed
 			drawAnimation();
 			_gfx->copyToScreen();
-			_system->delayMillis(50);
+			responsiveAnimationDelay(_system, 50);
 			_armor++;
 		}
 		setObjectState(1, _armor * 2 + 1); // target state
@@ -1126,14 +1169,14 @@ void ColonyEngine::handleSuitClick(int item) {
 				setObjectState(4, i / 2 + 1);
 				drawAnimation();
 				_gfx->copyToScreen();
-				_system->delayMillis(30);
+				responsiveAnimationDelay(_system, 30);
 			}
 			_weapons = 0;
 		} else {
 			setObjectState(2, (_weapons * 2 + 1) + 1); // intermediate/pressed
 			drawAnimation();
 			_gfx->copyToScreen();
-			_system->delayMillis(50);
+			responsiveAnimationDelay(_system, 50);
 			_weapons++;
 		}
 		setObjectState(2, _weapons * 2 + 1);
@@ -1165,14 +1208,14 @@ void ColonyEngine::handleDoorClick(int item) {
 				setObjectState(2, i);
 				drawAnimation();
 				_gfx->copyToScreen();
-				_system->delayMillis(80);
+				responsiveAnimationDelay(_system, 80);
 			}
 		} else {
 			for (int i = openStart; i <= doorFrames; i++) {
 				setObjectState(2, i);
 				drawAnimation();
 				_gfx->copyToScreen();
-				_system->delayMillis(80);
+				responsiveAnimationDelay(_system, 80);
 			}
 		}
 		_doorOpen = !_doorOpen;
@@ -1220,7 +1263,7 @@ void ColonyEngine::handleAirlockClick(int item) {
 				setObjectState(doorItem, i);
 				drawAnimation();
 				_gfx->copyToScreen();
-				_system->delayMillis(80);
+				responsiveAnimationDelay(_system, 80);
 			}
 			setDoorState(_airlockX, _airlockY, _airlockDirection, 1);
 			_doorOpen = false;
@@ -1230,7 +1273,7 @@ void ColonyEngine::handleAirlockClick(int item) {
 				setObjectState(doorItem, i);
 				drawAnimation();
 				_gfx->copyToScreen();
-				_system->delayMillis(80);
+				responsiveAnimationDelay(_system, 80);
 			}
 
 			setDoorState(_airlockX, _airlockY, _airlockDirection, 0);
@@ -1275,7 +1318,7 @@ void ColonyEngine::handleElevatorClick(int item) {
 						setObjectState(4, i);
 						drawAnimation();
 						_gfx->copyToScreen();
-						_system->delayMillis(80);
+						responsiveAnimationDelay(_system, 80);
 					}
 
 					if (fl > _elevatorFloor) {
@@ -1285,7 +1328,7 @@ void ColonyEngine::handleElevatorClick(int item) {
 								setObjectState(2, i);
 							drawAnimation();
 							_gfx->copyToScreen();
-							_system->delayMillis(80);
+							responsiveAnimationDelay(_system, 80);
 						}
 					} else {
 						for (int i = _elevatorFloor; i >= fl; i--) {
@@ -1294,7 +1337,7 @@ void ColonyEngine::handleElevatorClick(int item) {
 								setObjectState(2, i);
 							drawAnimation();
 							_gfx->copyToScreen();
-							_system->delayMillis(80);
+							responsiveAnimationDelay(_system, 80);
 						}
 					}
 
@@ -1306,7 +1349,7 @@ void ColonyEngine::handleElevatorClick(int item) {
 						setObjectState(4, i);
 						drawAnimation();
 						_gfx->copyToScreen();
-						_system->delayMillis(80);
+						responsiveAnimationDelay(_system, 80);
 					}
 					setObjectState(item, 1);
 					drawAnimation();
@@ -1331,7 +1374,7 @@ void ColonyEngine::handleElevatorClick(int item) {
 					setObjectState(3, i);
 					drawAnimation();
 					_gfx->copyToScreen();
-					_system->delayMillis(80);
+					responsiveAnimationDelay(_system, 80);
 				}
 			} else {
 				for (int i = 2; i <= doorFrames; i++) {
@@ -1339,7 +1382,7 @@ void ColonyEngine::handleElevatorClick(int item) {
 					setObjectState(3, i);
 					drawAnimation();
 					_gfx->copyToScreen();
-					_system->delayMillis(80);
+					responsiveAnimationDelay(_system, 80);
 				}
 			}
 			_doorOpen = !_doorOpen;
@@ -1380,7 +1423,7 @@ void ColonyEngine::handleElevatorClick(int item) {
 					setObjectState(4, i);
 					drawAnimation();
 					_gfx->copyToScreen();
-					_system->delayMillis(80);
+					responsiveAnimationDelay(_system, 80);
 				}
 				_doorOpen = true;
 			} else {
@@ -1389,7 +1432,7 @@ void ColonyEngine::handleElevatorClick(int item) {
 					setObjectState(3, i);
 					drawAnimation();
 					_gfx->copyToScreen();
-					_system->delayMillis(80);
+					responsiveAnimationDelay(_system, 80);
 				}
 				_doorOpen = false;
 			}
@@ -1423,7 +1466,7 @@ void ColonyEngine::handleElevatorClick(int item) {
 					setObjectState(3, i);
 					drawAnimation();
 					_gfx->copyToScreen();
-					_system->delayMillis(80);
+					responsiveAnimationDelay(_system, 80);
 				}
 				_elevatorFloor = fl;
 				for (int i = 1; i <= 3; i++) {
@@ -1431,7 +1474,7 @@ void ColonyEngine::handleElevatorClick(int item) {
 					setObjectState(3, i);
 					drawAnimation();
 					_gfx->copyToScreen();
-					_system->delayMillis(80);
+					responsiveAnimationDelay(_system, 80);
 				}
 				setObjectState(item, 1);
 			}
@@ -1452,7 +1495,7 @@ void ColonyEngine::handleControlsClick(int item) {
 				setObjectState(4, i);
 				drawAnimation();
 				_gfx->copyToScreen();
-				_system->delayMillis(20);
+				responsiveAnimationDelay(_system, 20);
 			}
 			break;
 		}
@@ -1655,7 +1698,7 @@ void ColonyEngine::moveObject(int index) {
 			drawComplexSprite(linked[i], ox, oy);
 		_gfx->copyToScreen();
 
-		_system->delayMillis(20);
+		responsiveAnimationDelay(_system, 4);
 	}
 
 	// Reset frame for non-type-2
@@ -1698,7 +1741,7 @@ void ColonyEngine::dolSprite(int index) {
 						}
 						drawAnimation();
 						_gfx->copyToScreen();
-						_system->delayMillis(50);
+						responsiveAnimationDelay(_system, 50);
 					}
 				} else {
 					// Open: animate from current up to max
@@ -1716,7 +1759,7 @@ void ColonyEngine::dolSprite(int index) {
 						}
 						drawAnimation();
 						_gfx->copyToScreen();
-						_system->delayMillis(50);
+						responsiveAnimationDelay(_system, 50);
 					}
 				}
 			}
@@ -1733,14 +1776,14 @@ void ColonyEngine::dolSprite(int index) {
 						ls->current--;
 						drawAnimation();
 						_gfx->copyToScreen();
-						_system->delayMillis(50);
+						responsiveAnimationDelay(_system, 50);
 					}
 				} else {
 					while (ls->current < maxFrames - 1) {
 						ls->current++;
 						drawAnimation();
 						_gfx->copyToScreen();
-						_system->delayMillis(50);
+						responsiveAnimationDelay(_system, 50);
 					}
 				}
 			} else {
@@ -1750,14 +1793,14 @@ void ColonyEngine::dolSprite(int index) {
 						ls->current--;
 						drawAnimation();
 						_gfx->copyToScreen();
-						_system->delayMillis(50);
+						responsiveAnimationDelay(_system, 50);
 					}
 				} else if (ls->current == 1) {
 					while (ls->current < maxFrames - 1) {
 						ls->current++;
 						drawAnimation();
 						_gfx->copyToScreen();
-						_system->delayMillis(50);
+						responsiveAnimationDelay(_system, 50);
 					}
 				}
 			}
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 069307fcfeb..1399075d115 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -32,12 +32,92 @@
 #include "common/keyboard.h"
 #include "engines/util.h"
 #include "graphics/cursorman.h"
+#include "graphics/maccursor.h"
 #include "graphics/palette.h"
 #include "graphics/paletteman.h"
 #include <math.h>
 
 namespace Colony {
 
+namespace {
+
+class OwnedCursor final : public Graphics::Cursor {
+public:
+	OwnedCursor(uint16 width, uint16 height, uint16 hotspotX, uint16 hotspotY, byte keyColor,
+			const byte *palette, byte paletteStart, uint16 paletteCount, bool hasMask)
+		: _width(width), _height(height), _hotspotX(hotspotX), _hotspotY(hotspotY),
+		  _keyColor(keyColor), _paletteStart(paletteStart), _paletteCount(paletteCount) {
+		_surface.resize((uint)_width * _height);
+		if (palette && paletteCount) {
+			_palette.resize(paletteCount * 3);
+			memcpy(_palette.data(), palette, paletteCount * 3);
+		}
+		if (hasMask)
+			_mask.resize((uint)_width * _height);
+	}
+
+	uint16 getWidth() const override { return _width; }
+	uint16 getHeight() const override { return _height; }
+	uint16 getHotspotX() const override { return _hotspotX; }
+	uint16 getHotspotY() const override { return _hotspotY; }
+	byte getKeyColor() const override { return _keyColor; }
+	const byte *getSurface() const override { return _surface.data(); }
+	const byte *getMask() const override { return _mask.empty() ? nullptr : _mask.data(); }
+	const byte *getPalette() const override { return _palette.empty() ? nullptr : _palette.data(); }
+	byte getPaletteStartIndex() const override { return _paletteStart; }
+	uint16 getPaletteCount() const override { return _paletteCount; }
+
+	Common::Array<byte> _surface;
+	Common::Array<byte> _palette;
+	Common::Array<byte> _mask;
+
+private:
+	uint16 _width;
+	uint16 _height;
+	uint16 _hotspotX;
+	uint16 _hotspotY;
+	byte _keyColor;
+	byte _paletteStart;
+	uint16 _paletteCount;
+};
+
+static int getMacCursorScaleFactor(OSystem *system) {
+	if (!system)
+		return 1;
+
+	return MAX(1, (int)floorf(system->getHiDPIScreenFactor() + 0.5f));
+}
+
+static Graphics::Cursor *createScaledCursor(const byte *srcSurface, const byte *srcMask,
+		uint16 srcWidth, uint16 srcHeight, uint16 hotspotX, uint16 hotspotY, byte keyColor,
+		const byte *palette, byte paletteStart, uint16 paletteCount, int scale) {
+	scale = MAX(1, scale);
+	OwnedCursor *cursor = new OwnedCursor(srcWidth * scale, srcHeight * scale, hotspotX * scale,
+		hotspotY * scale, keyColor, palette, paletteStart, paletteCount, srcMask != nullptr);
+
+	for (uint16 y = 0; y < cursor->getHeight(); ++y) {
+		const uint16 srcY = y / scale;
+		for (uint16 x = 0; x < cursor->getWidth(); ++x) {
+			const uint16 srcX = x / scale;
+			const uint srcOffset = srcY * srcWidth + srcX;
+			const uint dstOffset = y * cursor->getWidth() + x;
+			cursor->_surface[dstOffset] = srcSurface[srcOffset];
+			if (srcMask)
+				cursor->_mask[dstOffset] = srcMask[srcOffset];
+		}
+	}
+
+	return cursor;
+}
+
+static Graphics::Cursor *cloneAndScaleCursor(const Graphics::Cursor &src, int scale) {
+	return createScaledCursor(src.getSurface(), src.getMask(), src.getWidth(), src.getHeight(),
+		src.getHotspotX(), src.getHotspotY(), src.getKeyColor(), src.getPalette(),
+		src.getPaletteStartIndex(), src.getPaletteCount(), scale);
+}
+
+} // namespace
+
 ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd), _randomSource("colony") {
 	_level = 0;
 	_robotNum = 0;
@@ -183,6 +263,8 @@ ColonyEngine::~ColonyEngine() {
 	delete _frameLimiter;
 	delete _gfx;
 	delete _sound;
+	delete _macArrowCursor;
+	delete _macCrossCursor;
 	delete _colorResMan;
 	delete _resMan;
 	delete _menuSurface;
@@ -250,6 +332,95 @@ void ColonyEngine::loadMacColors() {
 	debugC(1, kColonyDebugRender, "Loaded %d Mac colors", cnum);
 }
 
+void ColonyEngine::loadMacCursorResources() {
+	delete _macArrowCursor;
+	_macArrowCursor = nullptr;
+	delete _macCrossCursor;
+	_macCrossCursor = nullptr;
+
+	if (getPlatform() != Common::kPlatformMacintosh) {
+		warning("Colony cursor: skipped Mac cursor load (platform=%d, resMan=%p)",
+			(int)getPlatform(), (void *)_resMan);
+		return;
+	}
+
+	struct CursorSource {
+		Common::MacResManager *resMan;
+		const char *label;
+	};
+
+	const CursorSource sources[] = {
+		{ _colorResMan, "color" },
+		{ _resMan, "base" }
+	};
+	const uint32 types[] = {
+		MKTAG('C', 'U', 'R', 'S'),
+		MKTAG('c', 'r', 's', 'r')
+	};
+	const char *typeNames[] = {
+		"CURS",
+		"crsr"
+	};
+	const int cursorScale = getMacCursorScaleFactor(_system);
+	const byte *arrowData = nullptr;
+	const byte *arrowPalette = nullptr;
+	const byte *arrowMask = nullptr;
+	int arrowWidth = 0;
+	int arrowHeight = 0;
+	int arrowHotspotX = 0;
+	int arrowHotspotY = 0;
+	int arrowTransColor = 0;
+
+	if (Graphics::MacWindowManager::getBuiltInCursorData(Graphics::kMacCursorArrow, arrowData, arrowPalette,
+			arrowMask, arrowWidth, arrowHeight, arrowHotspotX, arrowHotspotY, arrowTransColor)) {
+		_macArrowCursor = createScaledCursor(arrowData, arrowMask, arrowWidth, arrowHeight,
+			arrowHotspotX, arrowHotspotY, (byte)arrowTransColor, arrowPalette, 0, 2, cursorScale);
+		warning("Colony cursor: prepared shared Mac arrow cursor at %dx scale (%ux%u)",
+			cursorScale, _macArrowCursor->getWidth(), _macArrowCursor->getHeight());
+	}
+
+	for (uint sourceIdx = 0; sourceIdx < ARRAYSIZE(sources); ++sourceIdx) {
+		Common::MacResManager *resMan = sources[sourceIdx].resMan;
+		if (!resMan || !resMan->hasResFork())
+			continue;
+
+		for (uint typeIdx = 0; typeIdx < ARRAYSIZE(types); ++typeIdx) {
+			Common::SeekableReadStream *cursorStream = resMan->getResource(types[typeIdx], 1000);
+			if (!cursorStream)
+				continue;
+
+			warning("Colony cursor: found %s 1000 in %s resource fork (stream size=%u)",
+				typeNames[typeIdx], sources[sourceIdx].label, (uint)cursorStream->size());
+
+			Graphics::MacCursor *cursor = new Graphics::MacCursor();
+			const bool forceCURSFormat = (types[typeIdx] == MKTAG('C', 'U', 'R', 'S'));
+			if (!cursor->readFromStream(*cursorStream, false, 0xff, forceCURSFormat)) {
+				warning("Colony cursor: failed to decode %s 1000 from %s resource fork",
+					typeNames[typeIdx], sources[sourceIdx].label);
+				delete cursor;
+				cursor = nullptr;
+			} else {
+				Graphics::Cursor *scaledCursor = cloneAndScaleCursor(*cursor, cursorScale);
+				delete cursor;
+				cursor = nullptr;
+				_macCrossCursor = scaledCursor;
+				warning("Colony cursor: loaded %s 1000 from %s resource fork at %dx scale (%ux%u hotspot=%u,%u)",
+					typeNames[typeIdx], sources[sourceIdx].label,
+					cursorScale,
+					_macCrossCursor->getWidth(), _macCrossCursor->getHeight(),
+					_macCrossCursor->getHotspotX(), _macCrossCursor->getHotspotY());
+			}
+
+			delete cursorStream;
+			if (_macCrossCursor) {
+				return;
+			}
+		}
+	}
+
+	warning("Colony cursor: no Mac cursor resource 1000 found in color or base resource forks");
+}
+
 void ColonyEngine::menuCommandsCallback(int action, Common::String &text, void *data) {
 	ColonyEngine *engine = (ColonyEngine *)data;
 	engine->handleMenuAction(action);
@@ -276,7 +447,35 @@ void ColonyEngine::syncMacMenuChecks() {
 void ColonyEngine::updateMouseCapture(bool recenter) {
 	_system->lockMouse(_mouseLocked);
 	_system->showMouse(!_mouseLocked);
-	CursorMan.setDefaultArrowCursor();
+
+	int cursorMode = 0;
+
+	if (!_mouseLocked && _renderMode == Common::kRenderMacintosh && _wm) {
+		if (_macCrossCursor) {
+			cursorMode = 1;
+			_wm->replaceCursor(Graphics::kMacCursorCustom, _macCrossCursor);
+		} else if (_macArrowCursor) {
+			cursorMode = 2;
+			_wm->replaceCursor(Graphics::kMacCursorCustom, _macArrowCursor);
+		} else {
+			cursorMode = 2;
+			_wm->replaceCursor(Graphics::kMacCursorArrow);
+		}
+	} else {
+		CursorMan.setDefaultArrowCursor();
+	}
+
+	if (cursorMode != _lastLoggedCursorMode) {
+		if (cursorMode == 1) {
+			warning("Colony cursor: selecting Mac custom cross cursor");
+		} else if (cursorMode == 2) {
+			warning("Colony cursor: selecting Mac arrow cursor fallback");
+		} else {
+			warning("Colony cursor: selecting default arrow cursor (locked=%d, renderMode=%d, wm=%p)",
+				_mouseLocked ? 1 : 0, (int)_renderMode, (void *)_wm);
+		}
+		_lastLoggedCursorMode = cursorMode;
+	}
 	CursorMan.showMouse(!_mouseLocked);
 
 	if (_mouseLocked && recenter) {
@@ -523,6 +722,7 @@ Common::Error ColonyEngine::run() {
 		if (!_colorResMan->open("(Color) Colony")) {
 			debugC(1, kColonyDebugRender, "Color Colony resource fork not found (optional)");
 		}
+		loadMacCursorResources();
 		_sound->init();
 	}
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index ccef772068e..651c70692c9 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -31,6 +31,7 @@
 #include "common/rendermode.h"
 #include "colony/renderer.h"
 #include "colony/sound.h"
+#include "graphics/cursor.h"
 #include "graphics/macgui/macwindowmanager.h"
 #include "graphics/macgui/macmenu.h"
 
@@ -497,6 +498,9 @@ private:
 
 	MacColor _macColors[145];
 	bool _hasMacColors;
+	Graphics::Cursor *_macCrossCursor = nullptr;
+	Graphics::Cursor *_macArrowCursor = nullptr;
+	int _lastLoggedCursorMode = -1;
 
 	// Mac menu bar (MacWindowManager overlay)
 	Graphics::MacWindowManager *_wm;
@@ -504,6 +508,7 @@ private:
 	Graphics::ManagedSurface *_menuSurface;
 	int _menuBarHeight;
 	void initMacMenus();
+	void loadMacCursorResources();
 	void handleMenuAction(int action);
 	static void menuCommandsCallback(int action, Common::String &text, void *data);
 


Commit: 5eef2e77646d24fb12bd3b11a3d55b64ee981844
    https://github.com/scummvm/scummvm/commit/5eef2e77646d24fb12bd3b11a3d55b64ee981844
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:43+02:00

Commit Message:
COLONY: implement iris wipe screen transition

Changed paths:
    engines/colony/render_objects.cpp


diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 3df0d74292b..84dbd0840bc 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -27,6 +27,95 @@ namespace Colony {
 // Must match the value in render_internal.h.
 static const int kColorCorridorWall = 1000;
 
+static uint32 packEyeOverlayMacColor(const uint16 rgb[3]) {
+	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
+}
+
+static int mapEyeOverlayColorToMacColor(int colorIdx, int level) {
+	switch (colorIdx) {
+	case kColorPupil:        return 36; // c_pupil
+	case kColorEyeball:      return 34; // c_eyeball
+	case kColorEyeIris:      return 32; // c_eye
+	case kColorMiniEyeIris:  return 33; // c_meye
+	case kColorQueenEye:     return 49; // c_equeen
+	default:                 return 6;  // c_dwall fallback
+	}
+}
+
+static uint8 mapEyeOverlayColorToDOSFill(int colorIdx, int level) {
+	switch (colorIdx) {
+	case kColorBlack:
+	case kColorPupil:
+		return 0;
+	case kColorEyeball:
+		return 15;
+	case kColorEyeIris:
+	case kColorMiniEyeIris:
+	case kColorQueenEye:
+		return 1;
+	default:
+		if (colorIdx >= 0 && colorIdx <= 15)
+			return (uint8)colorIdx;
+		if (colorIdx == kColorQueenBody && level == 7)
+			return 15;
+		return 7;
+	}
+}
+
+static bool projectCorridorPointRaw(const Common::Rect &screenR, uint8 look, int8 lookY,
+                                    const int *sint, const int *cost, int camX, int camY,
+                                    float worldX, float worldY, float worldZ,
+                                    int &screenX, int &screenY) {
+	const float dx = worldX - camX;
+	const float dy = worldY - camY;
+	const float dz = worldZ;
+
+	const float sinYaw = sint[look] / 128.0f;
+	const float cosYaw = cost[look] / 128.0f;
+	const float side = dx * sinYaw - dy * cosYaw;
+	const float forward = dx * cosYaw + dy * sinYaw;
+
+	const float pitchRad = lookY * 2.0f * (float)M_PI / 256.0f;
+	const float sinPitch = sinf(pitchRad);
+	const float cosPitch = cosf(pitchRad);
+
+	const float eyeX = side;
+	const float eyeY = dz * cosPitch + forward * sinPitch;
+	const float eyeZ = dz * sinPitch - forward * cosPitch;
+	if (eyeZ >= -1.0f)
+		return false;
+
+	const float focal = (screenR.height() * 0.5f) / tanf(75.0f * (float)M_PI / 360.0f);
+	const float centerX = screenR.left + screenR.width() * 0.5f;
+	const float centerY = screenR.top + screenR.height() * 0.5f;
+
+	screenX = (int)roundf(centerX + (eyeX * focal / -eyeZ));
+	screenY = (int)roundf(centerY - (eyeY * focal / -eyeZ));
+	return true;
+}
+
+static bool isProjectedSurfaceVisible(const int *surface, int pointCount, const int *screenX, const int *screenY) {
+	if (pointCount < 3)
+		return false;
+
+	for (int i = 0; i < pointCount; ++i) {
+		const int cur = surface[i];
+		const int next = surface[(i + 1) % pointCount];
+		const int next2 = surface[(i + 2) % pointCount];
+		const long dx = screenX[cur] - screenX[next];
+		const long dy = screenY[cur] - screenY[next];
+		const long dxp = screenX[next2] - screenX[next];
+		const long dyp = screenY[next2] - screenY[next];
+		const long cross = dx * dyp - dy * dxp;
+		if (cross < 0)
+			return true;
+		if (cross > 0)
+			return false;
+	}
+
+	return false;
+}
+
 static const int kScreenPts[8][3] = {
 	{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
 	{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
@@ -1000,13 +1089,77 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	                            uint32 fillColor, uint32 outlineColor) {
 		draw3DSphere(obj, pt0x, pt0y, pt0z, pt1x, pt1y, pt1z, fillColor, outlineColor, true);
 	};
-	const auto drawPrismFor = [&](Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride = -1, bool forceVisible = false) {
-		draw3DPrism(thing, def, useLook, colorOverride, true, forceVisible);
-	};
 	const auto drawSphereFor = [&](Thing &thing, int pt0x, int pt0y, int pt0z, int pt1x, int pt1y, int pt1z,
 	                               uint32 fillColor, uint32 outlineColor) {
 		draw3DSphere(thing, pt0x, pt0y, pt0z, pt1x, pt1y, pt1z, fillColor, outlineColor, true);
 	};
+	const auto drawPrismOvalFor = [&](Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride = -1) {
+		if (def.pointCount < 4 || def.surfaceCount < 1)
+			return;
+
+		const uint8 ang = (useLook ? thing.where.look : thing.where.ang) + 32;
+		const long rotCos = _cost[ang];
+		const long rotSin = _sint[ang];
+		int projectedX[32];
+		int projectedY[32];
+		bool projected[32];
+
+		assert(def.pointCount <= ARRAYSIZE(projectedX));
+
+		for (int i = 0; i < def.pointCount; ++i) {
+			const int ox = def.points[i][0];
+			const int oy = def.points[i][1];
+			const int oz = def.points[i][2];
+			const long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
+			const long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+			projected[i] = projectCorridorPointRaw(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+			                                       (float)(rx + thing.where.xloc), (float)(ry + thing.where.yloc), (float)(oz - 160),
+			                                       projectedX[i], projectedY[i]);
+		}
+
+		const int *surface = &def.surfaces[0][2];
+		const int pointCount = def.surfaces[0][1];
+		if (pointCount < 4)
+			return;
+		for (int i = 0; i < pointCount; ++i) {
+			const int pointIdx = surface[i];
+			if (pointIdx < 0 || pointIdx >= def.pointCount || !projected[pointIdx])
+				return;
+		}
+
+		if (!isProjectedSurfaceVisible(surface, pointCount, projectedX, projectedY))
+			return;
+
+		const int left = projectedX[3];
+		const int right = projectedX[1];
+		const int top = projectedY[2];
+		const int bottom = projectedY[0];
+		if (right <= left || bottom <= top)
+			return;
+
+		const int cx = (left + right) / 2;
+		const int cy = (top + bottom) / 2;
+		const int rx = MAX(1, (right - left) / 2);
+		const int ry = MAX(1, (bottom - top) / 2);
+		const int fillColorIdx = (colorOverride >= 0) ? colorOverride : def.surfaces[0][0];
+		uint32 fillColor;
+		if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+			const int macColorIdx = mapEyeOverlayColorToMacColor(fillColorIdx, _level);
+			const bool useForeground = (fillColorIdx == kColorPupil || fillColorIdx == kColorBlack);
+			fillColor = packEyeOverlayMacColor(useForeground ? _macColors[macColorIdx].fg : _macColors[macColorIdx].bg);
+		} else {
+			fillColor = mapEyeOverlayColorToDOSFill(fillColorIdx, _level);
+		}
+		const uint32 outlineColor = (_renderMode == Common::kRenderMacintosh) ? 0xFF000000 : (uint32)kColorBlack;
+
+		_gfx->fillEllipse(cx, cy, rx, ry, fillColor);
+		_gfx->drawEllipse(cx, cy, rx, ry, outlineColor);
+
+		thing.where.xmn = MIN(thing.where.xmn, left);
+		thing.where.xmx = MAX(thing.where.xmx, right);
+		thing.where.zmn = MIN(thing.where.zmn, top);
+		thing.where.zmx = MAX(thing.where.zmx, bottom);
+	};
 	const auto mergeBounds = [&](const Thing &thing) {
 		if (thing.where.xmn < obj.where.xmn)
 			obj.where.xmn = thing.where.xmn;
@@ -1284,15 +1437,15 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			break;
 		}
 		drawSphere(0, 0, 100, 0, 0, 200, eyeballColor, kColorBlack);
-		drawPrism(kEyeIrisDef, false);
-		drawPrism(kEyePupilDef, false, pupilColor);
+		drawPrismOvalFor(obj, kEyeIrisDef, false);
+		drawPrismOvalFor(obj, kEyePupilDef, false, pupilColor);
 		break;
 	case kRobPyramid:
 		drawPrism(kPShadowDef, false);
 		drawPrism(kPyramidBodyDef, false);
 		drawSphere(0, 0, 175, 0, 0, 200, eyeballColor, kColorBlack);
-		drawPrism(kPIrisDef, false);
-		drawPrism(kPPupilDef, false, pupilColor);
+		drawPrismOvalFor(obj, kPIrisDef, false);
+		drawPrismOvalFor(obj, kPPupilDef, false, pupilColor);
 		break;
 	case kRobCube:
 		drawPrism(kCubeBodyDef, false);
@@ -1303,8 +1456,8 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kRobFEye:
 		drawSphere(0, 0, 0, 0, 0, 100, eyeballColor, kColorBlack);
-		drawPrism(kFEyeIrisDef, false);
-		drawPrism(kFEyePupilDef, false, pupilColor);
+		drawPrismOvalFor(obj, kFEyeIrisDef, false);
+		drawPrismOvalFor(obj, kFEyePupilDef, false, pupilColor);
 		break;
 	case kRobFPyramid:
 		drawPrism(kFPyramidBodyDef, false);
@@ -1317,8 +1470,8 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kRobSEye:
 		drawSphere(0, 0, 0, 0, 0, 50, eyeballColor, kColorBlack);
-		drawPrism(kSEyeIrisDef, false);
-		drawPrism(kSEyePupilDef, false, pupilColor);
+		drawPrismOvalFor(obj, kSEyeIrisDef, false);
+		drawPrismOvalFor(obj, kSEyePupilDef, false, pupilColor);
 		break;
 	case kRobSPyramid:
 		drawPrism(kSPyramidBodyDef, false);
@@ -1331,8 +1484,8 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kRobMEye:
 		drawSphere(0, 0, 0, 0, 0, 25, eyeballColor, kColorBlack);
-		drawPrism(kMEyeIrisDef, false, kColorMiniEyeIris);
-		drawPrism(kMEyePupilDef, false, pupilColor);
+		drawPrismOvalFor(obj, kMEyeIrisDef, false, kColorMiniEyeIris);
+		drawPrismOvalFor(obj, kMEyePupilDef, false, pupilColor);
 		break;
 	case kRobMPyramid:
 		drawPrism(kMPyramidBodyDef, false);
@@ -1375,8 +1528,8 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 
 			drawPrism(farWing, false, wingColor, true);
 			drawSphereFor(farEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack);
-			drawPrismFor(farEye, kQIrisDef, true);
-			drawPrismFor(farEye, kQPupilDef, true, pupilColor);
+			drawPrismOvalFor(farEye, kQIrisDef, true);
+			drawPrismOvalFor(farEye, kQPupilDef, true, pupilColor);
 			mergeBounds(farEye);
 
 			drawPrism(kQThoraxDef, false);
@@ -1384,8 +1537,8 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 
 			drawPrism(nearWing, false, wingColor, true);
 			drawSphereFor(nearEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack);
-			drawPrismFor(nearEye, kQIrisDef, true);
-			drawPrismFor(nearEye, kQPupilDef, true, pupilColor);
+			drawPrismOvalFor(nearEye, kQIrisDef, true);
+			drawPrismOvalFor(nearEye, kQPupilDef, true, pupilColor);
 			mergeBounds(nearEye);
 		}
 		break;


Commit: 94b0fad16b59d005e6c8a050d8e886c756a215d4
    https://github.com/scummvm/scummvm/commit/94b0fad16b59d005e6c8a050d8e886c756a215d4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:43+02:00

Commit Message:
COLONY: fix PC speaker divide-by-zero and animation operator precedence

Changed paths:
    engines/colony/animation.cpp
    engines/colony/map.cpp
    engines/colony/movement.cpp
    engines/colony/render_objects.cpp
    engines/colony/think.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 33f7be6dc52..b98641d4204 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -1850,7 +1850,7 @@ void ColonyEngine::crypt(uint8 sarray[6], int i, int j, int k, int l) {
 	int res[6];
 	res[0] = ((3 * l) ^ i ^ j ^ k) % 10;
 	res[1] = ((i * 3) ^ (j * 7) ^ (k * 11) ^ (l * 13)) % 10;
-	res[2] = (3 + (l * 17) ^ (j * 19) ^ (k * 23) ^ (i * 29)) % 10;
+	res[2] = ((3 + (l * 17)) ^ (j * 19) ^ (k * 23) ^ (i * 29)) % 10;
 	res[3] = ((l * 19) ^ (j * 23) ^ (k * 29) ^ (i * 31)) % 10;
 	res[4] = ((l * 17) | (j * 19) | (k * 23) | (i * 29)) % 10;
 	res[5] = (29 + (l * 17) - (j * 19) - (k * 23) - (i * 29)) % 10;
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 2f9ea66689f..07b4c641e28 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -55,7 +55,7 @@ static const int kStaticObjectStartIndex = kMeNum;
 static const int kMaxObjectSlots = 255;
 
 static void clearThing(Thing &thing) {
-	memset(&thing, 0, sizeof(thing));
+	thing = Thing();
 }
 
 static void ensureObjectLayout(Common::Array<Thing> &objects) {
@@ -91,9 +91,8 @@ void ColonyEngine::loadMap(int mnum) {
 	}
 
 	file->readUint32BE(); // "DAVE" header
-	int16 mapDefs[10];
 	for (int i = 0; i < 10; i++) {
-		mapDefs[i] = file->readSint16BE();
+		file->readSint16BE(); // skip map defs
 	}
 
 	uint16 bLength = file->readUint16BE();
@@ -179,8 +178,6 @@ void ColonyEngine::resetObjectSlot(int slot, int type, int xloc, int yloc, uint8
 	Thing &obj = _objects[slot];
 	clearThing(obj);
 	const int lvl = MIN<int>(MAX<int>(_level - 1, 0), 5);
-	while (ang > 255)
-		ang -= 256;
 	obj.opcode = 3; // FORWARD
 	obj.alive = 1;
 	obj.visible = 0;
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 254865227a0..029492eacec 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -115,7 +115,7 @@ static void projectTunnelPoint(const Common::Rect &rect, int pnt[2], int rox, in
 
 	const int centerX = (rect.left + rect.right) >> 1;
 	const int centerY = (rect.top + rect.bottom) >> 1;
-	long p = centerX + (((long)rox) << 9) / roy;
+	long p = centerX + (((long)rox) * 512) / roy;
 
 	if (p < -32000)
 		p = -32000;
@@ -264,7 +264,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 	auto occupied = [&]() -> int {
 		return occupiedObjectAt(xind2, yind2, pobject);
 	};
-	auto moveTo = [&](uint8 trailCode = 0) -> int {
+	auto moveTo = [&](uint8 trailCode) -> int {
 		const int rnum = occupied();
 		if (rnum)
 			return rnum;
@@ -434,7 +434,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		}
 	}
 
-	return moveTo();
+	return moveTo(0);
 }
 
 bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 84dbd0840bc..b636ed804fe 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1082,7 +1082,7 @@ void ColonyEngine::drawStaticObjects() {
 }
 
 bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
-	const auto drawPrism = [&](const PrismPartDef &def, bool useLook, int colorOverride = -1, bool forceVisible = false) {
+	const auto drawPrism = [&](const PrismPartDef &def, bool useLook, int colorOverride, bool forceVisible) {
 		draw3DPrism(obj, def, useLook, colorOverride, true, forceVisible);
 	};
 	const auto drawSphere = [&](int pt0x, int pt0y, int pt0z, int pt1x, int pt1y, int pt1z,
@@ -1093,7 +1093,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	                               uint32 fillColor, uint32 outlineColor) {
 		draw3DSphere(thing, pt0x, pt0y, pt0z, pt1x, pt1y, pt1z, fillColor, outlineColor, true);
 	};
-	const auto drawPrismOvalFor = [&](Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride = -1) {
+	const auto drawPrismOvalFor = [&](Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride) {
 		if (def.pointCount < 4 || def.surfaceCount < 1)
 			return;
 
@@ -1175,64 +1175,64 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 
 	switch (obj.type) {
 	case kObjConsole:
-		drawPrism(kConsolePart, false);
+		drawPrism(kConsolePart, false, -1, false);
 		break;
 	case kObjCChair:
 		for (int i = 0; i < 5; i++) {
 			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			drawPrism(kCChairParts[i], false);
+			drawPrism(kCChairParts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjPlant:
 		// DOS MakePlant draw order: top pot, then green leaf lines, then pot on top.
-		drawPrism(kPlantParts[1], false); // top pot (soil)
+		drawPrism(kPlantParts[1], false, -1, false); // top pot (soil)
 		for (int i = 2; i < 8; i++)
 			draw3DLeaf(obj, kPlantParts[i]); // leaves as lines
-		drawPrism(kPlantParts[0], false); // pot (drawn last, on top)
+		drawPrism(kPlantParts[0], false, -1, false); // pot (drawn last, on top)
 		break;
 	case kObjCouch:
 	case kObjChair: {
 		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
 		for (int i = 0; i < 4; i++) {
 			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
-			drawPrism(parts[i], false);
+			drawPrism(parts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	}
 	case kObjTV:
 		_gfx->setDepthRange(0.002, 1.0); // body base layer (pushed back)
-		drawPrism(kTVParts[0], false);
+		drawPrism(kTVParts[0], false, -1, false);
 		_gfx->setDepthRange(0.0, 1.0);   // screen on top of body face
-		drawPrism(kTVParts[1], false);
+		drawPrism(kTVParts[1], false, -1, false);
 		break;
 	case kObjDrawer:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kDrawerParts[i], false);
+			drawPrism(kDrawerParts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjFWall:
 		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
-			drawPrism(kFWallPart, false, kColorCorridorWall);
+			drawPrism(kFWallPart, false, kColorCorridorWall, false);
 		else
-			drawPrism(kFWallPart, false);
+			drawPrism(kFWallPart, false, -1, false);
 		break;
 	case kObjCWall:
 		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
-			drawPrism(kCWallPart, false, kColorCorridorWall);
+			drawPrism(kCWallPart, false, kColorCorridorWall, false);
 		else
-			drawPrism(kCWallPart, false);
+			drawPrism(kCWallPart, false, -1, false);
 		break;
 	case kObjScreen:
-		drawPrism(kScreenPart, false);
+		drawPrism(kScreenPart, false, -1, false);
 		break;
 	case kObjTable:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kTableParts[i], false);
+			drawPrism(kTableParts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
@@ -1241,7 +1241,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
 		for (int i = 0; i < 3; i++) {
 			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
-			drawPrism(parts[i], false);
+			drawPrism(parts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
@@ -1249,28 +1249,28 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	case kObjDesk:
 		for (int i = 0; i < 10; i++) {
 			_gfx->setDepthRange((9 - i) * 0.002, 1.0);
-			drawPrism(kDeskParts[i], false);
+			drawPrism(kDeskParts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjBox1:
-		drawPrism(kBox1Part, false);
+		drawPrism(kBox1Part, false, -1, false);
 		break;
 	case kObjBench:
-		drawPrism(kBenchPart, false);
+		drawPrism(kBenchPart, false, -1, false);
 		break;
 	case kObjCBench:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kCBenchParts[i], false);
+			drawPrism(kCBenchParts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjBox2:
 		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kBox2Parts[1], false); // base first
+		drawPrism(kBox2Parts[1], false, -1, false); // base first
 		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kBox2Parts[0], false); // top second
+		drawPrism(kBox2Parts[0], false, -1, false); // top second
 		break;
 	case kObjReactor: {
 		// MakeReactor: animate core height and recolor only the original
@@ -1341,12 +1341,12 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		// Depth separation matches the original draw order closely, but the
 		// per-face colors now follow MakeReactor() exactly.
 		_gfx->setDepthRange(0.004, 1.0);
-		drawPrism(modTopDef, false);
+		drawPrism(modTopDef, false, -1, false);
 		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(modRingDef, false);
+		drawPrism(modRingDef, false, -1, false);
 		if (_coreState[_coreIndex] < 2) {
 			_gfx->setDepthRange(0.0, 1.0);
-			drawPrism(modCoreDef, false);
+			drawPrism(modCoreDef, false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
@@ -1359,76 +1359,76 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 
 		for (int i = 0; i < 4; i++) {
 			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			drawPrism(kPowerSuitParts[i], false);
+			drawPrism(kPowerSuitParts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kPowerSuitParts[4], false, sourceColor);
+		drawPrism(kPowerSuitParts[4], false, sourceColor, false);
 		break;
 	}
 	case kObjTeleport:
-		drawPrism(kTelePart, false);
+		drawPrism(kTelePart, false, -1, false);
 		break;
 	case kObjCryo:
 		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kCryoParts[1], false); // base first
+		drawPrism(kCryoParts[1], false, -1, false); // base first
 		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kCryoParts[0], false); // top second
+		drawPrism(kCryoParts[0], false, -1, false); // top second
 		break;
 	case kObjProjector:
 		// Projector sits on table  draw table first, then projector parts
 		_gfx->setDepthRange(0.008, 1.0);
-		drawPrism(kTableParts[0], false); // table base
+		drawPrism(kTableParts[0], false, -1, false); // table base
 		_gfx->setDepthRange(0.006, 1.0);
-		drawPrism(kTableParts[1], false); // table top
+		drawPrism(kTableParts[1], false, -1, false); // table top
 		_gfx->setDepthRange(0.004, 1.0);
-		drawPrism(kProjectorParts[1], false); // stand
+		drawPrism(kProjectorParts[1], false, -1, false); // stand
 		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kProjectorParts[0], false); // body
+		drawPrism(kProjectorParts[0], false, -1, false); // body
 		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kProjectorParts[2], false); // lens
+		drawPrism(kProjectorParts[2], false, -1, false); // lens
 		break;
 	case kObjTub:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kTubParts[i], false);
+			drawPrism(kTubParts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjSink:
 		for (int i = 0; i < 3; i++) {
 			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
-			drawPrism(kSinkParts[i], false);
+			drawPrism(kSinkParts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjToilet:
 		for (int i = 0; i < 4; i++) {
 			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
-			drawPrism(kToiletParts[i], false);
+			drawPrism(kToiletParts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjPToilet:
 		for (int i = 0; i < 5; i++) {
 			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			drawPrism(kPToiletParts[i], false);
+			drawPrism(kPToiletParts[i], false, -1, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjForkLift:
 		// Draw order: forks, arms, treads, cab (back-to-front)
 		_gfx->setDepthRange(0.010, 1.0);
-		drawPrism(kForkliftParts[3], false); // FLLL (left fork)
+		drawPrism(kForkliftParts[3], false, -1, false); // FLLL (left fork)
 		_gfx->setDepthRange(0.008, 1.0);
-		drawPrism(kForkliftParts[2], false); // FLUL (left arm)
+		drawPrism(kForkliftParts[2], false, -1, false); // FLUL (left arm)
 		_gfx->setDepthRange(0.006, 1.0);
-		drawPrism(kForkliftParts[5], false); // FLLR (right fork)
+		drawPrism(kForkliftParts[5], false, -1, false); // FLLR (right fork)
 		_gfx->setDepthRange(0.004, 1.0);
-		drawPrism(kForkliftParts[4], false); // FLUR (right arm)
+		drawPrism(kForkliftParts[4], false, -1, false); // FLUR (right arm)
 		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kForkliftParts[1], false); // treads
+		drawPrism(kForkliftParts[1], false, -1, false); // treads
 		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kForkliftParts[0], false); // cab
+		drawPrism(kForkliftParts[0], false, -1, false); // cab
 		break;
 	// === Robot types (1-20) ===
 	case kRobEye:
@@ -1437,50 +1437,50 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			break;
 		}
 		drawSphere(0, 0, 100, 0, 0, 200, eyeballColor, kColorBlack);
-		drawPrismOvalFor(obj, kEyeIrisDef, false);
+		drawPrismOvalFor(obj, kEyeIrisDef, false, -1);
 		drawPrismOvalFor(obj, kEyePupilDef, false, pupilColor);
 		break;
 	case kRobPyramid:
-		drawPrism(kPShadowDef, false);
-		drawPrism(kPyramidBodyDef, false);
+		drawPrism(kPShadowDef, false, -1, false);
+		drawPrism(kPyramidBodyDef, false, -1, false);
 		drawSphere(0, 0, 175, 0, 0, 200, eyeballColor, kColorBlack);
-		drawPrismOvalFor(obj, kPIrisDef, false);
+		drawPrismOvalFor(obj, kPIrisDef, false, -1);
 		drawPrismOvalFor(obj, kPPupilDef, false, pupilColor);
 		break;
 	case kRobCube:
-		drawPrism(kCubeBodyDef, false);
+		drawPrism(kCubeBodyDef, false, -1, false);
 		break;
 	case kRobUPyramid:
-		drawPrism(kUPShadowDef, false);
-		drawPrism(kUPyramidBodyDef, false);
+		drawPrism(kUPShadowDef, false, -1, false);
+		drawPrism(kUPyramidBodyDef, false, -1, false);
 		break;
 	case kRobFEye:
 		drawSphere(0, 0, 0, 0, 0, 100, eyeballColor, kColorBlack);
-		drawPrismOvalFor(obj, kFEyeIrisDef, false);
+		drawPrismOvalFor(obj, kFEyeIrisDef, false, -1);
 		drawPrismOvalFor(obj, kFEyePupilDef, false, pupilColor);
 		break;
 	case kRobFPyramid:
-		drawPrism(kFPyramidBodyDef, false);
+		drawPrism(kFPyramidBodyDef, false, -1, false);
 		break;
 	case kRobFCube:
-		drawPrism(kFCubeBodyDef, false);
+		drawPrism(kFCubeBodyDef, false, -1, false);
 		break;
 	case kRobFUPyramid:
-		drawPrism(kFUPyramidBodyDef, false);
+		drawPrism(kFUPyramidBodyDef, false, -1, false);
 		break;
 	case kRobSEye:
 		drawSphere(0, 0, 0, 0, 0, 50, eyeballColor, kColorBlack);
-		drawPrismOvalFor(obj, kSEyeIrisDef, false);
+		drawPrismOvalFor(obj, kSEyeIrisDef, false, -1);
 		drawPrismOvalFor(obj, kSEyePupilDef, false, pupilColor);
 		break;
 	case kRobSPyramid:
-		drawPrism(kSPyramidBodyDef, false);
+		drawPrism(kSPyramidBodyDef, false, -1, false);
 		break;
 	case kRobSCube:
-		drawPrism(kSCubeBodyDef, false);
+		drawPrism(kSCubeBodyDef, false, -1, false);
 		break;
 	case kRobSUPyramid:
-		drawPrism(kSUPyramidBodyDef, false);
+		drawPrism(kSUPyramidBodyDef, false, -1, false);
 		break;
 	case kRobMEye:
 		drawSphere(0, 0, 0, 0, 0, 25, eyeballColor, kColorBlack);
@@ -1488,13 +1488,13 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		drawPrismOvalFor(obj, kMEyePupilDef, false, pupilColor);
 		break;
 	case kRobMPyramid:
-		drawPrism(kMPyramidBodyDef, false);
+		drawPrism(kMPyramidBodyDef, false, -1, false);
 		break;
 	case kRobMCube:
-		drawPrism(kMCubeBodyDef, false);
+		drawPrism(kMCubeBodyDef, false, -1, false);
 		break;
 	case kRobMUPyramid:
-		drawPrism(kMUPyramidBodyDef, false);
+		drawPrism(kMUPyramidBodyDef, false, -1, false);
 		break;
 	case kRobQueen:
 		{
@@ -1528,26 +1528,26 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 
 			drawPrism(farWing, false, wingColor, true);
 			drawSphereFor(farEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack);
-			drawPrismOvalFor(farEye, kQIrisDef, true);
+			drawPrismOvalFor(farEye, kQIrisDef, true, -1);
 			drawPrismOvalFor(farEye, kQPupilDef, true, pupilColor);
 			mergeBounds(farEye);
 
-			drawPrism(kQThoraxDef, false);
-			drawPrism(kQAbdomenDef, false);
+			drawPrism(kQThoraxDef, false, -1, false);
+			drawPrism(kQAbdomenDef, false, -1, false);
 
 			drawPrism(nearWing, false, wingColor, true);
 			drawSphereFor(nearEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack);
-			drawPrismOvalFor(nearEye, kQIrisDef, true);
+			drawPrismOvalFor(nearEye, kQIrisDef, true, -1);
 			drawPrismOvalFor(nearEye, kQPupilDef, true, pupilColor);
 			mergeBounds(nearEye);
 		}
 		break;
 	case kRobDrone:
-		drawPrism(kDAbdomenDef, false);
-		drawPrism(kDLLPincerDef, false);
-		drawPrism(kDRRPincerDef, false);
-		drawPrism(kDLEyeDef, false);
-		drawPrism(kDREyeDef, false);
+		drawPrism(kDAbdomenDef, false, -1, false);
+		drawPrism(kDLLPincerDef, false, -1, false);
+		drawPrism(kDRRPincerDef, false, -1, false);
+		drawPrism(kDLEyeDef, false, -1, false);
+		drawPrism(kDREyeDef, false, -1, false);
 		break;
 	case kRobSoldier:
 		{
@@ -1567,16 +1567,16 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			const PrismPartDef leftPincerDef = {4, leftPincerPts, 4, kDLPincerSurf};
 			const PrismPartDef rightPincerDef = {4, rightPincerPts, 4, kDRPincerSurf};
 
-			drawPrism(kDAbdomenDef, false, kColorSoldierBody);
-			drawPrism(leftPincerDef, false);
-			drawPrism(rightPincerDef, false);
-			drawPrism(kDLEyeDef, false, kColorSoldierEye);
-			drawPrism(kDREyeDef, false, kColorSoldierEye);
+			drawPrism(kDAbdomenDef, false, kColorSoldierBody, false);
+			drawPrism(leftPincerDef, false, -1, false);
+			drawPrism(rightPincerDef, false, -1, false);
+			drawPrism(kDLEyeDef, false, kColorSoldierEye, false);
+			drawPrism(kDREyeDef, false, kColorSoldierEye, false);
 		}
 		break;
 	case kRobSnoop:
-		drawPrism(kSnoopAbdomenDef, false);
-		drawPrism(kSnoopHeadDef, false);
+		drawPrism(kSnoopAbdomenDef, false, -1, false);
+		drawPrism(kSnoopHeadDef, false, -1, false);
 		break;
 	default:
 		return false;
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index 076797ca8d2..abdb0a9a282 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -85,7 +85,7 @@ void ColonyEngine::copyOverflowObjectToSlot(int num) {
 		Thing replacement = source;
 		replacement.visible = 0;
 		_objects[num - 1] = replacement;
-		memset(&source, 0, sizeof(source));
+		source = Thing();
 		return;
 	}
 }


Commit: 112332e117ed8a36b1a94e28a880bef9fb84eed6
    https://github.com/scummvm/scummvm/commit/112332e117ed8a36b1a94e28a880bef9fb84eed6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:44+02:00

Commit Message:
COLONY: malloc -> new / free -> delete

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.h
    engines/colony/map.cpp
    engines/colony/sound.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index b98641d4204..1c49759f1ac 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -769,8 +769,8 @@ Image *ColonyEngine::loadImage(Common::SeekableReadStream &file) {
 		// from the stream to keep file position aligned.
 		uint32 bsize = readUint32(file);
 		size = readUint32(file);
-		im->data = (byte *)malloc(size);
-		byte *packed = (byte *)calloc(bsize + 8, 1); // +8 matches original NewPtr(bsize+8)
+		im->data = new byte[size];
+		byte *packed = new byte[bsize + 8](); // +8 matches original NewPtr(bsize+8)
 		file.read(packed, bsize);
 		// Decompress: exact match of Mac UnPackBytes(src, dst, len).
 		// Buffer is pairs of (count, value). Count is decremented in-place;
@@ -786,10 +786,10 @@ Image *ColonyEngine::loadImage(Common::SeekableReadStream &file) {
 				(*sp)--;
 			}
 		}
-		free(packed);
+		delete[] packed;
 	} else {
 		size = readUint32(file);
-		im->data = (byte *)malloc(size);
+		im->data = new byte[size];
 		file.read(im->data, size);
 	}
 	return im;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 651c70692c9..0e54d35c97d 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -324,7 +324,7 @@ struct Image {
 	byte *data;
 
 	Image() : width(0), height(0), align(0), rowBytes(0), bits(0), planes(0), data(nullptr) {}
-	~Image() { free(data); }
+	~Image() { delete[] data; }
 };
 
 struct Sprite {
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 07b4c641e28..d5478e19456 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -96,11 +96,7 @@ void ColonyEngine::loadMap(int mnum) {
 	}
 
 	uint16 bLength = file->readUint16BE();
-	uint8 *buffer = (uint8 *)malloc(bLength);
-	if (!buffer) {
-		delete file;
-		error("Out of memory loading map");
-	}
+	uint8 *buffer = new uint8[bLength];
 	file->read(buffer, bLength);
 	delete file;
 
@@ -152,7 +148,7 @@ void ColonyEngine::loadMap(int mnum) {
 			}
 		}
 	}
-	free(buffer);
+	delete[] buffer;
 	_dynamicObjectBase = kStaticObjectStartIndex;
 	_robotNum = MAX<int>(_robotNum, (int)_objects.size() + 1);
 	_level = mnum;
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index 4a7ec18dcc9..840d7d1aa0f 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -333,7 +333,7 @@ bool Sound::playResource(int resID) {
 
 	snd->seek(42);
 	uint32 dataSize = snd->size() - 42;
-	byte *data = (byte *)malloc(dataSize);
+	byte *data = new byte[dataSize];
 	snd->read(data, dataSize);
 	delete snd;
 
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 9fd5f708284..7072e6f374a 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -814,7 +814,7 @@ void ColonyEngine::doText(int entry, int center) {
 		return;
 	}
 
-	byte *page = (byte *)malloc(ch + 1);
+	byte *page = new byte[ch + 1];
 	file->seek(offset);
 	file->read(page, ch);
 	delete file;
@@ -866,7 +866,7 @@ void ColonyEngine::doText(int entry, int center) {
 		if (drawMacTextPopup(_wm, _gfx, _width, _height, _centerX, _centerY, popupLines,
 				center == 1 ? Graphics::kTextAlignCenter : Graphics::kTextAlignLeft, _hasMacColors)) {
 			waitForInput();
-			free(page);
+			delete[] page;
 			return;
 		}
 	}
@@ -901,7 +901,7 @@ void ColonyEngine::doText(int entry, int center) {
 	// Wait for key
 	waitForInput();
 
-	free(page);
+	delete[] page;
 }
 
 bool ColonyEngine::clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const {


Commit: c4741f9a60328ac219362b6d16e0b5571b2a1d1a
    https://github.com/scummvm/scummvm/commit/c4741f9a60328ac219362b6d16e0b5571b2a1d1a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:44+02:00

Commit Message:
COLONY: removed lambdas

Changed paths:
    engines/colony/battle.cpp
    engines/colony/colony.h
    engines/colony/movement.cpp
    engines/colony/render_features.cpp
    engines/colony/render_objects.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 1e16c89b8f5..9aba10a99b1 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -956,6 +956,19 @@ void ColonyEngine::battleThink() {
 	}
 }
 
+void ColonyEngine::enterColonyFromBattle(int mapNum, int xloc, int yloc) {
+	playTunnelAirlockEffect();
+	_gameMode = kModeColony;
+	_projon = false;
+	_pcount = 0;
+	_me.xloc = xloc;
+	_me.yloc = 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) {
 	xnew = battleNormalizeCoord(xnew);
 	ynew = battleNormalizeCoord(ynew);
@@ -974,25 +987,12 @@ void ColonyEngine::battleCommand(int xnew, int ynew) {
 		}
 	}
 
-	auto enterColony = [&](int mapNum, int xloc, int yloc) {
-		playTunnelAirlockEffect();
-		_gameMode = kModeColony;
-		_projon = false;
-		_pcount = 0;
-		_me.xloc = xloc;
-		_me.yloc = yloc;
-		_me.xindex = _me.xloc >> 8;
-		_me.yindex = _me.yloc >> 8;
-		loadMap(mapNum);
-		_coreIndex = (mapNum == 1) ? 0 : 1;
-	};
-
 	if (!_orbit &&
 	    xnew > _battleShip.xloc - 2 * kBattleSize &&
 	    xnew < _battleShip.xloc &&
 	    ynew > _battleShip.yloc - kBattleSize / 2 &&
 	    ynew < _battleShip.yloc + kBattleSize / 2) {
-		enterColony(1, 900, 3000);
+		enterColonyFromBattle(1, 900, 3000);
 		return;
 	}
 
@@ -1000,7 +1000,7 @@ void ColonyEngine::battleCommand(int xnew, int ynew) {
 	    xnew < _battleEnter.xloc &&
 	    ynew > _battleEnter.yloc - kBattleSize / 2 &&
 	    ynew < _battleEnter.yloc + kBattleSize / 2) {
-		enterColony(2, 384, 640);
+		enterColonyFromBattle(2, 384, 640);
 		return;
 	}
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 0e54d35c97d..1db807d063c 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -386,6 +386,10 @@ public:
 	void startNewGame();
 	void corridor();
 	void quadrant();
+	bool hasInteractiveWallFeature(int cx, int cy, int dir) const;
+	void clampToWalls(Locate *p);
+	int checkwallMoveTo(int xnew, int ynew, int xind2, int yind2, Locate *pobject, uint8 trailCode);
+	int checkwallTryFeature(int xnew, int ynew, int xind2, int yind2, Locate *pobject, int dir);
 	int checkwall(int xnew, int ynew, Locate *pobject);
 	void cCommand(int xnew, int ynew, bool allowInteraction);
 	bool scrollInfo(const Graphics::Font *macFont = nullptr);
@@ -553,6 +557,7 @@ private:
 	void draw3DLeaf(const Thing &obj, const PrismPartDef &def);
 	void draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 	                  int pt1x, int pt1y, int pt1z, uint32 fillColor, uint32 outlineColor, bool accumulateBounds = false);
+	void drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride);
 	bool drawStaticObjectPrisms3D(Thing &obj);
 	void initRobots();
 	void renderCorridor3D();
@@ -574,6 +579,7 @@ private:
 	void battleInit();
 	void battleSet();
 	void battleThink();
+	void enterColonyFromBattle(int mapNum, int xloc, int yloc);
 	void battleCommand(int xnew, int ynew);
 	void battleShoot();
 	void battleProjCommand(int xcheck, int ycheck);
@@ -606,6 +612,9 @@ private:
 	void updateViewportLayout();
 	void drawDashboardStep1();
 	void drawDashboardMac();
+	void drawMiniMapMarker(int x, int y, int halfSize, uint32 color, bool isMac);
+	bool hasRobotAt(int x, int y) const;
+	bool hasFoodAt(int x, int y) const;
 	void drawMiniMap(uint32 lineColor);
 	void drawCrosshair();
 	bool clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const;
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 029492eacec..89933ed743f 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -220,54 +220,87 @@ int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
 	return rnum;
 }
 
-int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
-	const int xind2 = xnew >> 8;
-	const int yind2 = ynew >> 8;
+bool ColonyEngine::hasInteractiveWallFeature(int cx, int cy, int dir) const {
+	if (cx < 0 || cx >= 31 || cy < 0 || cy >= 31)
+		return false;
+	uint8 type = _mapData[cx][cy][dir][0];
+	return type == kWallFeatureDoor || type == kWallFeatureAirlock ||
+		type == kWallFeatureUpStairs || type == kWallFeatureDnStairs ||
+		type == kWallFeatureTunnel || type == kWallFeatureElevator;
+}
 
-	// Clamp position to maintain minimum distance from walls within a cell.
-	// This prevents the camera from clipping through thin walls.
-	// Skip clamping for walls that have any interactive feature (door, airlock,
-	// stairs, etc.) so the player can reach the boundary and trigger the interaction.
-	// The wall itself still blocks via checkwall; clamping only prevents camera clipping
-	// on plain walls with no features.
+void ColonyEngine::clampToWalls(Locate *p) {
 	static const int kWallPad = 40;
-	auto hasFeature = [this](int cx, int cy, int dir) -> bool {
-		if (cx < 0 || cx >= 31 || cy < 0 || cy >= 31)
-			return false;
-		uint8 type = _mapData[cx][cy][dir][0];
-		return type == kWallFeatureDoor || type == kWallFeatureAirlock ||
-			type == kWallFeatureUpStairs || type == kWallFeatureDnStairs ||
-			type == kWallFeatureTunnel || type == kWallFeatureElevator;
-	};
-	auto clampToWalls = [this, &hasFeature](Locate *p) {
-		int cx = p->xindex;
-		int cy = p->yindex;
-		int cellMinX = cx << 8;
-		int cellMinY = cy << 8;
-		int cellMaxX = cellMinX + 255;
-		int cellMaxY = cellMinY + 255;
-
-		// South wall of this cell (at cellMinY boundary)
-		if ((wallAt(cx, cy) & 0x01) && !hasFeature(cx, cy, kDirSouth))
-			p->yloc = MAX(p->yloc, cellMinY + kWallPad);
-		// North wall (at cellMaxY+1 boundary = south wall of cell above)
-		if ((wallAt(cx, cy + 1) & 0x01) && !hasFeature(cx, cy, kDirNorth))
-			p->yloc = MIN(p->yloc, cellMaxY - kWallPad);
-		// West wall of this cell (at cellMinX boundary)
-		if ((wallAt(cx, cy) & 0x02) && !hasFeature(cx, cy, kDirWest))
-			p->xloc = MAX(p->xloc, cellMinX + kWallPad);
-		// East wall (at cellMaxX+1 boundary = west wall of cell to right)
-		if ((wallAt(cx + 1, cy) & 0x02) && !hasFeature(cx, cy, kDirEast))
-			p->xloc = MIN(p->xloc, cellMaxX - kWallPad);
-	};
-
-	auto occupied = [&]() -> int {
-		return occupiedObjectAt(xind2, yind2, pobject);
-	};
-	auto moveTo = [&](uint8 trailCode) -> int {
-		const int rnum = occupied();
-		if (rnum)
+	int cx = p->xindex;
+	int cy = p->yindex;
+	int cellMinX = cx << 8;
+	int cellMinY = cy << 8;
+	int cellMaxX = cellMinX + 255;
+	int cellMaxY = cellMinY + 255;
+
+	// South wall of this cell (at cellMinY boundary)
+	if ((wallAt(cx, cy) & 0x01) && !hasInteractiveWallFeature(cx, cy, kDirSouth))
+		p->yloc = MAX(p->yloc, cellMinY + kWallPad);
+	// North wall (at cellMaxY+1 boundary = south wall of cell above)
+	if ((wallAt(cx, cy + 1) & 0x01) && !hasInteractiveWallFeature(cx, cy, kDirNorth))
+		p->yloc = MIN(p->yloc, cellMaxY - kWallPad);
+	// West wall of this cell (at cellMinX boundary)
+	if ((wallAt(cx, cy) & 0x02) && !hasInteractiveWallFeature(cx, cy, kDirWest))
+		p->xloc = MAX(p->xloc, cellMinX + kWallPad);
+	// East wall (at cellMaxX+1 boundary = west wall of cell to right)
+	if ((wallAt(cx + 1, cy) & 0x02) && !hasInteractiveWallFeature(cx, cy, kDirEast))
+		p->xloc = MIN(p->xloc, cellMaxX - kWallPad);
+}
+
+int ColonyEngine::checkwallMoveTo(int xnew, int ynew, int xind2, int yind2, Locate *pobject, uint8 trailCode) {
+	const int rnum = occupiedObjectAt(xind2, yind2, pobject);
+	if (rnum)
+		return rnum;
+	if (trailCode != 0 && pobject->type == kMeNum &&
+	    pobject->xindex >= 0 && pobject->xindex < 32 &&
+	    pobject->yindex >= 0 && pobject->yindex < 32)
+		_dirXY[pobject->xindex][pobject->yindex] = trailCode;
+	pobject->yindex = yind2;
+	pobject->xindex = xind2;
+	pobject->dx = xnew - pobject->xloc;
+	pobject->dy = ynew - pobject->yloc;
+	pobject->xloc = xnew;
+	pobject->yloc = ynew;
+	clampToWalls(pobject);
+	return 0;
+}
+
+int ColonyEngine::checkwallTryFeature(int xnew, int ynew, int xind2, int yind2, Locate *pobject, int dir) {
+	const uint8 *feature = mapFeatureAt(pobject->xindex, pobject->yindex, dir);
+	int r = tryPassThroughFeature(pobject->xindex, pobject->yindex, dir, pobject);
+	if (r == 2)
+		return 0; // teleported  position already updated by the feature
+	if (r == 1) {
+		const int rnum = occupiedObjectAt(xind2, yind2, pobject);
+		if (rnum) {
+			const bool showDoorText = (pobject == &_me && feature &&
+				(feature[0] == kWallFeatureDoor || feature[0] == kWallFeatureAirlock));
+			if (showDoorText)
+				doText(75, 0);
 			return rnum;
+		}
+		uint8 trailCode = 0;
+		switch (dir) {
+		case kDirNorth:
+			trailCode = 1;
+			break;
+		case kDirSouth:
+			trailCode = 5;
+			break;
+		case kDirEast:
+			trailCode = 3;
+			break;
+		case kDirWest:
+			trailCode = 7;
+			break;
+		default:
+			break;
+		}
 		if (trailCode != 0 && pobject->type == kMeNum &&
 		    pobject->xindex >= 0 && pobject->xindex < 32 &&
 		    pobject->yindex >= 0 && pobject->yindex < 32)
@@ -280,55 +313,13 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		pobject->yloc = ynew;
 		clampToWalls(pobject);
 		return 0;
-	};
-
-	// tryPassThroughFeature returns: 0=blocked, 1=pass through, 2=teleported (position already set)
-	auto tryFeature = [&](int dir) -> int {
-		const uint8 *feature = mapFeatureAt(pobject->xindex, pobject->yindex, dir);
-		int r = tryPassThroughFeature(pobject->xindex, pobject->yindex, dir, pobject);
-		if (r == 2)
-			return 0; // teleported  position already updated by the feature
-		if (r == 1) {
-			const int rnum = occupied();
-			if (rnum) {
-				const bool showDoorText = (pobject == &_me && feature &&
-					(feature[0] == kWallFeatureDoor || feature[0] == kWallFeatureAirlock));
-				if (showDoorText)
-					doText(75, 0);
-				return rnum;
-			}
-			uint8 trailCode = 0;
-			switch (dir) {
-			case kDirNorth:
-				trailCode = 1;
-				break;
-			case kDirSouth:
-				trailCode = 5;
-				break;
-			case kDirEast:
-				trailCode = 3;
-				break;
-			case kDirWest:
-				trailCode = 7;
-				break;
-			default:
-				break;
-			}
-			if (trailCode != 0 && pobject->type == kMeNum &&
-			    pobject->xindex >= 0 && pobject->xindex < 32 &&
-			    pobject->yindex >= 0 && pobject->yindex < 32)
-				_dirXY[pobject->xindex][pobject->yindex] = trailCode;
-			pobject->yindex = yind2;
-			pobject->xindex = xind2;
-			pobject->dx = xnew - pobject->xloc;
-			pobject->dy = ynew - pobject->yloc;
-			pobject->xloc = xnew;
-			pobject->yloc = ynew;
-			clampToWalls(pobject);
-			return 0;
-		}
-		return -2; // blocked, caller handles
-	};
+	}
+	return -2; // blocked, caller handles
+}
+
+int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
+	const int xind2 = xnew >> 8;
+	const int yind2 = ynew >> 8;
 
 	if (xind2 == pobject->xindex) {
 		if (yind2 == pobject->yindex) {
@@ -342,11 +333,11 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 
 		if (yind2 > pobject->yindex) {
 			if (!(_wall[pobject->xindex][yind2] & 1))
-				return moveTo(1);
+				return checkwallMoveTo(xnew, ynew, xind2, yind2, pobject, 1);
 			if (pobject->type == 2)
 				return -1;
 			{
-				int r = tryFeature(kDirNorth);
+				int r = checkwallTryFeature(xnew, ynew, xind2, yind2, pobject, kDirNorth);
 				if (r != -2)
 					return r;
 			}
@@ -358,11 +349,11 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		}
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 1))
-			return moveTo(5);
+			return checkwallMoveTo(xnew, ynew, xind2, yind2, pobject, 5);
 		if (pobject->type == 2)
 			return -1;
 		{
-			int r = tryFeature(kDirSouth);
+			int r = checkwallTryFeature(xnew, ynew, xind2, yind2, pobject, kDirSouth);
 			if (r != -2)
 				return r;
 		}
@@ -376,11 +367,11 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 	if (yind2 == pobject->yindex) {
 		if (xind2 > pobject->xindex) {
 			if (!(_wall[xind2][pobject->yindex] & 2))
-				return moveTo(3);
+				return checkwallMoveTo(xnew, ynew, xind2, yind2, pobject, 3);
 			if (pobject->type == 2)
 				return -1;
 			{
-				int r = tryFeature(kDirEast);
+				int r = checkwallTryFeature(xnew, ynew, xind2, yind2, pobject, kDirEast);
 				if (r != -2)
 					return r;
 			}
@@ -392,11 +383,11 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		}
 
 		if (!(_wall[pobject->xindex][pobject->yindex] & 2))
-			return moveTo(7);
+			return checkwallMoveTo(xnew, ynew, xind2, yind2, pobject, 7);
 		if (pobject->type == 2)
 			return -1;
 		{
-			int r = tryFeature(kDirWest);
+			int r = checkwallTryFeature(xnew, ynew, xind2, yind2, pobject, kDirWest);
 			if (r != -2)
 				return r;
 		}
@@ -434,7 +425,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 		}
 	}
 
-	return moveTo(0);
+	return checkwallMoveTo(xnew, ynew, xind2, yind2, pobject, 0);
 }
 
 bool ColonyEngine::setDoorState(int x, int y, int direction, int state) {
diff --git a/engines/colony/render_features.cpp b/engines/colony/render_features.cpp
index c2903515ff4..c85f2fc7f66 100644
--- a/engines/colony/render_features.cpp
+++ b/engines/colony/render_features.cpp
@@ -444,6 +444,10 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 	}
 }
 
+static float stairStepHeight(const float *vf, const float *vc, int d, int s) {
+	return vf[d] + (s + 1) / 8.0f * (vc[d] - vf[d]);
+}
+
 void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	const uint8 *map = mapFeatureAt(cellX, cellY, direction);
 	if (!map || map[0] == kWallFeatureNone)
@@ -665,10 +669,6 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 			vf[i] = inset;        // floor rises toward center
 			vc[i] = 1.0f - inset; // ceiling drops toward center
 		}
-		// Step height: at depth d, step s is at fraction (s+1)/8 from floor to ceiling
-		auto vh = [&](int d, int s) -> float {
-			return vf[d] + (s + 1) / 8.0f * (vc[d] - vf[d]);
-		};
 		// Back of passage (full depth)
 		float bi = 1.0f / 3.0f; // back inset
 		float bu = bi, bur = 1.0f - bi, bvc = 1.0f - bi;
@@ -678,10 +678,10 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		wallLine(corners, bur, 0.5f, bur, bvc, col);
 
 		// 2. Back wall landing (depth 6 to full depth)
-		wallLine(corners, ul[6], vh(6, 6), bu, bvc, col);
+		wallLine(corners, ul[6], stairStepHeight(vf, vc, 6, 6), bu, bvc, col);
 		wallLine(corners, bu, bvc, bur, bvc, col);
-		wallLine(corners, bur, bvc, ur[6], vh(6, 6), col);
-		wallLine(corners, ur[6], vh(6, 6), ul[6], vh(6, 6), col);
+		wallLine(corners, bur, bvc, ur[6], stairStepHeight(vf, vc, 6, 6), col);
+		wallLine(corners, ur[6], stairStepHeight(vf, vc, 6, 6), ul[6], stairStepHeight(vf, vc, 6, 6), col);
 
 		// 3. First step tread (floor from wall face to depth 0)
 		wallLine(corners, 0.0f, 0.0f, ul[0], vf[0], col);
@@ -690,24 +690,24 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		wallLine(corners, 1.0f, 0.0f, 0.0f, 0.0f, col);
 
 		// 4. First step riser (at depth 0)
-		wallLine(corners, ul[0], vh(0, 0), ul[0], vf[0], col);
-		wallLine(corners, ur[0], vf[0], ur[0], vh(0, 0), col);
-		wallLine(corners, ur[0], vh(0, 0), ul[0], vh(0, 0), col);
+		wallLine(corners, ul[0], stairStepHeight(vf, vc, 0, 0), ul[0], vf[0], col);
+		wallLine(corners, ur[0], vf[0], ur[0], stairStepHeight(vf, vc, 0, 0), col);
+		wallLine(corners, ur[0], stairStepHeight(vf, vc, 0, 0), ul[0], stairStepHeight(vf, vc, 0, 0), col);
 
 		// 5. Step treads (i=3..0: depth i to depth i+1)
 		for (int i = 3; i >= 0; i--) {
-			wallLine(corners, ul[i], vh(i, i), ul[i + 1], vh(i + 1, i), col);
-			wallLine(corners, ul[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i), col);
-			wallLine(corners, ur[i + 1], vh(i + 1, i), ur[i], vh(i, i), col);
-			wallLine(corners, ur[i], vh(i, i), ul[i], vh(i, i), col);
+			wallLine(corners, ul[i], stairStepHeight(vf, vc, i, i), ul[i + 1], stairStepHeight(vf, vc, i + 1, i), col);
+			wallLine(corners, ul[i + 1], stairStepHeight(vf, vc, i + 1, i), ur[i + 1], stairStepHeight(vf, vc, i + 1, i), col);
+			wallLine(corners, ur[i + 1], stairStepHeight(vf, vc, i + 1, i), ur[i], stairStepHeight(vf, vc, i, i), col);
+			wallLine(corners, ur[i], stairStepHeight(vf, vc, i, i), ul[i], stairStepHeight(vf, vc, i, i), col);
 		}
 
 		// 6. Step risers (i=5..0: vertical face at depth i+1)
 		for (int i = 5; i >= 0; i--) {
-			wallLine(corners, ul[i + 1], vh(i + 1, i + 1), ul[i + 1], vh(i + 1, i), col);
-			wallLine(corners, ul[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i), col);
-			wallLine(corners, ur[i + 1], vh(i + 1, i), ur[i + 1], vh(i + 1, i + 1), col);
-			wallLine(corners, ur[i + 1], vh(i + 1, i + 1), ul[i + 1], vh(i + 1, i + 1), col);
+			wallLine(corners, ul[i + 1], stairStepHeight(vf, vc, i + 1, i + 1), ul[i + 1], stairStepHeight(vf, vc, i + 1, i), col);
+			wallLine(corners, ul[i + 1], stairStepHeight(vf, vc, i + 1, i), ur[i + 1], stairStepHeight(vf, vc, i + 1, i), col);
+			wallLine(corners, ur[i + 1], stairStepHeight(vf, vc, i + 1, i), ur[i + 1], stairStepHeight(vf, vc, i + 1, i + 1), col);
+			wallLine(corners, ur[i + 1], stairStepHeight(vf, vc, i + 1, i + 1), ul[i + 1], stairStepHeight(vf, vc, i + 1, i + 1), col);
 		}
 
 		// 7. Handrails: from center of wall edges up to near-ceiling at mid-depth
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index b636ed804fe..4fcd946da73 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1081,158 +1081,138 @@ void ColonyEngine::drawStaticObjects() {
 	}
 }
 
-bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
-	const auto drawPrism = [&](const PrismPartDef &def, bool useLook, int colorOverride, bool forceVisible) {
-		draw3DPrism(obj, def, useLook, colorOverride, true, forceVisible);
-	};
-	const auto drawSphere = [&](int pt0x, int pt0y, int pt0z, int pt1x, int pt1y, int pt1z,
-	                            uint32 fillColor, uint32 outlineColor) {
-		draw3DSphere(obj, pt0x, pt0y, pt0z, pt1x, pt1y, pt1z, fillColor, outlineColor, true);
-	};
-	const auto drawSphereFor = [&](Thing &thing, int pt0x, int pt0y, int pt0z, int pt1x, int pt1y, int pt1z,
-	                               uint32 fillColor, uint32 outlineColor) {
-		draw3DSphere(thing, pt0x, pt0y, pt0z, pt1x, pt1y, pt1z, fillColor, outlineColor, true);
-	};
-	const auto drawPrismOvalFor = [&](Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride) {
-		if (def.pointCount < 4 || def.surfaceCount < 1)
-			return;
-
-		const uint8 ang = (useLook ? thing.where.look : thing.where.ang) + 32;
-		const long rotCos = _cost[ang];
-		const long rotSin = _sint[ang];
-		int projectedX[32];
-		int projectedY[32];
-		bool projected[32];
-
-		assert(def.pointCount <= ARRAYSIZE(projectedX));
-
-		for (int i = 0; i < def.pointCount; ++i) {
-			const int ox = def.points[i][0];
-			const int oy = def.points[i][1];
-			const int oz = def.points[i][2];
-			const long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
-			const long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
-			projected[i] = projectCorridorPointRaw(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
-			                                       (float)(rx + thing.where.xloc), (float)(ry + thing.where.yloc), (float)(oz - 160),
-			                                       projectedX[i], projectedY[i]);
-		}
+void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride) {
+	if (def.pointCount < 4 || def.surfaceCount < 1)
+		return;
+
+	const uint8 ang = (useLook ? thing.where.look : thing.where.ang) + 32;
+	const long rotCos = _cost[ang];
+	const long rotSin = _sint[ang];
+	int projectedX[32];
+	int projectedY[32];
+	bool projected[32];
+
+	assert(def.pointCount <= ARRAYSIZE(projectedX));
+
+	for (int i = 0; i < def.pointCount; ++i) {
+		const int ox = def.points[i][0];
+		const int oy = def.points[i][1];
+		const int oz = def.points[i][2];
+		const long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
+		const long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+		projected[i] = projectCorridorPointRaw(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+		                                       (float)(rx + thing.where.xloc), (float)(ry + thing.where.yloc), (float)(oz - 160),
+		                                       projectedX[i], projectedY[i]);
+	}
 
-		const int *surface = &def.surfaces[0][2];
-		const int pointCount = def.surfaces[0][1];
-		if (pointCount < 4)
+	const int *surface = &def.surfaces[0][2];
+	const int pointCount = def.surfaces[0][1];
+	if (pointCount < 4)
+		return;
+	for (int i = 0; i < pointCount; ++i) {
+		const int pointIdx = surface[i];
+		if (pointIdx < 0 || pointIdx >= def.pointCount || !projected[pointIdx])
 			return;
-		for (int i = 0; i < pointCount; ++i) {
-			const int pointIdx = surface[i];
-			if (pointIdx < 0 || pointIdx >= def.pointCount || !projected[pointIdx])
-				return;
-		}
+	}
 
-		if (!isProjectedSurfaceVisible(surface, pointCount, projectedX, projectedY))
-			return;
+	if (!isProjectedSurfaceVisible(surface, pointCount, projectedX, projectedY))
+		return;
+
+	const int left = projectedX[3];
+	const int right = projectedX[1];
+	const int top = projectedY[2];
+	const int bottom = projectedY[0];
+	if (right <= left || bottom <= top)
+		return;
+
+	const int cx = (left + right) / 2;
+	const int cy = (top + bottom) / 2;
+	const int rx = MAX(1, (right - left) / 2);
+	const int ry = MAX(1, (bottom - top) / 2);
+	const int fillColorIdx = (colorOverride >= 0) ? colorOverride : def.surfaces[0][0];
+	uint32 fillColor;
+	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+		const int macColorIdx = mapEyeOverlayColorToMacColor(fillColorIdx, _level);
+		const bool useForeground = (fillColorIdx == kColorPupil || fillColorIdx == kColorBlack);
+		fillColor = packEyeOverlayMacColor(useForeground ? _macColors[macColorIdx].fg : _macColors[macColorIdx].bg);
+	} else {
+		fillColor = mapEyeOverlayColorToDOSFill(fillColorIdx, _level);
+	}
+	const uint32 outlineColor = (_renderMode == Common::kRenderMacintosh) ? 0xFF000000 : (uint32)kColorBlack;
 
-		const int left = projectedX[3];
-		const int right = projectedX[1];
-		const int top = projectedY[2];
-		const int bottom = projectedY[0];
-		if (right <= left || bottom <= top)
-			return;
+	_gfx->fillEllipse(cx, cy, rx, ry, fillColor);
+	_gfx->drawEllipse(cx, cy, rx, ry, outlineColor);
 
-		const int cx = (left + right) / 2;
-		const int cy = (top + bottom) / 2;
-		const int rx = MAX(1, (right - left) / 2);
-		const int ry = MAX(1, (bottom - top) / 2);
-		const int fillColorIdx = (colorOverride >= 0) ? colorOverride : def.surfaces[0][0];
-		uint32 fillColor;
-		if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
-			const int macColorIdx = mapEyeOverlayColorToMacColor(fillColorIdx, _level);
-			const bool useForeground = (fillColorIdx == kColorPupil || fillColorIdx == kColorBlack);
-			fillColor = packEyeOverlayMacColor(useForeground ? _macColors[macColorIdx].fg : _macColors[macColorIdx].bg);
-		} else {
-			fillColor = mapEyeOverlayColorToDOSFill(fillColorIdx, _level);
-		}
-		const uint32 outlineColor = (_renderMode == Common::kRenderMacintosh) ? 0xFF000000 : (uint32)kColorBlack;
-
-		_gfx->fillEllipse(cx, cy, rx, ry, fillColor);
-		_gfx->drawEllipse(cx, cy, rx, ry, outlineColor);
-
-		thing.where.xmn = MIN(thing.where.xmn, left);
-		thing.where.xmx = MAX(thing.where.xmx, right);
-		thing.where.zmn = MIN(thing.where.zmn, top);
-		thing.where.zmx = MAX(thing.where.zmx, bottom);
-	};
-	const auto mergeBounds = [&](const Thing &thing) {
-		if (thing.where.xmn < obj.where.xmn)
-			obj.where.xmn = thing.where.xmn;
-		if (thing.where.xmx > obj.where.xmx)
-			obj.where.xmx = thing.where.xmx;
-		if (thing.where.zmn < obj.where.zmn)
-			obj.where.zmn = thing.where.zmn;
-		if (thing.where.zmx > obj.where.zmx)
-			obj.where.zmx = thing.where.zmx;
-	};
+	thing.where.xmn = MIN(thing.where.xmn, left);
+	thing.where.xmx = MAX(thing.where.xmx, right);
+	thing.where.zmn = MIN(thing.where.zmn, top);
+	thing.where.zmx = MAX(thing.where.zmx, bottom);
+}
+
+bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	const int eyeballColor = (_level == 1 || _level == 7) ? kColorPupil : kColorEyeball;
 	const int pupilColor = (_level == 1 || _level == 7) ? kColorEyeball : kColorPupil;
 
 	switch (obj.type) {
 	case kObjConsole:
-		drawPrism(kConsolePart, false, -1, false);
+		draw3DPrism(obj, kConsolePart, false, -1, true, false);
 		break;
 	case kObjCChair:
 		for (int i = 0; i < 5; i++) {
 			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			drawPrism(kCChairParts[i], false, -1, false);
+			draw3DPrism(obj, kCChairParts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjPlant:
 		// DOS MakePlant draw order: top pot, then green leaf lines, then pot on top.
-		drawPrism(kPlantParts[1], false, -1, false); // top pot (soil)
+		draw3DPrism(obj, kPlantParts[1], false, -1, true, false); // top pot (soil)
 		for (int i = 2; i < 8; i++)
 			draw3DLeaf(obj, kPlantParts[i]); // leaves as lines
-		drawPrism(kPlantParts[0], false, -1, false); // pot (drawn last, on top)
+		draw3DPrism(obj, kPlantParts[0], false, -1, true, false); // pot (drawn last, on top)
 		break;
 	case kObjCouch:
 	case kObjChair: {
 		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
 		for (int i = 0; i < 4; i++) {
 			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
-			drawPrism(parts[i], false, -1, false);
+			draw3DPrism(obj, parts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	}
 	case kObjTV:
 		_gfx->setDepthRange(0.002, 1.0); // body base layer (pushed back)
-		drawPrism(kTVParts[0], false, -1, false);
+		draw3DPrism(obj, kTVParts[0], false, -1, true, false);
 		_gfx->setDepthRange(0.0, 1.0);   // screen on top of body face
-		drawPrism(kTVParts[1], false, -1, false);
+		draw3DPrism(obj, kTVParts[1], false, -1, true, false);
 		break;
 	case kObjDrawer:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kDrawerParts[i], false, -1, false);
+			draw3DPrism(obj, kDrawerParts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjFWall:
 		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
-			drawPrism(kFWallPart, false, kColorCorridorWall, false);
+			draw3DPrism(obj, kFWallPart, false, kColorCorridorWall, true, false);
 		else
-			drawPrism(kFWallPart, false, -1, false);
+			draw3DPrism(obj, kFWallPart, false, -1, true, false);
 		break;
 	case kObjCWall:
 		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
-			drawPrism(kCWallPart, false, kColorCorridorWall, false);
+			draw3DPrism(obj, kCWallPart, false, kColorCorridorWall, true, false);
 		else
-			drawPrism(kCWallPart, false, -1, false);
+			draw3DPrism(obj, kCWallPart, false, -1, true, false);
 		break;
 	case kObjScreen:
-		drawPrism(kScreenPart, false, -1, false);
+		draw3DPrism(obj, kScreenPart, false, -1, true, false);
 		break;
 	case kObjTable:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kTableParts[i], false, -1, false);
+			draw3DPrism(obj, kTableParts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
@@ -1241,7 +1221,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
 		for (int i = 0; i < 3; i++) {
 			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
-			drawPrism(parts[i], false, -1, false);
+			draw3DPrism(obj, parts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
@@ -1249,28 +1229,28 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	case kObjDesk:
 		for (int i = 0; i < 10; i++) {
 			_gfx->setDepthRange((9 - i) * 0.002, 1.0);
-			drawPrism(kDeskParts[i], false, -1, false);
+			draw3DPrism(obj, kDeskParts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjBox1:
-		drawPrism(kBox1Part, false, -1, false);
+		draw3DPrism(obj, kBox1Part, false, -1, true, false);
 		break;
 	case kObjBench:
-		drawPrism(kBenchPart, false, -1, false);
+		draw3DPrism(obj, kBenchPart, false, -1, true, false);
 		break;
 	case kObjCBench:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kCBenchParts[i], false, -1, false);
+			draw3DPrism(obj, kCBenchParts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjBox2:
 		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kBox2Parts[1], false, -1, false); // base first
+		draw3DPrism(obj, kBox2Parts[1], false, -1, true, false); // base first
 		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kBox2Parts[0], false, -1, false); // top second
+		draw3DPrism(obj, kBox2Parts[0], false, -1, true, false); // top second
 		break;
 	case kObjReactor: {
 		// MakeReactor: animate core height and recolor only the original
@@ -1341,12 +1321,12 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		// Depth separation matches the original draw order closely, but the
 		// per-face colors now follow MakeReactor() exactly.
 		_gfx->setDepthRange(0.004, 1.0);
-		drawPrism(modTopDef, false, -1, false);
+		draw3DPrism(obj, modTopDef, false, -1, true, false);
 		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(modRingDef, false, -1, false);
+		draw3DPrism(obj, modRingDef, false, -1, true, false);
 		if (_coreState[_coreIndex] < 2) {
 			_gfx->setDepthRange(0.0, 1.0);
-			drawPrism(modCoreDef, false, -1, false);
+			draw3DPrism(obj, modCoreDef, false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
@@ -1359,76 +1339,76 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 
 		for (int i = 0; i < 4; i++) {
 			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			drawPrism(kPowerSuitParts[i], false, -1, false);
+			draw3DPrism(obj, kPowerSuitParts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kPowerSuitParts[4], false, sourceColor, false);
+		draw3DPrism(obj, kPowerSuitParts[4], false, sourceColor, true, false);
 		break;
 	}
 	case kObjTeleport:
-		drawPrism(kTelePart, false, -1, false);
+		draw3DPrism(obj, kTelePart, false, -1, true, false);
 		break;
 	case kObjCryo:
 		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kCryoParts[1], false, -1, false); // base first
+		draw3DPrism(obj, kCryoParts[1], false, -1, true, false); // base first
 		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kCryoParts[0], false, -1, false); // top second
+		draw3DPrism(obj, kCryoParts[0], false, -1, true, false); // top second
 		break;
 	case kObjProjector:
 		// Projector sits on table  draw table first, then projector parts
 		_gfx->setDepthRange(0.008, 1.0);
-		drawPrism(kTableParts[0], false, -1, false); // table base
+		draw3DPrism(obj, kTableParts[0], false, -1, true, false); // table base
 		_gfx->setDepthRange(0.006, 1.0);
-		drawPrism(kTableParts[1], false, -1, false); // table top
+		draw3DPrism(obj, kTableParts[1], false, -1, true, false); // table top
 		_gfx->setDepthRange(0.004, 1.0);
-		drawPrism(kProjectorParts[1], false, -1, false); // stand
+		draw3DPrism(obj, kProjectorParts[1], false, -1, true, false); // stand
 		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kProjectorParts[0], false, -1, false); // body
+		draw3DPrism(obj, kProjectorParts[0], false, -1, true, false); // body
 		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kProjectorParts[2], false, -1, false); // lens
+		draw3DPrism(obj, kProjectorParts[2], false, -1, true, false); // lens
 		break;
 	case kObjTub:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
-			drawPrism(kTubParts[i], false, -1, false);
+			draw3DPrism(obj, kTubParts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjSink:
 		for (int i = 0; i < 3; i++) {
 			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
-			drawPrism(kSinkParts[i], false, -1, false);
+			draw3DPrism(obj, kSinkParts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjToilet:
 		for (int i = 0; i < 4; i++) {
 			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
-			drawPrism(kToiletParts[i], false, -1, false);
+			draw3DPrism(obj, kToiletParts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjPToilet:
 		for (int i = 0; i < 5; i++) {
 			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			drawPrism(kPToiletParts[i], false, -1, false);
+			draw3DPrism(obj, kPToiletParts[i], false, -1, true, false);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjForkLift:
 		// Draw order: forks, arms, treads, cab (back-to-front)
 		_gfx->setDepthRange(0.010, 1.0);
-		drawPrism(kForkliftParts[3], false, -1, false); // FLLL (left fork)
+		draw3DPrism(obj, kForkliftParts[3], false, -1, true, false); // FLLL (left fork)
 		_gfx->setDepthRange(0.008, 1.0);
-		drawPrism(kForkliftParts[2], false, -1, false); // FLUL (left arm)
+		draw3DPrism(obj, kForkliftParts[2], false, -1, true, false); // FLUL (left arm)
 		_gfx->setDepthRange(0.006, 1.0);
-		drawPrism(kForkliftParts[5], false, -1, false); // FLLR (right fork)
+		draw3DPrism(obj, kForkliftParts[5], false, -1, true, false); // FLLR (right fork)
 		_gfx->setDepthRange(0.004, 1.0);
-		drawPrism(kForkliftParts[4], false, -1, false); // FLUR (right arm)
+		draw3DPrism(obj, kForkliftParts[4], false, -1, true, false); // FLUR (right arm)
 		_gfx->setDepthRange(0.002, 1.0);
-		drawPrism(kForkliftParts[1], false, -1, false); // treads
+		draw3DPrism(obj, kForkliftParts[1], false, -1, true, false); // treads
 		_gfx->setDepthRange(0.0, 1.0);
-		drawPrism(kForkliftParts[0], false, -1, false); // cab
+		draw3DPrism(obj, kForkliftParts[0], false, -1, true, false); // cab
 		break;
 	// === Robot types (1-20) ===
 	case kRobEye:
@@ -1436,65 +1416,65 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		    (obj.where.yloc - _me.yloc) * (obj.where.yloc - _me.yloc) <= 64 * 64) {
 			break;
 		}
-		drawSphere(0, 0, 100, 0, 0, 200, eyeballColor, kColorBlack);
-		drawPrismOvalFor(obj, kEyeIrisDef, false, -1);
-		drawPrismOvalFor(obj, kEyePupilDef, false, pupilColor);
+		draw3DSphere(obj, 0, 0, 100, 0, 0, 200, eyeballColor, kColorBlack, true);
+		drawPrismOval3D(obj, kEyeIrisDef, false, -1);
+		drawPrismOval3D(obj, kEyePupilDef, false, pupilColor);
 		break;
 	case kRobPyramid:
-		drawPrism(kPShadowDef, false, -1, false);
-		drawPrism(kPyramidBodyDef, false, -1, false);
-		drawSphere(0, 0, 175, 0, 0, 200, eyeballColor, kColorBlack);
-		drawPrismOvalFor(obj, kPIrisDef, false, -1);
-		drawPrismOvalFor(obj, kPPupilDef, false, pupilColor);
+		draw3DPrism(obj, kPShadowDef, false, -1, true, false);
+		draw3DPrism(obj, kPyramidBodyDef, false, -1, true, false);
+		draw3DSphere(obj, 0, 0, 175, 0, 0, 200, eyeballColor, kColorBlack, true);
+		drawPrismOval3D(obj, kPIrisDef, false, -1);
+		drawPrismOval3D(obj, kPPupilDef, false, pupilColor);
 		break;
 	case kRobCube:
-		drawPrism(kCubeBodyDef, false, -1, false);
+		draw3DPrism(obj, kCubeBodyDef, false, -1, true, false);
 		break;
 	case kRobUPyramid:
-		drawPrism(kUPShadowDef, false, -1, false);
-		drawPrism(kUPyramidBodyDef, false, -1, false);
+		draw3DPrism(obj, kUPShadowDef, false, -1, true, false);
+		draw3DPrism(obj, kUPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobFEye:
-		drawSphere(0, 0, 0, 0, 0, 100, eyeballColor, kColorBlack);
-		drawPrismOvalFor(obj, kFEyeIrisDef, false, -1);
-		drawPrismOvalFor(obj, kFEyePupilDef, false, pupilColor);
+		draw3DSphere(obj, 0, 0, 0, 0, 0, 100, eyeballColor, kColorBlack, true);
+		drawPrismOval3D(obj, kFEyeIrisDef, false, -1);
+		drawPrismOval3D(obj, kFEyePupilDef, false, pupilColor);
 		break;
 	case kRobFPyramid:
-		drawPrism(kFPyramidBodyDef, false, -1, false);
+		draw3DPrism(obj, kFPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobFCube:
-		drawPrism(kFCubeBodyDef, false, -1, false);
+		draw3DPrism(obj, kFCubeBodyDef, false, -1, true, false);
 		break;
 	case kRobFUPyramid:
-		drawPrism(kFUPyramidBodyDef, false, -1, false);
+		draw3DPrism(obj, kFUPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobSEye:
-		drawSphere(0, 0, 0, 0, 0, 50, eyeballColor, kColorBlack);
-		drawPrismOvalFor(obj, kSEyeIrisDef, false, -1);
-		drawPrismOvalFor(obj, kSEyePupilDef, false, pupilColor);
+		draw3DSphere(obj, 0, 0, 0, 0, 0, 50, eyeballColor, kColorBlack, true);
+		drawPrismOval3D(obj, kSEyeIrisDef, false, -1);
+		drawPrismOval3D(obj, kSEyePupilDef, false, pupilColor);
 		break;
 	case kRobSPyramid:
-		drawPrism(kSPyramidBodyDef, false, -1, false);
+		draw3DPrism(obj, kSPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobSCube:
-		drawPrism(kSCubeBodyDef, false, -1, false);
+		draw3DPrism(obj, kSCubeBodyDef, false, -1, true, false);
 		break;
 	case kRobSUPyramid:
-		drawPrism(kSUPyramidBodyDef, false, -1, false);
+		draw3DPrism(obj, kSUPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobMEye:
-		drawSphere(0, 0, 0, 0, 0, 25, eyeballColor, kColorBlack);
-		drawPrismOvalFor(obj, kMEyeIrisDef, false, kColorMiniEyeIris);
-		drawPrismOvalFor(obj, kMEyePupilDef, false, pupilColor);
+		draw3DSphere(obj, 0, 0, 0, 0, 0, 25, eyeballColor, kColorBlack, true);
+		drawPrismOval3D(obj, kMEyeIrisDef, false, kColorMiniEyeIris);
+		drawPrismOval3D(obj, kMEyePupilDef, false, pupilColor);
 		break;
 	case kRobMPyramid:
-		drawPrism(kMPyramidBodyDef, false, -1, false);
+		draw3DPrism(obj, kMPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobMCube:
-		drawPrism(kMCubeBodyDef, false, -1, false);
+		draw3DPrism(obj, kMCubeBodyDef, false, -1, true, false);
 		break;
 	case kRobMUPyramid:
-		drawPrism(kMUPyramidBodyDef, false, -1, false);
+		draw3DPrism(obj, kMUPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobQueen:
 		{
@@ -1526,28 +1506,42 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			const PrismPartDef &nearWing = leftFirst ? kQRWingDef : kQLWingDef;
 			const int wingColor = (_renderMode != Common::kRenderMacintosh && _level == 7) ? kColorQueenWingRed : kColorClear;
 
-			drawPrism(farWing, false, wingColor, true);
-			drawSphereFor(farEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack);
-			drawPrismOvalFor(farEye, kQIrisDef, true, -1);
-			drawPrismOvalFor(farEye, kQPupilDef, true, pupilColor);
-			mergeBounds(farEye);
-
-			drawPrism(kQThoraxDef, false, -1, false);
-			drawPrism(kQAbdomenDef, false, -1, false);
-
-			drawPrism(nearWing, false, wingColor, true);
-			drawSphereFor(nearEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack);
-			drawPrismOvalFor(nearEye, kQIrisDef, true, -1);
-			drawPrismOvalFor(nearEye, kQPupilDef, true, pupilColor);
-			mergeBounds(nearEye);
+			draw3DPrism(obj, farWing, false, wingColor, true, true);
+			draw3DSphere(farEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			drawPrismOval3D(farEye, kQIrisDef, true, -1);
+			drawPrismOval3D(farEye, kQPupilDef, true, pupilColor);
+			if (farEye.where.xmn < obj.where.xmn)
+				obj.where.xmn = farEye.where.xmn;
+			if (farEye.where.xmx > obj.where.xmx)
+				obj.where.xmx = farEye.where.xmx;
+			if (farEye.where.zmn < obj.where.zmn)
+				obj.where.zmn = farEye.where.zmn;
+			if (farEye.where.zmx > obj.where.zmx)
+				obj.where.zmx = farEye.where.zmx;
+
+			draw3DPrism(obj, kQThoraxDef, false, -1, true, false);
+			draw3DPrism(obj, kQAbdomenDef, false, -1, true, false);
+
+			draw3DPrism(obj, nearWing, false, wingColor, true, true);
+			draw3DSphere(nearEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			drawPrismOval3D(nearEye, kQIrisDef, true, -1);
+			drawPrismOval3D(nearEye, kQPupilDef, true, pupilColor);
+			if (nearEye.where.xmn < obj.where.xmn)
+				obj.where.xmn = nearEye.where.xmn;
+			if (nearEye.where.xmx > obj.where.xmx)
+				obj.where.xmx = nearEye.where.xmx;
+			if (nearEye.where.zmn < obj.where.zmn)
+				obj.where.zmn = nearEye.where.zmn;
+			if (nearEye.where.zmx > obj.where.zmx)
+				obj.where.zmx = nearEye.where.zmx;
 		}
 		break;
 	case kRobDrone:
-		drawPrism(kDAbdomenDef, false, -1, false);
-		drawPrism(kDLLPincerDef, false, -1, false);
-		drawPrism(kDRRPincerDef, false, -1, false);
-		drawPrism(kDLEyeDef, false, -1, false);
-		drawPrism(kDREyeDef, false, -1, false);
+		draw3DPrism(obj, kDAbdomenDef, false, -1, true, false);
+		draw3DPrism(obj, kDLLPincerDef, false, -1, true, false);
+		draw3DPrism(obj, kDRRPincerDef, false, -1, true, false);
+		draw3DPrism(obj, kDLEyeDef, false, -1, true, false);
+		draw3DPrism(obj, kDREyeDef, false, -1, true, false);
 		break;
 	case kRobSoldier:
 		{
@@ -1567,16 +1561,16 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			const PrismPartDef leftPincerDef = {4, leftPincerPts, 4, kDLPincerSurf};
 			const PrismPartDef rightPincerDef = {4, rightPincerPts, 4, kDRPincerSurf};
 
-			drawPrism(kDAbdomenDef, false, kColorSoldierBody, false);
-			drawPrism(leftPincerDef, false, -1, false);
-			drawPrism(rightPincerDef, false, -1, false);
-			drawPrism(kDLEyeDef, false, kColorSoldierEye, false);
-			drawPrism(kDREyeDef, false, kColorSoldierEye, false);
+			draw3DPrism(obj, kDAbdomenDef, false, kColorSoldierBody, true, false);
+			draw3DPrism(obj, leftPincerDef, false, -1, true, false);
+			draw3DPrism(obj, rightPincerDef, false, -1, true, false);
+			draw3DPrism(obj, kDLEyeDef, false, kColorSoldierEye, true, false);
+			draw3DPrism(obj, kDREyeDef, false, kColorSoldierEye, true, false);
 		}
 		break;
 	case kRobSnoop:
-		drawPrism(kSnoopAbdomenDef, false, -1, false);
-		drawPrism(kSnoopHeadDef, false, -1, false);
+		draw3DPrism(obj, kSnoopAbdomenDef, false, -1, true, false);
+		draw3DPrism(obj, kSnoopHeadDef, false, -1, true, false);
 		break;
 	default:
 		return false;
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 7072e6f374a..33fa745be25 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -537,6 +537,41 @@ void ColonyEngine::drawDashboardMac() {
 	}
 }
 
+void ColonyEngine::drawMiniMapMarker(int x, int y, int halfSize, uint32 color, bool isMac) {
+	if (x < _headsUpRect.left + 1 || x >= _headsUpRect.right - 1 ||
+	    y < _headsUpRect.top + 1 || y >= _headsUpRect.bottom - 1)
+		return;
+	if (isMac) {
+		_gfx->drawEllipse(x, y, halfSize, halfSize, color);
+	} else {
+		const int l = MAX<int>(_headsUpRect.left + 1, x - halfSize);
+		const int t = MAX<int>(_headsUpRect.top + 1, y - halfSize);
+		const int r = MIN<int>(_headsUpRect.right - 1, x + halfSize + 1);
+		const int b = MIN<int>(_headsUpRect.bottom - 1, y + halfSize + 1);
+		if (l >= r || t >= b)
+			return;
+		_gfx->drawRect(Common::Rect(l, t, r, b), color);
+	}
+}
+
+bool ColonyEngine::hasRobotAt(int x, int y) const {
+	if (x < 0 || x >= 32 || y < 0 || y >= 32)
+		return false;
+	return _robotArray[x][y] != 0;
+}
+
+bool ColonyEngine::hasFoodAt(int x, int y) const {
+	const int kFWALLType = 48;
+	if (x < 0 || x >= 32 || y < 0 || y >= 32)
+		return false;
+	const uint8 num = _foodArray[x][y];
+	if (num == 0)
+		return false;
+	if (num <= _objects.size())
+		return _objects[num - 1].type < kFWALLType;
+	return true;
+}
+
 // Draws the mini floor map into _headsUpRect (shared by Mac and DOS paths)
 void ColonyEngine::drawMiniMap(uint32 lineColor) {
 	if (_gameMode != kModeColony)
@@ -544,7 +579,6 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 	if (_me.xindex < 0 || _me.xindex >= 32 || _me.yindex < 0 || _me.yindex >= 32)
 		return;
 
-	const int kFWALLType = 48;
 	const Common::Rect miniMapClip(_headsUpRect.left + 1, _headsUpRect.top + 1, _headsUpRect.right - 1, _headsUpRect.bottom - 1);
 	auto drawMiniMapLine = [&](int x1, int y1, int x2, int y2, uint32 color) {
 		if (clipLineToRect(x1, y1, x2, y2, miniMapClip))
@@ -604,66 +638,32 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 	const int foodR = isMac ? 3 : 1;
 	const int robotR = isMac ? 5 : 2;
 
-	auto drawMarker = [&](int x, int y, int halfSize, uint32 color) {
-		if (x < _headsUpRect.left + 1 || x >= _headsUpRect.right - 1 ||
-		    y < _headsUpRect.top + 1 || y >= _headsUpRect.bottom - 1)
-			return;
-		if (isMac) {
-			// compass.c: FrameOval  circle outline
-			_gfx->drawEllipse(x, y, halfSize, halfSize, color);
-		} else {
-			const int l = MAX<int>(_headsUpRect.left + 1, x - halfSize);
-			const int t = MAX<int>(_headsUpRect.top + 1, y - halfSize);
-			const int r = MIN<int>(_headsUpRect.right - 1, x + halfSize + 1);
-			const int b = MIN<int>(_headsUpRect.bottom - 1, y + halfSize + 1);
-			if (l >= r || t >= b)
-				return;
-			_gfx->drawRect(Common::Rect(l, t, r, b), color);
-		}
-	};
-
-	auto hasRobotAt = [&](int x, int y) -> bool {
-		if (x < 0 || x >= 32 || y < 0 || y >= 32)
-			return false;
-		return _robotArray[x][y] != 0;
-	};
-	auto hasFoodAt = [&](int x, int y) -> bool {
-		if (x < 0 || x >= 32 || y < 0 || y >= 32)
-			return false;
-		const uint8 num = _foodArray[x][y];
-		if (num == 0)
-			return false;
-		if (num <= _objects.size())
-			return _objects[num - 1].type < kFWALLType;
-		return true;
-	};
-
 	if (hasFoodAt(_me.xindex, _me.yindex))
-		drawMarker(xcorner[4], ycorner[4], foodR, lineColor);
+		drawMiniMapMarker(xcorner[4], ycorner[4], foodR, lineColor, isMac);
 
 	if (_me.yindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x01)) {
 		if (hasFoodAt(_me.xindex, _me.yindex - 1))
-			drawMarker(xcorner[4] + dy, ycorner[4] + dx, foodR, lineColor);
+			drawMiniMapMarker(xcorner[4] + dy, ycorner[4] + dx, foodR, lineColor, isMac);
 		if (hasRobotAt(_me.xindex, _me.yindex - 1))
-			drawMarker(xcorner[4] + dy, ycorner[4] + dx, robotR, lineColor);
+			drawMiniMapMarker(xcorner[4] + dy, ycorner[4] + dx, robotR, lineColor, isMac);
 	}
 	if (_me.xindex > 0 && !(_wall[_me.xindex][_me.yindex] & 0x02)) {
 		if (hasFoodAt(_me.xindex - 1, _me.yindex))
-			drawMarker(xcorner[4] - dx, ycorner[4] + dy, foodR, lineColor);
+			drawMiniMapMarker(xcorner[4] - dx, ycorner[4] + dy, foodR, lineColor, isMac);
 		if (hasRobotAt(_me.xindex - 1, _me.yindex))
-			drawMarker(xcorner[4] - dx, ycorner[4] + dy, robotR, lineColor);
+			drawMiniMapMarker(xcorner[4] - dx, ycorner[4] + dy, robotR, lineColor, isMac);
 	}
 	if (_me.yindex < 30 && !(_wall[_me.xindex][_me.yindex + 1] & 0x01)) {
 		if (hasFoodAt(_me.xindex, _me.yindex + 1))
-			drawMarker(xcorner[4] - dy, ycorner[4] - dx, foodR, lineColor);
+			drawMiniMapMarker(xcorner[4] - dy, ycorner[4] - dx, foodR, lineColor, isMac);
 		if (hasRobotAt(_me.xindex, _me.yindex + 1))
-			drawMarker(xcorner[4] - dy, ycorner[4] - dx, robotR, lineColor);
+			drawMiniMapMarker(xcorner[4] - dy, ycorner[4] - dx, robotR, lineColor, isMac);
 	}
 	if (_me.xindex < 30 && !(_wall[_me.xindex + 1][_me.yindex] & 0x02)) {
 		if (hasFoodAt(_me.xindex + 1, _me.yindex))
-			drawMarker(xcorner[4] + dx, ycorner[4] - dy, foodR, lineColor);
+			drawMiniMapMarker(xcorner[4] + dx, ycorner[4] - dy, foodR, lineColor, isMac);
 		if (hasRobotAt(_me.xindex + 1, _me.yindex))
-			drawMarker(xcorner[4] + dx, ycorner[4] - dy, robotR, lineColor);
+			drawMiniMapMarker(xcorner[4] + dx, ycorner[4] - dy, robotR, lineColor, isMac);
 	}
 }
 


Commit: 6712d35cef1ac0b77d8801fff6e563b2ecbeb8c5
    https://github.com/scummvm/scummvm/commit/6712d35cef1ac0b77d8801fff6e563b2ecbeb8c5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:44+02:00

Commit Message:
COLONY: improved DOS UI

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 1399075d115..553e445e59c 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -199,6 +199,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_me.power[0] = 256; // weapons power
 	_me.power[1] = 256; // life power
 	_me.power[2] = 256; // armor power
+	updateDOSPowerBars();
 
 	// Animation system init
 	_backgroundMask = nullptr;
@@ -672,6 +673,7 @@ void ColonyEngine::startNewGame() {
 	_me.power[0] = 256;
 	_me.power[1] = 256;
 	_me.power[2] = 256;
+	updateDOSPowerBars();
 
 	for (int i = 0; i < 4; i++)
 		_decode1[i] = _decode2[i] = _decode3[i] = 0;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 1db807d063c..7de15760718 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -464,6 +464,7 @@ private:
 	int _coreState[2];
 	int _coreHeight[2];
 	int _corePower[3];
+	int _epower[3];   // log2 display levels for power bars (from qlog)
 	int _coreIndex;
 	int _orbit = 0;
 	int _armor = 0;
@@ -526,9 +527,15 @@ private:
 	Common::Rect _clip;
 	Common::Rect _screenR;
 	Common::Rect _dashBoardRect;
-	Common::Rect _compassRect;
-	Common::Rect _headsUpRect;
-	Common::Rect _powerRect;
+	Common::Rect _compassRect;   // DOS: compOval (after shrink); Mac: moveWindow
+	Common::Rect _headsUpRect;   // DOS: floorRect; Mac: minimap inside moveWindow
+	Common::Rect _powerRect;     // DOS: powerRect; Mac: infoWindow
+
+	// DOS dashboard layout (from original MetaWINDOW pix_per_Qinch values)
+	int _pQx;           // pixels per quarter-inch X (24 for EGA 640x350)
+	int _pQy;           // pixels per quarter-inch Y (18 for EGA 640x350)
+	int _powerWidth;     // width of each of the 3 power bar columns
+	int _powerHeight;    // pixel height per power bar unit (max 5)
 
 	// Cached decoded PICT surfaces for dashboard panels (Mac color mode)
 	Graphics::Surface *_pictPower = nullptr;      // PICT -32755 (normal) or -32760 (trouble)
@@ -612,6 +619,9 @@ private:
 	void updateViewportLayout();
 	void drawDashboardStep1();
 	void drawDashboardMac();
+	void drawDOSBarGraph(int x, int y, int height);
+	void updateDOSPowerBars();
+	static int qlog(int32 x);
 	void drawMiniMapMarker(int x, int y, int halfSize, uint32 color, bool isMac);
 	bool hasRobotAt(int x, int y) const;
 	bool hasFoodAt(int x, int y) const;
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index ee6511cd5a3..342ec633e61 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -296,6 +296,8 @@ void ColonyEngine::setPower(int p0, int p1, int p2) {
 			_level, _me.xindex, _me.yindex);
 	}
 
+	updateDOSPowerBars();
+
 	if (_me.power[1] <= 0) {
 		debugC(1, kColonyDebugUI, "Player died! power=[%d,%d,%d]",
 			(int)_me.power[0], (int)_me.power[1], (int)_me.power[2]);
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 33fa745be25..809f1fb5916 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -245,22 +245,29 @@ void ColonyEngine::updateViewportLayout() {
 		return Common::Rect(left, top, right, bottom);
 	};
 
+	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+
+	// Original IBM_INIT.C: pix_per_Qinch = pixResX/4, pixResY/4
+	// MetaWINDOW EGA 640x350: pixResX=96, pixResY=72 → pQx=24, pQy=18
+	_pQx = 24;
+	_pQy = 18;
+
 	int dashWidth = 0;
 	if (_showDashBoard) {
-		// Mac mode: original inits.c sets screenR.left=96  fixed 96px sidebar.
-		// Two floating windows (infoWindow + moveWindow) centered in sidebar.
-		const bool isMac = (_renderMode == Common::kRenderMacintosh);
 		if (isMac)
 			dashWidth = MIN(96, _width / 2);
 		else
-			dashWidth = CLIP<int>(_width / 6, 72, 140);
+			dashWidth = 4 * _pQx + 2; // DASHBOAR.C: DashBoard.right = 4*pix_per_Qinch_x+2
 		if (_width - dashWidth < 160)
 			dashWidth = 0;
 	}
 
 	const int menuTop = _menuBarHeight; // 0 for DOS/EGA, 20 for Mac
 
-	_screenR = makeSafeRect(dashWidth, menuTop, _width, _height);
+	// Original IBM_INIT.C: screenR.left = sR.left + 4*pix_per_Qinch_x + 4
+	// This creates a 2px gap between dashboard right edge and viewport left edge.
+	const int viewportLeft = isMac ? dashWidth : (dashWidth > 0 ? 4 * _pQx + 4 : 0);
+	_screenR = makeSafeRect(viewportLeft, menuTop, _width, _height);
 	_clip = _screenR;
 	_centerX = (_screenR.left + _screenR.right) >> 1;
 	_centerY = (_screenR.top + _screenR.bottom) >> 1;
@@ -273,9 +280,7 @@ void ColonyEngine::updateViewportLayout() {
 		return;
 	}
 
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
 	const int pad = 2;
-	const int topPad = menuTop + pad;
 
 	if (isMac) {
 		// Original Mac layout from inits.c/compass.c/power.c:
@@ -331,22 +336,83 @@ void ColonyEngine::updateViewportLayout() {
 		const int infoTop = menuTop + pad;
 		_powerRect = makeSafeRect(infoLeft, infoTop, infoLeft + infoW, infoTop + infoH);
 	} else {
-		const int blockLeft = pad;
-		const int blockRight = dashWidth - pad;
-		const int unit = MAX(8, (dashWidth - (pad * 2)) / 4);
-
-		const int compassBottom = _height - MAX(2, unit / 4);
-		const int compassTop = MAX(topPad, compassBottom - unit * 4);
-		_compassRect = makeSafeRect(blockLeft, compassTop, blockRight, compassBottom);
+		// DASHBOAR.C RCompass(): compOval before shrink
+		// compOval.bottom = r->bottom - (pQy >> 2)
+		// compOval.top = r->bottom - 4*pQy
+		// compOval.left = 2, compOval.right = 4*pQx
+		// Then shrink by 2px each side for EraseOval
+		const int compBottom = _height - (_pQy >> 2) - 2;  // after shrink
+		const int compTop = _height - 4 * _pQy + 2;        // after shrink
+		const int compLeft = 2 + 2;                          // after shrink
+		const int compRight = 4 * _pQx - 2;                  // after shrink
+		_compassRect = makeSafeRect(compLeft, compTop, compRight, compBottom);
+
+		// DASHBOAR.C RHeadsUp(): floorRect uses compOval.top (after shrink)
+		// floorRect.bottom = (compOval.top - 4) - (pQy >> 2)
+		// floorRect.top = (compOval.top - 4) - 4*pQy
+		// floorRect.left = 2, floorRect.right = r->right - 2
+		const int floorBottom = (compTop - 4) - (_pQy >> 2);
+		const int floorTop = (compTop - 4) - 4 * _pQy;
+		_headsUpRect = makeSafeRect(2, MAX(0, floorTop), dashWidth - 2, MAX(0, floorBottom));
+
+		// DASHBOAR.C RPower(): powerRect layout
+		// wd = r->right - r->left = dashWidth
+		// twd = wd/3; twd--; twd>>=1; twd<<=1 (round down to even)
+		// powerWidth = twd; twd *= 3; l = (wd - twd) >> 1
+		int twd = dashWidth / 3;
+		twd--;
+		twd >>= 1;
+		twd <<= 1;
+		_powerWidth = twd;
+		const int totalBarWidth = twd * 3;
+		const int powerLeft = (dashWidth - totalBarWidth) >> 1;
+
+		// powerRect.top = r->top + 8
+		// powerRect.bottom = r->bottom - 2*(4*pQy + 8)
+		const int pTop = 8;
+		const int pBottom = _height - 2 * (4 * _pQy + 8);
+		_powerHeight = (pBottom - pTop) / 32;
+		_powerHeight = MIN(_powerHeight, 5);
+		const int pBottomAdj = pTop + _powerHeight * 32;
+		_powerRect = makeSafeRect(powerLeft, pTop, powerLeft + totalBarWidth, pBottomAdj);
+	}
+}
 
-		const int headsUpBottom = _compassRect.top - MAX(2, unit / 4) - 4;
-		const int headsUpTop = headsUpBottom - unit * 4;
-		_headsUpRect = makeSafeRect(blockLeft, MAX(topPad, headsUpTop), blockRight, MAX(topPad, headsUpBottom));
+// DASHBOAR.C qlog(): bit-length of x (equivalent to floor(log2(x))+1)
+int ColonyEngine::qlog(int32 x) {
+	int i = 0;
+	while (x > 0) {
+		x >>= 1;
+		i++;
+	}
+	return i;
+}
 
-		_powerRect = makeSafeRect(blockLeft, topPad, blockRight, _headsUpRect.top - 4);
+// DASHBOAR.C DrawBarGraph(): draw a single power bar column
+void ColonyEngine::drawDOSBarGraph(int x, int y, int height) {
+	int color;
+	if (height <= 3)
+		color = 4; // vRED
+	else
+		color = 1; // vBLUE
+
+	for (int i = 0; i < height; i++) {
+		for (int j = 0; j < _powerHeight - 1; j++) {
+			int h = y - (2 + i * _powerHeight + j);
+			if (h < _powerRect.top)
+				return;
+			// First line of each segment is black (separator), rest is color
+			_gfx->drawLine(x + 2, h, x + _powerWidth - 2, h, (j == 0) ? 0 : color);
+		}
 	}
 }
 
+// DASHBOAR.C SetPower() display update: recompute epower[] from Me.power[]
+void ColonyEngine::updateDOSPowerBars() {
+	for (int i = 0; i < 3; i++)
+		_epower[i] = qlog(_me.power[i]);
+}
+
 void ColonyEngine::drawDashboardStep1() {
 	if (_dashBoardRect.width() <= 0 || _dashBoardRect.height() <= 0)
 		return;
@@ -358,54 +424,85 @@ void ColonyEngine::drawDashboardStep1() {
 		return;
 	}
 
-	// --- DOS/EGA path (unchanged) ---
-	_gfx->fillRect(_dashBoardRect, 0);
-	_gfx->fillDitherRect(_dashBoardRect, 0, 15);
-	_gfx->drawRect(_dashBoardRect, 1);
-
-	const int shiftX = MAX(1, _dashBoardRect.width() / 8);
-	const int shiftY = MAX(1, _dashBoardRect.width() / 8);
-	const Common::Rect r = _dashBoardRect;
+	// --- DOS/EGA path ---
+	// Matches original DASHBOAR.C DrawDashBoard() → RConsole + RCompass + RHeadsUp + RPower
 
-	_gfx->drawLine(r.left, r.top, r.left + shiftX, r.top + shiftY, 0);
-	_gfx->drawLine(r.right - 1, r.top, r.right - 1 - shiftX, r.top + shiftY, 0);
-	_gfx->drawLine(r.left, r.bottom - 1, r.left + shiftX, r.bottom - 1 - shiftY, 0);
-	_gfx->drawLine(r.right - 1, r.bottom - 1, r.right - 1 - shiftX, r.bottom - 1 - shiftY, 0);
-
-	const Common::Rect tr(r.left + shiftX, r.top + shiftY, r.right - shiftX, r.bottom - shiftY);
-	_gfx->fillDitherRect(tr, 0, 15);
-	_gfx->drawRect(tr, 0);
-
-	if (_compassRect.width() > 4 && _compassRect.height() > 4) {
+	// DrawDashBoard(): separator line at screenR.left-1 in vBLACK
+	if (_screenR.left > 0)
+		_gfx->drawLine(_screenR.left - 1, _screenR.top, _screenR.left - 1, _screenR.bottom - 1, 0);
+
+	// RConsole(): fill dashboard with pattern, frame in vBLUE
+	// FillRect(r, 3) with BackColor(vWHITE) = dither pattern using black+white
+	_gfx->fillDitherRect(_dashBoardRect, 0, 7);
+	_gfx->drawRect(_dashBoardRect, 1); // vBLUE frame
+
+	// RCompass(): draw compass oval
+	// _compassRect stores the post-shrink compOval (inner erasable area)
+	if (_compassRect.width() > 2 && _compassRect.height() > 2) {
+		// Original draws FillOval on the pre-shrink rect, then EraseOval on the shrunk rect.
+		// Pre-shrink = _compassRect expanded by 2 on each side
 		const int cx = (_compassRect.left + _compassRect.right) >> 1;
 		const int cy = (_compassRect.top + _compassRect.bottom) >> 1;
-		const int rx = MAX(2, (_compassRect.width() - 6) >> 1);
-		const int ry = MAX(2, (_compassRect.height() - 6) >> 1);
-
-		_gfx->fillEllipse(cx, cy, rx, ry, 15);
-		_gfx->drawEllipse(cx, cy, rx, ry, 1);
-
-		const int ex = cx + ((_cost[_me.look] * rx) >> 8);
-		const int ey = cy - ((_sint[_me.look] * ry) >> 8);
-		_gfx->drawLine(cx, cy, ex, ey, 0);
-		_gfx->drawLine(cx - 2, cy, cx + 2, cy, 0);
-		_gfx->drawLine(cx, cy - 2, cx, cy + 2, 0);
+		const int outerRx = (_compassRect.width() + 4) >> 1;
+		const int outerRy = (_compassRect.height() + 4) >> 1;
+		const int innerRx = _compassRect.width() >> 1;
+		const int innerRy = _compassRect.height() >> 1;
+
+		// FillOval: filled oval (vINTWHITE background)
+		_gfx->fillEllipse(cx, cy, outerRx, outerRy, 15);
+		// EraseOval: clear interior (also vINTWHITE)
+		_gfx->fillEllipse(cx, cy, innerRx, innerRy, 15);
+		// Frame the outer oval
+		_gfx->drawEllipse(cx, cy, outerRx, outerRy, 0);
+
+		// Compass needle: updateDashBoard() uses Me.ang
+		const int ex = cx + ((_cost[_me.ang] * innerRx) >> 8);
+		const int ey = cy - ((_sint[_me.ang] * innerRy) >> 8);
+		_gfx->drawLine(cx, cy, ex, ey, 0); // vBLACK needle
 	}
 
-	if (_screenR.left > 0)
-		_gfx->drawLine(_screenR.left - 1, _screenR.top, _screenR.left - 1, _screenR.bottom - 1, 15);
-
-	if (_headsUpRect.width() > 4 && _headsUpRect.height() > 4) {
-		_gfx->fillRect(_headsUpRect, 15);
-		_gfx->drawRect(_headsUpRect, 1);
+	// RHeadsUp(): minimap
+	if (_headsUpRect.width() > 2 && _headsUpRect.height() > 2) {
+		_gfx->fillRect(_headsUpRect, 15); // EraseRect (vINTWHITE)
+		_gfx->drawRect(_headsUpRect, 0);  // FrameRect (vBLACK)
 		drawMiniMap(0);
 	}
 
-	if (_powerRect.width() > 4 && _powerRect.height() > 4) {
-		_gfx->fillRect(_powerRect, 15);
-		_gfx->drawRect(_powerRect, 1);
-		const int barY = _powerRect.bottom - MAX(3, _powerRect.width() / 8);
-		_gfx->drawLine(_powerRect.left + 1, barY, _powerRect.right - 2, barY, 0);
+	// RPower(): power bars (only when armored or armed)
+	if (_powerRect.width() > 2 && _powerRect.height() > 2 && (_armor > 0 || _weapons > 0)) {
+		_gfx->fillRect(_powerRect, 15); // EraseRect (vINTWHITE)
+		_gfx->drawRect(_powerRect, 0);  // FrameRect (vBLACK)
+
+		// Vertical dividers between 3 columns
+		const int pl = _powerRect.left;
+		_gfx->drawLine(pl + _powerWidth, _powerRect.bottom - 1, pl + _powerWidth, _powerRect.top, 0);
+		_gfx->drawLine(pl + _powerWidth * 2, _powerRect.bottom - 1, pl + _powerWidth * 2, _powerRect.top, 0);
+
+		// Horizontal divider above symbol area
+		_gfx->drawLine(pl, _powerRect.bottom - _powerWidth - 1, _powerRect.right - 1, _powerRect.bottom - _powerWidth - 1, 0);
+
+		// Symbol 1: triangle (weapon power)
+		_gfx->drawLine(pl + 2, _powerRect.bottom - 2, pl + _powerWidth - 2, _powerRect.bottom - 2, 0);
+		_gfx->drawLine(pl + _powerWidth - 2, _powerRect.bottom - 2, pl + (_powerWidth >> 1), _powerRect.bottom - (_powerWidth - 2), 0);
+		_gfx->drawLine(pl + (_powerWidth >> 1), _powerRect.bottom - (_powerWidth - 2), pl + 2, _powerRect.bottom - 2, 0);
+
+		// Symbol 2: diamond (life power)
+		const int d2l = pl + _powerWidth;
+		_gfx->drawLine(d2l + 1, _powerRect.bottom - (_powerWidth >> 1), d2l + (_powerWidth >> 1), _powerRect.bottom - (_powerWidth - 1), 0);
+		_gfx->drawLine(d2l + (_powerWidth >> 1), _powerRect.bottom - (_powerWidth - 1), d2l + _powerWidth - 1, _powerRect.bottom - (_powerWidth >> 1), 0);
+		_gfx->drawLine(d2l + _powerWidth - 1, _powerRect.bottom - (_powerWidth >> 1), d2l + (_powerWidth >> 1), _powerRect.bottom - 1, 0);
+		_gfx->drawLine(d2l + (_powerWidth >> 1), _powerRect.bottom - 1, d2l + 1, _powerRect.bottom - (_powerWidth >> 1), 0);
+
+		// Symbol 3: inverted triangle (shield power)
+		const int d3l = pl + 2 * _powerWidth;
+		_gfx->drawLine(d3l + 2, _powerRect.bottom - (_powerWidth - 2), d3l + _powerWidth - 2, _powerRect.bottom - (_powerWidth - 2), 0);
+		_gfx->drawLine(d3l + _powerWidth - 2, _powerRect.bottom - (_powerWidth - 2), d3l + (_powerWidth >> 1), _powerRect.bottom - 2, 0);
+		_gfx->drawLine(d3l + (_powerWidth >> 1), _powerRect.bottom - 2, d3l + 2, _powerRect.bottom - (_powerWidth - 2), 0);
+
+		// Draw power bar graphs
+		drawDOSBarGraph(pl, _powerRect.bottom - (_powerWidth + 1), _epower[0]);
+		drawDOSBarGraph(pl + _powerWidth, _powerRect.bottom - (_powerWidth + 1), _epower[1]);
+		drawDOSBarGraph(pl + _powerWidth * 2, _powerRect.bottom - (_powerWidth + 1), _epower[2]);
 	}
 }
 
@@ -665,6 +762,17 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 		if (hasRobotAt(_me.xindex + 1, _me.yindex))
 			drawMiniMapMarker(xcorner[4] + dx, ycorner[4] - dy, robotR, lineColor, isMac);
 	}
+
+	// DASHBOAR.C DrawHeadsUp(): player eye icon at minimap center
+	// Outer oval: FrameOval ±(pQx/2, pQy/4) = ±(12, 4) for EGA
+	// Inner oval (pupil): FillOval ±(pQx/4, pQy/4) = ±(6, 4) for EGA
+	if (!isMac) {
+		const int px = _pQx >> 1;  // 12
+		const int py = _pQy >> 2;  // 4
+		_gfx->drawEllipse(ccenterx, ccentery, px, py, lineColor);
+		const int px2 = _pQx >> 2; // 6
+		_gfx->fillEllipse(ccenterx, ccentery, px2, py, lineColor);
+	}
 }
 
 void ColonyEngine::drawCrosshair() {
@@ -692,8 +800,11 @@ void ColonyEngine::drawCrosshair() {
 
 	const int cx = _centerX;
 	const int cy = _centerY;
-	const int qx = MAX(2, _screenR.width() / 32);
-	const int qy = MAX(2, _screenR.height() / 32);
+
+	// Original IBM_DISP.C: uses pix_per_Qinch for crosshair sizing
+	// pix_per_Finch = (pix_per_Qinch * 3) >> 1  (1.5x quarter-inch)
+	const int qx = isMac ? MAX(2, _screenR.width() / 32) : _pQx;
+	const int qy = isMac ? MAX(2, _screenR.height() / 32) : _pQy;
 	const int fx = (qx * 3) >> 1;
 	const int fy = (qy * 3) >> 1;
 	auto drawCrossLine = [&](int x1, int y1, int x2, int y2) {
@@ -702,17 +813,23 @@ void ColonyEngine::drawCrosshair() {
 	};
 
 	if (_weapons > 0) {
+		// Original IBM_DISP.C: two bracket shapes (left + right)
+		// insight: inner edge at pQx, outer edge at pFinch_x, height pQy..pFinch_y
+		// normal: inner edge at pQx, outer edge at pFinch_x, height pFinch_y
 		const int yTop = _insight ? (cy - qy) : (cy - fy);
 		const int yBottom = _insight ? (cy + qy) : (cy + fy);
 
+		// Left bracket: top-left corner down to bottom-left
 		drawCrossLine(cx - qx, yTop, cx - fx, yTop);
 		drawCrossLine(cx - fx, yTop, cx - fx, yBottom);
 		drawCrossLine(cx - fx, yBottom, cx - qx, yBottom);
+		// Right bracket: top-right corner down to bottom-right
 		drawCrossLine(cx + qx, yTop, cx + fx, yTop);
 		drawCrossLine(cx + fx, yTop, cx + fx, yBottom);
 		drawCrossLine(cx + fx, yBottom, cx + qx, yBottom);
 		_insight = false;
 	} else {
+		// Original IBM_DISP.C: simple cross ±pix_per_Qinch from center
 		drawCrossLine(cx - qx, cy, cx + qx, cy);
 		drawCrossLine(cx, cy - qy, cx, cy + qy);
 	}


Commit: 02c393e3ef6ccb51e09c9fed0d548c789d78f5ec
    https://github.com/scummvm/scummvm/commit/02c393e3ef6ccb51e09c9fed0d548c789d78f5ec
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:45+02:00

Commit Message:
COLONY: corepower fixes

Changed paths:
    engines/colony/animation.cpp
    engines/colony/interaction.cpp
    engines/colony/render.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 1c49759f1ac..9f4d94d208a 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -1160,8 +1160,6 @@ void ColonyEngine::handleSuitClick(int item) {
 		setObjectState(3, _armor + 1); // display state
 		drawAnimation();
 		_gfx->copyToScreen();
-		if (_armor == 3 && _weapons == 3)
-			_corePower[_coreIndex] = 2;
 	} else if (item == 2) { // Weapons
 		if (_weapons == 3) {
 			for (int i = 6; i >= 1; i--) {
@@ -1183,8 +1181,6 @@ void ColonyEngine::handleSuitClick(int item) {
 		setObjectState(4, _weapons + 1);
 		drawAnimation();
 		_gfx->copyToScreen();
-		if (_armor == 3 && _weapons == 3)
-			_corePower[_coreIndex] = 2;
 	}
 }
 
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 342ec633e61..34a9cc023d6 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -81,6 +81,9 @@ void ColonyEngine::interactWithObject(int objNum) {
 		}
 		break;
 	case kObjPowerSuit:
+		// GANIMATE.C DoPowerSuit(): if(!corepower[coreindex])return;
+		if (!_corePower[_coreIndex])
+			break;
 		if (loadAnimation("suit"))
 			playAnimation();
 		break;
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index c2c51b1c39a..9a0265bfec3 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -876,9 +876,10 @@ void ColonyEngine::renderCorridor3D() {
 			ceilColor = wallFill;
 		}
 	} else {
-		// Mac B&W: walls are pure white (c_dwall=WHITE); EGA: light gray (7)
-		wallFill = lit ? (macMode ? 255 : 7) : 0;
-		wallLine = lit ? 0 : (macMode ? 255 : 7);
+		// IBM_DISP.C: lit → BackColor(vWHITE)=15, color_wall=vwall_Light=0 (black lines on white bg)
+		//             dark → BackColor(vBLACK)=0, color_wall=vINTWHITE=15 (white lines on black bg)
+		wallFill = lit ? (macMode ? 255 : 15) : 0;
+		wallLine = lit ? 0 : (macMode ? 255 : 15);
 		floorColor = macMode ? (lit ? 255 : 0) : wallFill;
 		ceilColor  = macMode ? (lit ? 255 : 0) : wallFill;
 	}
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 809f1fb5916..762e0f8a123 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -793,9 +793,9 @@ void ColonyEngine::drawCrosshair() {
 		else
 			color = (_corePower[_coreIndex] > 0) ? packRGB(0, 0, 0) : packRGB(255, 255, 255);
 	} else {
-		color = (_weapons > 0) ? 15 : 7;
-		if (_corePower[_coreIndex] > 0)
-			color = 0;
+		// IBM_DISP.C: powered → color_cursor=realcolor[vBLACK]=0
+		//             unpowered → color_cursor=realcolor[vWHITE]=15
+		color = (_corePower[_coreIndex] > 0) ? 0 : 15;
 	}
 
 	const int cx = _centerX;


Commit: 85a8dab31a21a681daf8822999606986dc9cb32e
    https://github.com/scummvm/scummvm/commit/85a8dab31a21a681daf8822999606986dc9cb32e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:45+02:00

Commit Message:
COLONY: mac palette inverted in animations

Changed paths:
    engines/colony/animation.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 9f4d94d208a..d54653b8e81 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -641,12 +641,14 @@ void ColonyEngine::drawAnimation() {
 			}
 		}
 	} else {
+		// Mac QuickDraw FillRect: pattern bit=1 → ForeColor (black=0),
+		// bit=0 → BackColor (white=15). Same for B&W Mac and DOS.
 		for (int y = 0; y < 264; y++) {
 			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
 			byte row = pat[y % 8];
 			for (int x = 0; x < 416; x++) {
 				bool set = (row & (0x80 >> (x % 8))) != 0;
-				_gfx->setPixel(ox + x, oy + y, set ? 15 : 0);
+				_gfx->setPixel(ox + x, oy + y, set ? 0 : 15);
 			}
 		}
 	}
@@ -712,11 +714,11 @@ void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y, uin
 	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh);
 	// Mac QuickDraw srcBic+srcOr rendering:
 	//   mask bit=1 -> opaque (part of sprite)
-	//   fg bit=1   -> ForeColor (black outline)
+	//   fg bit=1   -> ForeColor (black)
 	//   fg bit=0   -> BackColor (fillColor from BMColor)
-	// B&W/DOS fallback preserves existing palette-index behavior.
-	const uint32 fgColor = useColor ? (uint32)0xFF000000 : 15;
-	const uint32 bgColor = useColor ? fillColor : 0;
+	// B&W/DOS: same semantics — fg bit=1 is black (0), fg bit=0 is white (15).
+	const uint32 fgColor = useColor ? (uint32)0xFF000000 : 0;
+	const uint32 bgColor = useColor ? fillColor : 15;
 
 	for (int iy = 0; iy < img->height; iy++) {
 		for (int ix = 0; ix < img->width; ix++) {


Commit: 76af8ad65c871f1656ca5dda7c09cdda0601d667
    https://github.com/scummvm/scummvm/commit/76af8ad65c871f1656ca5dda7c09cdda0601d667
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:45+02:00

Commit Message:
COLONY: add periodic power drain timer and minimap bounds guards

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 553e445e59c..db014c343ac 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -1060,6 +1060,21 @@ Common::Error ColonyEngine::run() {
 			drawDashboardStep1();
 			drawCrosshair();
 		} else {
+			// Periodic power drain every FCOUNT(32) frames.
+			// Mac display.c implements this; DOS IBM_DISP.C defines FCOUNT but
+			// never applies the drain — a bug in the original DOS port that left
+			// equipment upgrades without any energy cost, removing the intended
+			// risk/reward trade-off. We apply the drain on both platforms.
+			// Formula: SetPower(-level*armor2, -level*(armor2+weapons2), -level*weapons2)
+			_foodCount--;
+			if (_foodCount <= 0) {
+				const int a2 = _armor * _armor;
+				const int w2 = _weapons * _weapons;
+				if (a2 > 0 || w2 > 0)
+					setPower(-_level * a2, -_level * (a2 + w2), -_level * w2);
+				_foodCount = 32;
+			}
+
 			_gfx->clear((_corePower[_coreIndex] > 0) ? 15 : 0);
 			corridor();
 			drawDashboardStep1();
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 7de15760718..5d02d603ec4 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -472,6 +472,7 @@ private:
 	uint32 _blackoutColor = 0;
 	uint32 _lastClickTime = 0;
 	uint32 _displayCount = 0; // Frame counter for COLOR wall animation (Mac: count)
+	int _foodCount = 32;      // Mac display.c: periodic power drain counter (FCOUNT=32)
 	uint32 _lastAnimUpdate = 0;
 	uint32 _lastWarningChimeTime = 0;
 	int _action0, _action1;
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 762e0f8a123..11197ebe8ff 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -264,10 +264,11 @@ void ColonyEngine::updateViewportLayout() {
 
 	const int menuTop = _menuBarHeight; // 0 for DOS/EGA, 20 for Mac
 
-	// Original IBM_INIT.C: screenR.left = sR.left + 4*pix_per_Qinch_x + 4
-	// This creates a 2px gap between dashboard right edge and viewport left edge.
+	// DOS IBM_INIT.C: screenR.left = sR.left + 4*pix_per_Qinch_x + 4 (2px gap)
+	// Mac inits.c: screenR.left=96, screenR.bottom=rScreen.bottom-8 (8px bottom margin)
 	const int viewportLeft = isMac ? dashWidth : (dashWidth > 0 ? 4 * _pQx + 4 : 0);
-	_screenR = makeSafeRect(viewportLeft, menuTop, _width, _height);
+	const int viewportBottom = isMac ? (_height - 8) : _height;
+	_screenR = makeSafeRect(viewportLeft, menuTop, _width, viewportBottom);
 	_clip = _screenR;
 	_centerX = (_screenR.left + _screenR.right) >> 1;
 	_centerY = (_screenR.top + _screenR.bottom) >> 1;
@@ -801,12 +802,12 @@ void ColonyEngine::drawCrosshair() {
 	const int cx = _centerX;
 	const int cy = _centerY;
 
-	// Original IBM_DISP.C: uses pix_per_Qinch for crosshair sizing
-	// pix_per_Finch = (pix_per_Qinch * 3) >> 1  (1.5x quarter-inch)
-	const int qx = isMac ? MAX(2, _screenR.width() / 32) : _pQx;
-	const int qy = isMac ? MAX(2, _screenR.height() / 32) : _pQy;
-	const int fx = (qx * 3) >> 1;
-	const int fy = (qy * 3) >> 1;
+	// Mac display.c: fixed ±20/±30 pixel crosshair.
+	// DOS IBM_DISP.C: uses pix_per_Qinch (24/18) and pix_per_Finch (36/27).
+	const int qx = isMac ? 20 : _pQx;
+	const int qy = isMac ? 20 : _pQy;
+	const int fx = isMac ? 30 : ((_pQx * 3) >> 1);
+	const int fy = isMac ? 30 : ((_pQy * 3) >> 1);
 	auto drawCrossLine = [&](int x1, int y1, int x2, int y2) {
 		if (clipLineToRect(x1, y1, x2, y2, _screenR))
 			_gfx->drawLine(x1, y1, x2, y2, color);


Commit: a510a6d5c2ebb9c782903bebf3a637595051b59d
    https://github.com/scummvm/scummvm/commit/a510a6d5c2ebb9c782903bebf3a637595051b59d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:46+02:00

Commit Message:
COLONY: make sure some effects do not run too fast

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index db014c343ac..1d408aab3d1 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -805,6 +805,7 @@ Common::Error ColonyEngine::run() {
 	uint32 lastMoveTick = _system->getMillis();
 	uint32 lastColonyTick = lastMoveTick;
 	uint32 lastBattleTick = lastMoveTick;
+	uint32 lastCenterTick = lastMoveTick;
 	while (!shouldQuit()) {
 		_frameLimiter->startFrame();
 
@@ -824,15 +825,34 @@ Common::Error ColonyEngine::run() {
 			_lastWarningChimeTime = now;
 		}
 
-		if (_gameMode == kModeColony && now - lastColonyTick >= 66) {
+		// Original Mac gmain.c: CThink()+Display() run in one loop with no
+		// throttle — both at the hardware-limited frame rate (~8fps on Mac
+		// Plus). Robot AI, movement, and shooting all happen at this cadence.
+		// 125ms (~8fps) matches the original balance for robot aggression.
+		if (_gameMode == kModeColony && now - lastColonyTick >= 125) {
 			lastColonyTick = now;
 			cThink();
 		}
 
-		// The original battle loop advanced AI on the game loop cadence, not
-		// every rendered frame. Running this at 60 fps makes enemies and
-		// projectiles several times more aggressive than DOS/Mac.
-		if (_gameMode == kModeBattle && now - lastBattleTick >= 66) {
+		// Periodic equipment power drain every FCOUNT(32) game ticks at ~8fps.
+		// Mac display.c implements this; DOS IBM_DISP.C defines FCOUNT but
+		// never applies the drain — a bug in the original DOS port that
+		// removed the intended equipment energy cost trade-off.
+		if (_gameMode == kModeColony && now - lastCenterTick >= 125) {
+			lastCenterTick = now;
+			_foodCount--;
+			if (_foodCount <= 0) {
+				const int a2 = _armor * _armor;
+				const int w2 = _weapons * _weapons;
+				if (a2 > 0 || w2 > 0)
+					setPower(-_level * a2, -_level * (a2 + w2), -_level * w2);
+				_foodCount = 32;
+			}
+		}
+
+		// Original Mac gmain.c: BThink()+Display() both at hardware frame rate.
+		// 125ms (~8fps) matches original Mac Plus battle cadence.
+		if (_gameMode == kModeBattle && now - lastBattleTick >= 125) {
 			lastBattleTick = now;
 			battleThink();
 		}
@@ -1060,21 +1080,6 @@ Common::Error ColonyEngine::run() {
 			drawDashboardStep1();
 			drawCrosshair();
 		} else {
-			// Periodic power drain every FCOUNT(32) frames.
-			// Mac display.c implements this; DOS IBM_DISP.C defines FCOUNT but
-			// never applies the drain — a bug in the original DOS port that left
-			// equipment upgrades without any energy cost, removing the intended
-			// risk/reward trade-off. We apply the drain on both platforms.
-			// Formula: SetPower(-level*armor2, -level*(armor2+weapons2), -level*weapons2)
-			_foodCount--;
-			if (_foodCount <= 0) {
-				const int a2 = _armor * _armor;
-				const int w2 = _weapons * _weapons;
-				if (a2 > 0 || w2 > 0)
-					setPower(-_level * a2, -_level * (a2 + w2), -_level * w2);
-				_foodCount = 32;
-			}
-
 			_gfx->clear((_corePower[_coreIndex] > 0) ? 15 : 0);
 			corridor();
 			drawDashboardStep1();
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 5d02d603ec4..a9060ad61d4 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -473,6 +473,7 @@ private:
 	uint32 _lastClickTime = 0;
 	uint32 _displayCount = 0; // Frame counter for COLOR wall animation (Mac: count)
 	int _foodCount = 32;      // Mac display.c: periodic power drain counter (FCOUNT=32)
+	uint32 _lastHotfootTime = 0;  // Time-gate for HOTFOOT damage (~8fps)
 	uint32 _lastAnimUpdate = 0;
 	uint32 _lastWarningChimeTime = 0;
 	int _action0, _action1;
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 89933ed743f..696f647388d 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -520,6 +520,9 @@ int ColonyEngine::goToDestination(const uint8 *map, Locate *pobject) {
 		playTunnelAirlockEffect();
 
 		if (_orbit || !(_armor || _fl)) {
+			debugC(1, kColonyDebugMove,
+				"Airlock death: no protection (orbit=%d armor=%d fl=%d) at (%d,%d) level=%d",
+				_orbit, _armor, _fl, _me.xindex, _me.yindex, _level);
 			terminateGame(false);
 			return 0;
 		}
@@ -997,15 +1000,25 @@ void ColonyEngine::fallThroughHole() {
 }
 
 void ColonyEngine::checkCenter() {
-	// DOS CCenter(): check if player is standing on a floor hole or hotfoot
+	// DOS CCenter(): check if player is standing on a floor hole or hotfoot.
+	// Called every render frame at 60fps for responsive hole/egg detection.
+	// HOTFOOT damage is time-gated via _hotfootAccum to match the original
+	// Mac Display() cadence (~8fps). The original had no throttle — damage
+	// fired once per Display() call, which was hardware-limited to ~5-8fps
+	// on a Mac Plus.
 	if (_me.xindex < 0 || _me.xindex >= 31 || _me.yindex < 0 || _me.yindex >= 31)
 		return;
 
 	const uint8 cellType = _mapData[_me.xindex][_me.yindex][4][0];
 	if (cellType != 0) {
+		debugC(2, kColonyDebugMove,
+			"checkCenter: cellType=%d at (%d,%d) dest=[%d,%d,%d] level=%d",
+			cellType, _me.xindex, _me.yindex,
+			_mapData[_me.xindex][_me.yindex][4][2],
+			_mapData[_me.xindex][_me.yindex][4][3],
+			_mapData[_me.xindex][_me.yindex][4][4], _level);
 		switch (cellType) {
 		case 1: { // SMHOLEFLR  small floor hole, must be near center
-			// DOS: xcheck=abs(xloc-(xindex<<8)); if(xcheck>64&&xcheck<192)
 			int xcheck = ABS(_me.xloc - (_me.xindex << 8));
 			int ycheck = ABS(_me.yloc - (_me.yindex << 8));
 			if (xcheck > 64 && xcheck < 192 && ycheck > 64 && ycheck < 192)
@@ -1015,13 +1028,21 @@ void ColonyEngine::checkCenter() {
 		case 2: // LGHOLEFLR  large floor hole, full cell
 			fallThroughHole();
 			break;
-		case 5: // HOTFOOT  electric floor, damages power
-			_sound->play(Sound::kBzzz);
-			debugC(1, kColonyDebugCombat,
-				"hotfoot: level=%d cell=(%d,%d) delta=[%d,%d,%d]",
-				_level, _me.xindex, _me.yindex, -(5 << _level), -(5 << _level), -(5 << _level));
-			setPower(-(5 << _level), -(5 << _level), -(5 << _level));
+		case 5: { // HOTFOOT  electric floor, damages power
+			// Time-gate damage: accumulate ms, fire at ~8fps (125ms intervals)
+			uint32 now = _system->getMillis();
+			uint32 elapsed = now - _lastHotfootTime;
+			if (elapsed >= 125) {
+				_lastHotfootTime = now;
+				_sound->play(Sound::kBzzz);
+				debugC(1, kColonyDebugCombat,
+					"hotfoot: level=%d cell=(%d,%d) delta=[%d,%d,%d]",
+					_level, _me.xindex, _me.yindex,
+					-(5 << _level), -(5 << _level), -(5 << _level));
+				setPower(-(5 << _level), -(5 << _level), -(5 << _level));
+			}
 			break;
+		}
 		default:
 			break;
 		}


Commit: 6ffe27e77d8d570ab9d03c73785338a5da680aeb
    https://github.com/scummvm/scummvm/commit/6ffe27e77d8d570ab9d03c73785338a5da680aeb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:46+02:00

Commit Message:
COLONY: powersuit ui in mac b&w

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 1d408aab3d1..5c368d12859 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -720,9 +720,13 @@ Common::Error ColonyEngine::run() {
 				warning("Failed to open Colony resource fork");
 			}
 		}
-		// Try to open Color Colony for additional color PICT resources
+		// Open Color Colony resource fork for shared PICT resources.
+		// The B&W Colony app references PICTs (-32752, -32757) that only
+		// exist in the Color Colony resource fork. Both apps were always
+		// distributed together — the B&W code dereferences GetPicture()
+		// without null checks, so these PICTs are required, not optional.
 		if (!_colorResMan->open("(Color) Colony")) {
-			debugC(1, kColonyDebugRender, "Color Colony resource fork not found (optional)");
+			warning("Color Colony resource fork not found — dashboard PICTs may be missing");
 		}
 		loadMacCursorResources();
 		_sound->init();
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 11197ebe8ff..258c677b9b6 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -178,8 +178,10 @@ Graphics::Surface *ColonyEngine::loadPictSurface(int resID) {
 		pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
 	}
 
-	if (!pictStream)
+	if (!pictStream) {
+		warning("loadPictSurface(%d): PICT resource not found", resID);
 		return nullptr;
+	}
 
 	::Image::PICTDecoder decoder;
 	Graphics::Surface *result = nullptr;
@@ -187,19 +189,36 @@ Graphics::Surface *ColonyEngine::loadPictSurface(int resID) {
 		const Graphics::Surface *src = decoder.getSurface();
 		if (src) {
 			// Convert to a persistent RGB surface (decoder surface is transient)
+			// Detect 1-bit B&W PICTs where ScummVM's PICTDecoder inverts
+			// the Mac QuickDraw convention. In QuickDraw, 1-bit bitmaps
+			// use bit 0 = BackColor (white) and bit 1 = ForeColor (black).
+			// The PICTDecoder maps index 0 → black instead of white.
+			// We detect this by checking for CLUT8 with a tiny palette
+			// (0-2 entries, typical of 1-bit PICTs) and invert.
+			bool invert1bit = false;
+			if (src->format == Graphics::PixelFormat::createFormatCLUT8()) {
+				const Graphics::Palette &checkPal = decoder.getPalette();
+				invert1bit = ((int)checkPal.size() <= 2);
+			}
+
 			result = new Graphics::Surface();
 			result->create(src->w, src->h, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
 			for (int y = 0; y < src->h; y++) {
 				for (int x = 0; x < src->w; x++) {
 					byte r, g, b;
 					if (src->format == Graphics::PixelFormat::createFormatCLUT8()) {
-						// For CLUT8, use the decoder's palette
 						byte idx = *((const byte *)src->getBasePtr(x, y));
-						const Graphics::Palette &pal = decoder.getPalette();
-						if (idx < (int)pal.size()) {
-							pal.get(idx, r, g, b);
+						if (invert1bit) {
+							// QuickDraw 1-bit: index 0 = white, index 1 = black
+							byte lum = (idx == 0) ? 255 : 0;
+							r = g = b = lum;
 						} else {
-							r = g = b = 0;
+							const Graphics::Palette &pal = decoder.getPalette();
+							if (idx < (int)pal.size()) {
+								pal.get(idx, r, g, b);
+							} else {
+								r = g = b = 0;
+							}
 						}
 					} else {
 						uint32 pixel = src->getPixel(x, y);
@@ -208,7 +227,7 @@ Graphics::Surface *ColonyEngine::loadPictSurface(int resID) {
 					result->setPixel(x, y, result->format.ARGBToColor(255, r, g, b));
 				}
 			}
-			debugC(1, kColonyDebugUI, "loadPictSurface(%d): %dx%d", resID, result->w, result->h);
+			warning("loadPictSurface(%d): %dx%d invert1bit=%d", resID, result->w, result->h, invert1bit ? 1 : 0);
 		}
 	}
 	delete pictStream;
@@ -297,14 +316,17 @@ void ColonyEngine::updateViewportLayout() {
 		if (!_pictCompass)
 			_pictCompass = loadPictSurface(-32757);
 		if (!_pictPower) {
-			// power.c: !armor → FindDepth()>=8 ? -32761 (color) : -32752 (B&W)
+			// power.c DrawInfo(): armor → -32755/-32760; !armor → -32752/-32761
+			// In B&W, -32752 doesn't exist. The original GetPicture returns null
+			// and DrawPicture is a no-op — the panel stays blank until armor > 0.
+			// Only fall back to -32755 when armored.
 			int wantID;
 			if (_armor > 0)
 				wantID = -32755;
 			else
 				wantID = _hasMacColors ? -32761 : -32752;
 			_pictPower = loadPictSurface(wantID);
-			if (!_pictPower && wantID != -32755)
+			if (!_pictPower && _armor > 0 && wantID != -32755)
 				_pictPower = loadPictSurface(-32755);
 			_pictPowerID = _pictPower ? wantID : 0;
 		}
@@ -555,7 +577,7 @@ void ColonyEngine::drawDashboardMac() {
 		else
 			wantPictID = macColor ? -32761 : -32752;
 
-		// Reload PICT if state changed (fall back to -32755 if variant missing)
+		// Reload PICT if state changed (fall back to -32755 only when armored)
 		if (_pictPowerID != wantPictID) {
 			if (_pictPower) {
 				_pictPower->free();
@@ -563,12 +585,14 @@ void ColonyEngine::drawDashboardMac() {
 				_pictPower = nullptr;
 			}
 			_pictPower = loadPictSurface(wantPictID);
-			if (!_pictPower && wantPictID != -32755)
+			if (!_pictPower && _armor > 0 && wantPictID != -32755)
 				_pictPower = loadPictSurface(-32755);
 			_pictPowerID = wantPictID;
 		}
 
 		// power.c: SetRect(&info, -2, -2, xSize-2, ySize-2); DrawPicture(inf, &info)
+		// 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);
 


Commit: c8739fe2666702ae945db4bf4b721f7708963368
    https://github.com/scummvm/scummvm/commit/c8739fe2666702ae945db4bf4b721f7708963368
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:46+02:00

Commit Message:
COLONY: shooting effects

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


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 9aba10a99b1..aff9e9b7392 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -1043,40 +1043,13 @@ void ColonyEngine::battleShoot() {
 		return;
 
 	_sound->play(Sound::kBang);
-	setPower(-2, 0, 0);
 
+	// DOS BATTLE.C: doShootCircles(cx, cy) then SetPower(-2,0,0)
 	const Common::Point aim = getAimPoint();
 	const int cx = aim.x;
 	const int cy = aim.y;
-	_gfx->setXorMode(true);
-	for (int i = 100; i < 900; i += 200) {
-		const int outer = CLIP<int>(kFloor * 128 / i, 0, 1000);
-		const int inner = CLIP<int>(kFloor * 128 / (i + 100), 0, 1000);
-		_gfx->drawLine(cx - outer, cy - outer, cx - inner, cy - inner, 0xFFFFFF);
-		_gfx->drawLine(cx - outer, cy + outer, cx - inner, cy + inner, 0xFFFFFF);
-		_gfx->drawLine(cx + outer, cy + outer, cx + inner, cy + inner, 0xFFFFFF);
-		_gfx->drawLine(cx + outer, cy - outer, cx + inner, cy - inner, 0xFFFFFF);
-		_gfx->drawLine(cx - outer, cy - outer, cx - inner, cy - inner, 0xFFFFFF);
-		_gfx->drawLine(cx - outer, cy + outer, cx - inner, cy + inner, 0xFFFFFF);
-		_gfx->drawLine(cx + outer, cy + outer, cx + inner, cy + inner, 0xFFFFFF);
-		_gfx->drawLine(cx + outer, cy - outer, cx + inner, cy - inner, 0xFFFFFF);
-	}
-	_gfx->copyToScreen();
-	_system->updateScreen();
-	_system->delayMillis(30);
-	for (int i = 100; i < 900; i += 200) {
-		const int outer = CLIP<int>(kFloor * 128 / i, 0, 1000);
-		const int inner = CLIP<int>(kFloor * 128 / (i + 100), 0, 1000);
-		_gfx->drawLine(cx - outer, cy - outer, cx - inner, cy - inner, 0xFFFFFF);
-		_gfx->drawLine(cx - outer, cy + outer, cx - inner, cy + inner, 0xFFFFFF);
-		_gfx->drawLine(cx + outer, cy + outer, cx + inner, cy + inner, 0xFFFFFF);
-		_gfx->drawLine(cx + outer, cy - outer, cx + inner, cy - inner, 0xFFFFFF);
-		_gfx->drawLine(cx - outer, cy - outer, cx - inner, cy - inner, 0xFFFFFF);
-		_gfx->drawLine(cx - outer, cy + outer, cx - inner, cy + inner, 0xFFFFFF);
-		_gfx->drawLine(cx + outer, cy + outer, cx + inner, cy + inner, 0xFFFFFF);
-		_gfx->drawLine(cx + outer, cy - outer, cx + inner, cy - inner, 0xFFFFFF);
-	}
-	_gfx->setXorMode(false);
+	doShootCircles(cx, cy);
+	setPower(-2, 0, 0);
 
 	int bestDist = 11584;
 	int bestIndex = -1;
@@ -1093,6 +1066,10 @@ void ColonyEngine::battleShoot() {
 	if (bestIndex < 0 || bestDist >= 4000)
 		return;
 
+	// DOS BATTLE.C: doBurnHole(&r) with r sized from rtable[dist]
+	int hitRadius = (bestDist > 0) ? CLIP<int>(kFloor * 128 / bestDist, 1, 100) : 50;
+	doBurnHole(cx, cy, hitRadius);
+
 	Locate *target = _battlePwh[bestIndex];
 	if (target->type != kRobCube)
 		return;
@@ -1123,6 +1100,8 @@ void ColonyEngine::battleProjCommand(int xcheck, int ycheck) {
 		debugC(1, kColonyDebugCombat,
 			"battleProjHitPlayer: proj=(%d,%d) player=(%d,%d) delta=[-4,-4,-4]",
 			xcheck, ycheck, _me.xloc, _me.yloc);
+		// Mac battle.c: InvertRect(&Clip) flash when projectile hits player
+		meGetShot();
 		setPower(-4, -4, -4);
 		_sound->play(Sound::kExplode);
 		_projon = false;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index a9060ad61d4..4c2b62e8985 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -583,6 +583,9 @@ private:
 	void setPower(int p0, int p1, int p2);
 	void cShoot();
 	void destroyRobot(int num);
+	void doShootCircles(int cx, int cy);
+	void doBurnHole(int cx, int cy, int radius);
+	void meGetShot();
 
 	// battle.c: outdoor battle system (OpenGL 3D)
 	void battleInit();
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 34a9cc023d6..52d82d363a3 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -321,31 +321,14 @@ void ColonyEngine::cShoot() {
 
 	_sound->play(Sound::kBang);
 
-	// Drain weapons power: -(1 << level) per shot
-	setPower(-(1 << _level), 0, 0);
-
-	// Draw crosshair flash via XOR lines on the 3D viewport
+	// Draw shoot effect then drain power (matching original order)
 	const Common::Point aim = getAimPoint();
 	const int cx = aim.x;
 	const int cy = aim.y;
-	_gfx->setXorMode(true);
-	for (int r = 4; r <= 20; r += 4) {
-		_gfx->drawLine(cx - r, cy - r, cx + r, cy - r, 0xFFFFFF);
-		_gfx->drawLine(cx + r, cy - r, cx + r, cy + r, 0xFFFFFF);
-		_gfx->drawLine(cx + r, cy + r, cx - r, cy + r, 0xFFFFFF);
-		_gfx->drawLine(cx - r, cy + r, cx - r, cy - r, 0xFFFFFF);
-	}
-	_gfx->copyToScreen();
-	_system->updateScreen();
-	_system->delayMillis(30);
-	// XOR again to erase
-	for (int r = 4; r <= 20; r += 4) {
-		_gfx->drawLine(cx - r, cy - r, cx + r, cy - r, 0xFFFFFF);
-		_gfx->drawLine(cx + r, cy - r, cx + r, cy + r, 0xFFFFFF);
-		_gfx->drawLine(cx + r, cy + r, cx - r, cy + r, 0xFFFFFF);
-		_gfx->drawLine(cx - r, cy + r, cx - r, cy - r, 0xFFFFFF);
-	}
-	_gfx->setXorMode(false);
+	doShootCircles(cx, cy);
+
+	// Drain weapons power: -(1 << level) per shot
+	setPower(-(1 << _level), 0, 0);
 
 	int bestIdx = -1;
 	int bestDist = INT_MAX;
@@ -385,6 +368,9 @@ void ColonyEngine::cShoot() {
 		const Thing &target = _objects[bestIdx - 1];
 		debugC(1, kColonyDebugAnimation, "CShoot: hit robot %d (type=%d, dist=%d)",
 			bestIdx, target.type, bestDist);
+		// shoot.c: s0 = rtable[dist]; InvertOval (Mac) / doBurnHole (DOS)
+		int hitRadius = (bestDist > 0) ? CLIP<int>(160 * 128 / bestDist, 1, 100) : 50;
+		doBurnHole(cx, cy, hitRadius);
 		destroyRobot(bestIdx);
 	}
 }
@@ -447,4 +433,186 @@ void ColonyEngine::destroyRobot(int num) {
 	}
 }
 
+// SHOOT.C doShootCircles(): three V-shaped lines (red/white/red) from the
+// 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);
+
+	if (isMac) {
+		// Mac shoot.c CShoot(): patXor diagonal lines radiating from aim point
+		// using rtable[] perspective scaling with PenSize decreasing from 10 to 7.
+		// Simulate thick lines by drawing multiple parallel offsets.
+		auto thickXorLine = [this](int x1, int y1, int x2, int y2, int size) {
+			int half = size / 2;
+			for (int dy = -half; dy <= half; dy++) {
+				for (int dx = -half; dx <= half; dx++) {
+					_gfx->drawLine(x1 + dx, y1 + dy, x2 + dx, y2 + dy, 0xFFFFFF);
+				}
+			}
+		};
+
+		_gfx->setXorMode(true);
+		int psize = 10;
+		for (int i = 100; i < 900; i += 200) {
+			const int s0 = CLIP<int>(160 * 128 / i, 0, 1000);
+			const int s1 = CLIP<int>(160 * 128 / (i + 100), 0, 1000);
+			// Four diagonal ray segments from outer to inner
+			thickXorLine(cx - s0, cy - s0, cx - s1, cy - s1, psize);
+			thickXorLine(cx - s0, cy + s0, cx - s1, cy + s1, psize);
+			thickXorLine(cx + s0, cy + s0, cx + s1, cy + s1, psize);
+			thickXorLine(cx + s0, cy - s0, cx + s1, cy - s1, psize);
+			psize--;
+		}
+		_gfx->copyToScreen();
+		_system->updateScreen();
+		_system->delayMillis(30);
+		// XOR again to erase
+		psize = 10;
+		for (int i = 100; i < 900; i += 200) {
+			const int s0 = CLIP<int>(160 * 128 / i, 0, 1000);
+			const int s1 = CLIP<int>(160 * 128 / (i + 100), 0, 1000);
+			thickXorLine(cx - s0, cy - s0, cx - s1, cy - s1, psize);
+			thickXorLine(cx - s0, cy + s0, cx - s1, cy + s1, psize);
+			thickXorLine(cx + s0, cy + s0, cx + s1, cy + s1, psize);
+			thickXorLine(cx + s0, cy - s0, cx + s1, cy - s1, psize);
+			psize--;
+		}
+		_gfx->setXorMode(false);
+	} else {
+		// DOS SHOOT.C doShootCircles(): three converging V-lines from viewport
+		// bottom corners to aim point (red/white/red) + center oval.
+		_gfx->drawLine(_screenR.left + 1, _screenR.bottom - 1, cx, cy - 1, 4); // vRED
+		_gfx->drawLine(cx, cy - 1, _screenR.right - 2, _screenR.bottom - 1, 4);
+
+		_gfx->drawLine(_screenR.left, _screenR.bottom - 1, cx, cy, 15); // vINTWHITE
+		_gfx->drawLine(cx, cy, _screenR.right - 1, _screenR.bottom - 1, 15);
+
+		_gfx->drawLine(_screenR.left - 1, _screenR.bottom - 1, cx, cy + 1, 4); // vRED
+		_gfx->drawLine(cx, cy + 1, _screenR.right, _screenR.bottom - 1, 4);
+
+		// Small filled oval at aim point
+		_gfx->fillEllipse(cx, cy, 2, 2, 4); // vRED
+
+		_gfx->copyToScreen();
+		_system->updateScreen();
+		_system->delayMillis(30);
+	}
+}
+
+// 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);
+
+	if (isMac) {
+		// Mac: InvertOval at robot bounds
+		_gfx->setXorMode(true);
+		_gfx->fillEllipse(cx, cy, radius, radius, 0xFFFFFF);
+		_gfx->setXorMode(false);
+		_gfx->copyToScreen();
+		_system->updateScreen();
+		_system->delayMillis(50);
+	} else {
+		// DOS: expanding random rays in light blue → yellow → white
+		const int ra = MIN(radius * 2, _pQx * 6);
+		const int d = ra * 2;
+		const int dd = d * 2;
+
+		for (int i = 2; i < dd; i += 1 + (i >> 1)) {
+			const int i2 = i >> 1;
+			int i4 = i2 >> 1;
+			const int i8 = i4 >> 1;
+			const int i16 = i8 >> 1;
+			if (i8 == 0)
+				i4 = 1;
+
+			// Light blue rays
+			for (int k = 0; k < i16; k++) {
+				int tx = (_randomSource.getRandomNumber(i2) - i4);
+				int ty = (_randomSource.getRandomNumber(i2) - i4);
+				if (ABS(tx) + ABS(ty) < i8 * 3)
+					_gfx->drawLine(cx + tx, cy + ty, cx - tx, cy - ty, 9); // vLTBLUE
+			}
+			// Yellow rays
+			for (int k = 0; k < i16; k++) {
+				int tx = (_randomSource.getRandomNumber(i2) - i4);
+				int ty = (_randomSource.getRandomNumber(i2) - i4);
+				if (ABS(tx) + ABS(ty) < (i * 3) >> 3)
+					_gfx->drawLine(cx + tx, cy + ty, cx - tx, cy - ty, 14); // vYELLOW
+			}
+		}
+		// White center core
+		const int i8 = dd >> 4;
+		const int i2 = dd >> 1;
+		for (int k = 0; k < i8; k++) {
+			int tx = (_randomSource.getRandomNumber(i2) / 2 - (dd >> 4));
+			int ty = (_randomSource.getRandomNumber(i2) / 2 - (dd >> 4));
+			if (ABS(tx) + ABS(ty) < (dd * 3) >> 4)
+				_gfx->drawLine(cx + tx, cy + ty, cx - tx, cy - ty, 15); // vINTWHITE
+		}
+		// Center dot
+		_gfx->fillEllipse(cx, cy, _pQx / 8, _pQy / 8, 15);
+		_gfx->copyToScreen();
+		_system->updateScreen();
+		_system->delayMillis(50);
+	}
+}
+
+// SHOOT.C MeGetShot(): XOR'd X-shaped crosshairs scattered across the viewport
+// when a robot shoots the player. Four passes of 40 X-marks each.
+void ColonyEngine::meGetShot() {
+	const int vw = _screenR.width();
+	const int vh = _screenR.height();
+	if (vw <= 0 || vh <= 0)
+		return;
+
+	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+
+	if (isMac) {
+		// Mac shoot.c: InvertRect(&Clip) — full viewport flash
+		_gfx->setXorMode(true);
+		_gfx->fillRect(_screenR, 0xFFFFFF);
+		_gfx->setXorMode(false);
+		_gfx->copyToScreen();
+		_system->updateScreen();
+		_system->delayMillis(30);
+		// Erase flash
+		_gfx->setXorMode(true);
+		_gfx->fillRect(_screenR, 0xFFFFFF);
+		_gfx->setXorMode(false);
+	} else {
+		// DOS SHOOT.C MeGetShot(): 4 passes × 40 XOR'd X-marks at random positions
+		const int qx = _pQx;
+		const int qy = _pQy >> 1;
+		const int qx5 = (_pQx >> 2) * 5;
+
+		_gfx->setXorMode(true);
+		for (int pass = 0; pass < 4; pass++) {
+			for (int i = 0; i < 40; i++) {
+				int x, y;
+				if (pass & 1) {
+					// Passes 1,3: centered half-region
+					x = _randomSource.getRandomNumber(vw / 2) + (vw >> 2);
+					y = _randomSource.getRandomNumber(vh / 2) + (vh >> 2);
+				} else {
+					// Passes 0,2: full region
+					x = _randomSource.getRandomNumber(vw);
+					y = _randomSource.getRandomNumber(vh);
+				}
+				x += _screenR.left;
+				y += _screenR.top;
+				// Draw X-shaped crosshair (3 lines: two diagonals + horizontal)
+				_gfx->drawLine(x - qx, y - qy, x + qx, y + qy, 15);
+				_gfx->drawLine(x - qx5, y, x + qx5, y, 15);
+				_gfx->drawLine(x - qx, y + qy, x + qx, y - qy, 15);
+			}
+		}
+		_gfx->setXorMode(false);
+		_gfx->copyToScreen();
+		_system->updateScreen();
+		_system->delayMillis(50);
+	}
+}
+
 } // End of namespace Colony
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index abdb0a9a282..e96cc31a33f 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -797,6 +797,8 @@ void ColonyEngine::robotShoot(int num) {
 			"robotShoot: robot=%d type=%d cell=(%d,%d) ang=%d delta=[%d,%d,%d] player=(%d,%d)",
 			num, obj.type, obj.where.xindex, obj.where.yindex, obj.where.ang,
 			damage0, damage1, damage2, _me.xindex, _me.yindex);
+		// shoot.c: MeGetShot() — visual feedback when player is hit
+		meGetShot();
 		setPower(damage0, damage1, damage2);
 	}
 }


Commit: 5d868059ead3f33728be6e5759c4439a2414c836
    https://github.com/scummvm/scummvm/commit/5d868059ead3f33728be6e5759c4439a2414c836
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:47+02:00

Commit Message:
COLONY: improve crosshair aiming precision

Changed paths:
    engines/colony/render_objects.cpp


diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 4fcd946da73..5b121ccd8d9 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1078,6 +1078,14 @@ void ColonyEngine::drawStaticObjects() {
 		if (ox < 0 || ox >= 32 || oy < 0 || oy >= 32 || !_visibleCell[ox][oy])
 			continue;
 		drawStaticObjectPrisms3D(obj);
+		// MAKEROBO.C: if shootable robot straddles centerX, set insight
+		// (narrows crosshair brackets to indicate a target is in the line of fire)
+		int t = obj.type;
+		if ((t >= kRobEye && t <= kRobUPyramid) ||
+		    (t >= kRobQueen && t <= kRobSoldier)) {
+			if (obj.where.xmn < _centerX && obj.where.xmx > _centerX)
+				_insight = true;
+		}
 	}
 }
 


Commit: 449ee08621d5773c9e3f7de74ee311bf95146557
    https://github.com/scummvm/scummvm/commit/449ee08621d5773c9e3f7de74ee311bf95146557
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:47+02:00

Commit Message:
COLONY: add missing in-game text strings

Changed paths:
    engines/colony/animation.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index d54653b8e81..f46c9847af6 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -1183,6 +1183,9 @@ void ColonyEngine::handleSuitClick(int item) {
 		setObjectState(4, _weapons + 1);
 		drawAnimation();
 		_gfx->copyToScreen();
+	} else if (item == 6) {
+		// GANIMATE.C DoPowerSuit() case 6: help/info text
+		doText(13, 0);
 	}
 }
 


Commit: 05c18cace2c56e4f2623514e67df243fa349640d
    https://github.com/scummvm/scummvm/commit/05c18cace2c56e4f2623514e67df243fa349640d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:47+02:00

Commit Message:
COLONY: text rendering

Changed paths:
    engines/colony/ui.cpp


diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 258c677b9b6..cb6de9ffbe9 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -1013,22 +1013,27 @@ void ColonyEngine::doText(int entry, int center) {
 		}
 	}
 
+	// DOS DOTEXT.C: r positioned at (cX ± wdth, cY ± ((maxlines+1)*7 + 4))
+	// then offset by (+3,+3) for shadow. 3 nested FrameRects shrinking by 1.
+	const int halfH = ((maxlines + 1) * (lineheight / 2)) + 4;
 	Common::Rect r;
-	r.top = _centerY - (((maxlines + 1) * lineheight / 2) + 4);
-	r.bottom = _centerY + (((maxlines + 1) * lineheight / 2) + 4);
-	r.left = _centerX - (width / 2);
-	r.right = _centerX + (width / 2);
+	r.left = _centerX - (width / 2) + 3;
+	r.right = _centerX + (width / 2) + 3;
+	r.top = _centerY - halfH + 3;
+	r.bottom = _centerY + halfH + 3;
 
+	// DoGray(): dither the viewport background
 	_gfx->fillDitherRect(_screenR, 0, 15);
 
-	// Draw shadow/border (original draws 3 frames total)
-	for (int i = 0; i < 2; i++) {
+	// 3 nested FrameRects (shadow), then erase interior + final frame
+	for (int i = 0; i < 3; i++) {
 		_gfx->drawRect(r, 0);
 		r.translate(-1, -1);
 	}
 	_gfx->fillRect(r, 15);
 	_gfx->drawRect(r, 0);
 
+	// Draw first page of text
 	for (int i = 0; i < maxlines; i++) {
 		_gfx->drawString(&font, lineArray[i], r.left + 3, r.top + 4 + i * lineheight, 0);
 		if (center == 2) {
@@ -1037,12 +1042,33 @@ void ColonyEngine::doText(int entry, int center) {
 		}
 	}
 
-	_gfx->drawString(&font, (int)lineArray.size() > maxlines ? kmore : kpress, (r.left + r.right) / 2, r.top + 6 + maxlines * lineheight, 0, Graphics::kTextAlignCenter);
+	// Show "More" or "Press Any Key" prompt
+	const bool hasMore = ((int)lineArray.size() > maxlines);
+	_gfx->drawString(&font, hasMore ? "-Press Any Key For More...-" : kpress,
+		(r.left + r.right) / 2, r.top + 6 + maxlines * lineheight, 0, Graphics::kTextAlignCenter);
 	_gfx->copyToScreen();
-
-	// Wait for key
 	waitForInput();
 
+	// Second page: if text was truncated, show remainder
+	// DOS DOTEXT.C: starts from maxlines-1 (repeats last line of page 1 for context)
+	if (hasMore) {
+		_gfx->fillRect(r, 15);
+		_gfx->drawRect(r, 0);
+		int pageStart = maxlines - 1;
+		for (int i = pageStart; i < (int)lineArray.size() && (i - pageStart) < maxlines; i++) {
+			_gfx->drawString(&font, lineArray[i], r.left + 3,
+				r.top + 6 + (1 + i - pageStart) * lineheight, 0);
+			if (center == 2) {
+				_sound->play(Sound::kDit);
+				_system->delayMillis(20);
+			}
+		}
+		_gfx->drawString(&font, kpress,
+			(r.left + r.right) / 2, r.top + 6 + maxlines * lineheight, 0, Graphics::kTextAlignCenter);
+		_gfx->copyToScreen();
+		waitForInput();
+	}
+
 	delete[] page;
 }
 


Commit: 3db09eb0b21bac0af25854cc1d77dd99e57c7d76
    https://github.com/scummvm/scummvm/commit/3db09eb0b21bac0af25854cc1d77dd99e57c7d76
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:48+02:00

Commit Message:
COLONY: intro fixes

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


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index f46c9847af6..504cc4daca6 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -640,9 +640,9 @@ void ColonyEngine::drawAnimation() {
 				_gfx->setPixel(ox + x, oy + y, set ? (uint32)0xFF000000 : bg);
 			}
 		}
-	} else {
+	} else if (_renderMode == Common::kRenderMacintosh) {
 		// Mac QuickDraw FillRect: pattern bit=1 → ForeColor (black=0),
-		// bit=0 → BackColor (white=15). Same for B&W Mac and DOS.
+		// bit=0 → BackColor (white=15).
 		for (int y = 0; y < 264; y++) {
 			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
 			byte row = pat[y % 8];
@@ -651,6 +651,17 @@ void ColonyEngine::drawAnimation() {
 				_gfx->setPixel(ox + x, oy + y, set ? 0 : 15);
 			}
 		}
+	} else {
+		// DOS MetaWINDOW: pattern bit=1 → pen color (white=15),
+		// bit=0 → background (black=0). Opposite of Mac QuickDraw.
+		for (int y = 0; y < 264; y++) {
+			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
+			byte row = pat[y % 8];
+			for (int x = 0; x < 416; x++) {
+				bool set = (row & (0x80 >> (x % 8))) != 0;
+				_gfx->setPixel(ox + x, oy + y, set ? 15 : 0);
+			}
+		}
 	}
 
 	// Draw background image if active.
@@ -716,9 +727,20 @@ void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y, uin
 	//   mask bit=1 -> opaque (part of sprite)
 	//   fg bit=1   -> ForeColor (black)
 	//   fg bit=0   -> BackColor (fillColor from BMColor)
-	// B&W/DOS: same semantics — fg bit=1 is black (0), fg bit=0 is white (15).
-	const uint32 fgColor = useColor ? (uint32)0xFF000000 : 0;
-	const uint32 bgColor = useColor ? fillColor : 15;
+	// 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 isMacMode = (_renderMode == Common::kRenderMacintosh);
+	uint32 fgColor, bgColor;
+	if (useColor) {
+		fgColor = (uint32)0xFF000000;
+		bgColor = fillColor;
+	} else if (isMacMode) {
+		fgColor = 0;
+		bgColor = 15;
+	} else {
+		fgColor = 15;
+		bgColor = 0;
+	}
 
 	for (int iy = 0; iy < img->height; iy++) {
 		for (int ix = 0; ix < img->width; ix++) {
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 4c2b62e8985..2e8cb7e510c 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -670,6 +670,7 @@ private:
 	void playIntro();
 	bool makeStars(const Common::Rect &r, int btn);
 	bool makeBlackHole();
+	bool makePlanet();
 	bool timeSquare(const Common::String &str, const Graphics::Font *macFont = nullptr);
 	bool drawPict(int resID);
 	bool loadAnimation(const Common::String &name);
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 9ef8e3cc627..203d4fba63c 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -234,6 +234,7 @@ void ColonyEngine::playIntro() {
 
 			if (!qt) {
 				// 7. Makeblackhole()
+				// Original: SetPort(&metaPort); FillRect(&rScreen,black);
 				_gfx->clear(_gfx->black());
 				_gfx->copyToScreen();
 				qt = makeBlackHole();
@@ -252,10 +253,10 @@ void ColonyEngine::playIntro() {
 
 			if (!qt) {
 				// 10. makeplanet() + EndCSound()
-				// Simplified: starfield + delay (makeplanet draws a rotating planet)
+				// 3D wireframe planet: stars + sphere rotation + zoom approach
 				_gfx->clear(_gfx->black());
 				_gfx->copyToScreen();
-				qt = makeStars(_screenR, 0);
+				qt = makePlanet();
 				_sound->stop(); // EndCSound()
 			}
 		}
@@ -267,17 +268,31 @@ void ColonyEngine::playIntro() {
 		while (!qt && !shouldQuit() && _sound->isPlaying())
 			_system->delayMillis(10);
 
-		// Original: DoExplodeSound(); while(!SoundDone()) InvertRect(&rScreen); StopSound();
-		_sound->play(Sound::kExplode);
-		while (!shouldQuit() && _sound->isPlaying()) {
-			_gfx->clear(_gfx->white());
-			_gfx->copyToScreen();
-			_system->delayMillis(50);
-			_gfx->clear(_gfx->black());
-			_gfx->copyToScreen();
-			_system->delayMillis(50);
+		// Original intro.c lines 103-113:
+		// if(!soundon) { for(i=0;i<16;i++) InvertRect(&rScreen); }
+		// else { DoExplodeSound(); while(!SoundDone()) InvertRect(&rScreen); StopSound(); }
+		// InvertRect in a tight loop = rapid XOR flicker (each inversion toggles all pixels)
+		// Original: one InvertRect per Display() loop iteration at ~8fps hardware.
+		// Each inversion toggles the entire screen (black↔white) creating a
+		// ~4Hz strobe effect. We match this with 125ms per inversion.
+		if (!_soundOn) {
+			// No sound: exactly 16 inversions at ~8fps cadence
+			for (int i = 0; i < 16 && !shouldQuit(); i++) {
+				_gfx->clear(i % 2 ? _gfx->black() : _gfx->white());
+				_gfx->copyToScreen();
+				_system->delayMillis(125);
+			}
+		} else {
+			_sound->play(Sound::kExplode);
+			int frame = 0;
+			while (!shouldQuit() && _sound->isPlaying()) {
+				_gfx->clear(frame % 2 ? _gfx->black() : _gfx->white());
+				_gfx->copyToScreen();
+				_system->delayMillis(125);
+				frame++;
+			}
+			_sound->stop();
 		}
-		_sound->stop();
 		_gfx->clear(_gfx->black());
 		_gfx->copyToScreen();
 		delete macFont;
@@ -293,7 +308,101 @@ void ColonyEngine::playIntro() {
 		}
 		_gfx->setPalette(restorePal + 128 * 3, 128, 128);
 	} else {
-		scrollInfo();
+		// DOS IBM_INTR.C intro(): full sequence after ScrollInfo
+		// Use full screen for intro (not gameplay viewport with dashboard offset)
+		Common::Rect savedScreenR = _screenR;
+		_screenR = Common::Rect(0, 0, _width, _height);
+		_centerX = _width / 2;
+		_centerY = _height / 2;
+		bool qt = scrollInfo();
+
+		if (!qt) {
+			// Logo 2: "Mindscape Presents"
+			_sound->stop();
+			_sound->play(Sound::kStars1);
+			_gfx->clear(_gfx->black());
+			if (loadAnimation("logo2")) {
+				drawAnimation();
+				_gfx->copyToScreen();
+			}
+			qt = makeStars(_screenR, 0);
+			_gfx->clear(_gfx->black());
+		}
+
+		if (!qt) {
+			// Logo 1: "The Colony by David A. Smith"
+			_sound->stop();
+			_sound->play(Sound::kStars2);
+			if (loadAnimation("logo1")) {
+				drawAnimation();
+				_gfx->copyToScreen();
+			}
+			qt = makeStars(_screenR, 0);
+			_gfx->clear(_gfx->black());
+		}
+
+		if (!qt) {
+			// Empty starfield 1
+			_sound->stop();
+			_sound->play(Sound::kStars3);
+			_gfx->copyToScreen();
+			qt = makeStars(_screenR, 0);
+			_gfx->clear(_gfx->black());
+		}
+
+		if (!qt) {
+			// Empty starfield 2
+			_sound->stop();
+			_sound->play(Sound::kStars4);
+			_gfx->copyToScreen();
+			qt = makeStars(_screenR, 0);
+			_gfx->clear(_gfx->black());
+		}
+
+		if (!qt)
+			qt = timeSquare("...BLACK HOLE COLLISION...", nullptr);
+
+		if (!qt) {
+			_gfx->clear(_gfx->black());
+			_gfx->copyToScreen();
+			qt = makeBlackHole();
+			_gfx->clear(_gfx->black());
+		}
+
+		if (!qt)
+			qt = timeSquare("...FUEL HAS BEEN DEPLETED...", nullptr);
+
+		if (!qt)
+			qt = timeSquare("...PREPARE FOR CRASH LANDING...", nullptr);
+
+		if (!qt) {
+			_sound->stop();
+			_sound->play(Sound::kStars4);
+			_gfx->clear(_gfx->black());
+			_gfx->copyToScreen();
+			qt = makePlanet();
+			_gfx->clear(_gfx->black());
+		}
+
+		// Final crash: DOS IBM_INTR.C lines 119-131
+		// Original: while(!SoundDone()) { EraseRect; PaintRect; } at ~8fps
+		_sound->stop();
+		_sound->play(Sound::kExplode);
+		int frame = 0;
+		while (!shouldQuit() && _sound->isPlaying()) {
+			_gfx->clear(frame % 2 ? _gfx->black() : _gfx->white());
+			_gfx->copyToScreen();
+			_system->delayMillis(125);
+			frame++;
+		}
+		_sound->stop();
+		_gfx->clear(_gfx->black());
+		_gfx->copyToScreen();
+
+		// Restore gameplay viewport
+		_screenR = savedScreenR;
+		_centerX = (_screenR.left + _screenR.right) / 2;
+		_centerY = (_screenR.top + _screenR.bottom) / 2;
 	}
 }
 
@@ -446,7 +555,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 		int xx = centerX + (int)(((long long)s * rr) >> 7);
 		int yy = centerY + (int)(((long long)c * rr) >> 7);
 		if (xx >= 0 && xx < _width && yy >= 0 && yy < _height)
-			_gfx->setPixel(xx, yy, 15);
+			_gfx->setPixel(xx, yy, 0xFFFFFFFF);
 	}
 
 	// Initialize moving stars  original uses PenMode(patXor) so stars
@@ -475,7 +584,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 		xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
 		ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
 
-		_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
+		_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 0xFFFFFFFF);
 	}
 	_gfx->copyToScreen();
 
@@ -488,7 +597,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 
 		for (int i = 0; i < NSTARS; i++) {
 			// Erase previous  XOR the same line again to restore underlying pixels
-			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
+			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 0xFFFFFFFF);
 
 			int s = xang[i];
 			int c = yang[i];
@@ -511,7 +620,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 			ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
 
 			// Draw new star position
-			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 15);
+			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 0xFFFFFFFF);
 		}
 		_gfx->copyToScreen();
 		_system->delayMillis(16);
@@ -545,7 +654,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 				int y1 = centerY + (int)(((long long)c * rr1) >> 7);
 				int x2 = centerX + (int)(((long long)s * rr2) >> 7);
 				int y2 = centerY + (int)(((long long)c * rr2) >> 7);
-				_gfx->drawLine(x1, y1, x2, y2, 15);
+				_gfx->drawLine(x1, y1, x2, y2, 0xFFFFFFFF);
 			}
 		}
 		_gfx->copyToScreen();
@@ -627,6 +736,164 @@ bool ColonyEngine::makeBlackHole() {
 	return false;
 }
 
+// intro.c makeplanet(): 3D wireframe planet with rotation and zoom-in.
+// Phase 1: draw background stars + initial sphere wireframe
+// Phase 2: rotate the planet in place (25 frames)
+// Phase 3: planet approaches the camera (zoom from dist 800 to 32)
+// All rendering in XOR mode so dots toggle on/off.
+bool ColonyEngine::makePlanet() {
+	static const int PDELTA = 16;
+	// Original rtable has 11585 entries; planet uses indices up to 800.
+	static const int RTABLE_SIZE = 801;
+
+	int rtable[RTABLE_SIZE];
+	rtable[0] = 32000;
+	for (int i = 1; i < RTABLE_SIZE; i++)
+		rtable[i] = (160 * 128) / i; // Floor=160
+
+	const int centerx = _width / 2;
+	const int centery = _height / 2;
+	const int sintheta = _sint[210];
+	const int costheta = _cost[210];
+
+	// Phase 1a: draw background stars
+	static const int STAR_COUNT = 192; // (800-32)/16 * 4 = ~192
+	int xstars[STAR_COUNT], ystars[STAR_COUNT];
+	int starcnt = 0;
+
+	_gfx->setXorMode(true);
+	for (int i = 800; i > 32 && starcnt < STAR_COUNT - 4; i -= 16) {
+		for (int m = 0; m < 4; m++) {
+			int sindex = _randomSource.getRandomNumber(255);
+			int xx = centerx + (int)(((long)rtable[i] * _sint[sindex]) >> 7);
+			int yy = centery + (int)(((long)rtable[i] * _cost[sindex]) >> 7);
+			if (starcnt < STAR_COUNT) {
+				xstars[starcnt] = xx;
+				ystars[starcnt] = yy;
+				starcnt++;
+			}
+			_gfx->setPixel(xx, yy, 0xFFFFFFFF);
+		}
+	}
+
+	// Phase 1b: draw initial planet wireframe at distance 800
+	// Sphere: j=latitude (0..255 step PDELTA), k=longitude (0..127 step PDELTA)
+	// Tilted by sintheta/costheta around X axis (viewing angle)
+	static const int MAX_POINTS = (256 / PDELTA) * (128 / PDELTA); // 16*8 = 128
+	int xsave[MAX_POINTS], ysave[MAX_POINTS];
+	bool zsave[MAX_POINTS];
+	int start = 0, dstart = 1;
+
+	long rt = rtable[800];
+	int save = 0;
+	for (int j = 0; j < 256; j += PDELTA) {
+		for (int k = start; k < 128; k += PDELTA) {
+			int xx = (int)(((rt * _sint[j]) >> 7) * _cost[k] >> 7);
+			int zz = (int)(((rt * _sint[j]) >> 7) * _sint[k] >> 7);
+			int y = (int)((((rt * _cost[j]) >> 7) * (long)costheta - (long)zz * sintheta) >> 7);
+			zz = (int)(((long)_cost[j] * sintheta + (long)zz * costheta) >> 7);
+			if (save < MAX_POINTS) {
+				zsave[save] = (zz >= 0);
+				if (zsave[save]) {
+					xsave[save] = xx + centerx;
+					ysave[save] = centery + y;
+					_gfx->setPixel(xsave[save], ysave[save], 0xFFFFFFFF);
+				}
+				save++;
+			}
+		}
+	}
+	_gfx->copyToScreen();
+
+	// Phase 2: rotate the planet in place (25 frames)
+	for (int frame = 0; frame < 25; frame++) {
+		if (checkSkipRequested()) {
+			_gfx->setXorMode(false);
+			return true;
+		}
+
+		save = 0;
+		for (int j = 0; j < 256; j += PDELTA) {
+			for (int k = start, l = dstart; k < 128; k += PDELTA, l += PDELTA) {
+				if (save >= MAX_POINTS)
+					break;
+				// Erase old point
+				if (zsave[save])
+					_gfx->setPixel(xsave[save], ysave[save], 0xFFFFFFFF);
+
+				rt = rtable[800];
+				int xx = (int)(((rt * _sint[j]) >> 7) * _cost[l] >> 7);
+				int zz = (int)(((rt * _sint[j]) >> 7) * _sint[l] >> 7);
+				int z = (int)(((long)_cost[j] * sintheta + (long)zz * costheta) >> 7);
+				zsave[save] = (z >= 0);
+				if (zsave[save]) {
+					ysave[save] = centery + (int)((((rt * _cost[j]) >> 7) * (long)costheta - (long)zz * sintheta) >> 7);
+					xsave[save] = xx + centerx;
+					_gfx->setPixel(xsave[save], ysave[save], 0xFFFFFFFF);
+				}
+				save++;
+			}
+		}
+		start++;
+		if (start == PDELTA)
+			start = 0;
+		dstart++;
+		if (dstart == PDELTA)
+			dstart = 0;
+		_gfx->copyToScreen();
+		_system->delayMillis(33);
+	}
+
+	// Phase 3: planet approaches camera (zoom from dist 800 to 32)
+	starcnt = 0;
+	for (int i = 800; i > 32; i -= 16) {
+		if (checkSkipRequested()) {
+			_gfx->setXorMode(false);
+			return true;
+		}
+
+		// Erase stars as planet passes them
+		for (int m = 0; m < 4 && starcnt < STAR_COUNT; m++) {
+			_gfx->setPixel(xstars[starcnt], ystars[starcnt], 0xFFFFFFFF);
+			starcnt++;
+		}
+
+		save = 0;
+		for (int j = 0; j < 256; j += PDELTA) {
+			for (int k = start, l = dstart; k < 128; k += PDELTA, l += PDELTA) {
+				if (save >= MAX_POINTS)
+					break;
+				// Erase old
+				if (zsave[save])
+					_gfx->setPixel(xsave[save], ysave[save], 0xFFFFFFFF);
+
+				rt = rtable[i];
+				int xx = (int)(((rt * _sint[j]) >> 7) * _cost[l] >> 7);
+				int zz = (int)(((rt * _sint[j]) >> 7) * _sint[l] >> 7);
+				int z = (int)(((long)_cost[j] * sintheta + (long)zz * costheta) >> 7);
+				zsave[save] = (z >= 0);
+				if (zsave[save]) {
+					ysave[save] = centery + (int)((((rt * _cost[j]) >> 7) * (long)costheta - (long)zz * sintheta) >> 7);
+					xsave[save] = xx + centerx;
+					_gfx->setPixel(xsave[save], ysave[save], 0xFFFFFFFF);
+				}
+				save++;
+			}
+		}
+		start++;
+		if (start == PDELTA)
+			start = 0;
+		dstart++;
+		if (dstart == PDELTA)
+			dstart = 0;
+		_gfx->copyToScreen();
+		_system->delayMillis(16);
+	}
+
+	_gfx->setXorMode(false);
+	return false;
+}
+
 bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *macFont) {
 	// Original: TimeSquare() in intro.c
 	// 1. Draw horizontal blue gradient lines above/below center
@@ -684,27 +951,27 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 		_system->delayMillis(8);
 	}
 
-	// Phase 2: Klaxon flash  original: EndCSound(); then 6 iterations of:
-	//   if(Button()) if(qt=OptionKey()) break;
-	//   while(!SoundDone()); StopSound(); PlayKlaxon(); InvertRect(&invrt);
-	_sound->stop(); // EndCSound()
+	// Phase 2: Klaxon flash — original intro.c lines 312-322:
+	// EndCSound(); for 6 iterations: wait for sound, stop, play klaxon,
+	// InvertRect. The klaxon is short (~200ms). Inversions happen rapidly
+	// at the start of each klaxon, creating a fast strobe effect.
+	_sound->stop();
+	_gfx->setXorMode(true);
 	for (int i = 0; i < 6; i++) {
-		if (checkSkipRequested())
+		if (checkSkipRequested()) {
+			_gfx->setXorMode(false);
 			return true;
-
-		// Wait for previous klaxon to finish
-		while (_sound->isPlaying() && !shouldQuit())
-			_system->delayMillis(10);
-		_sound->stop();
+		}
 
 		_sound->play(Sound::kKlaxon);
-
-		// InvertRect(&invrt)  toggle the text band
-		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), i % 2 ? 0 : 15);
-		_gfx->drawString(font, str, targetX, centery + 2, i % 2 ? 15 : 0, Graphics::kTextAlignLeft);
+		// InvertRect(&invrt) — XOR the text band
+		_gfx->fillRect(Common::Rect(0, centery + 1, _width, centery + 16), 0xFFFFFFFF);
 		_gfx->copyToScreen();
+		// Brief pause matching klaxon duration (~200ms)
+		_system->delayMillis(200);
 	}
-	// Wait for last klaxon
+	_gfx->setXorMode(false);
+	// Wait for last klaxon to finish
 	while (_sound->isPlaying() && !shouldQuit())
 		_system->delayMillis(10);
 	_sound->stop();


Commit: 7f37140daddfa253e6dfef861cc6edafff97c57d
    https://github.com/scummvm/scummvm/commit/7f37140daddfa253e6dfef861cc6edafff97c57d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:48+02:00

Commit Message:
COLONY: add missing sound effect triggers

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


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 504cc4daca6..0546c34c9b7 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -1054,7 +1054,8 @@ void ColonyEngine::handleVanityClick(int item) {
 }
 
 void ColonyEngine::handleSlidesClick(int item) {
-	if (item == 2) { // Speaker
+	if (item == 2) { // Speaker — GANIMATE.C DoCreatures: DoTestSound() + DoText
+		_sound->play(Sound::kTest);
 		doText(261 + _creature, 0);
 	} else if (item == 5) { // Prev
 		_creature--;
@@ -1070,7 +1071,8 @@ void ColonyEngine::handleSlidesClick(int item) {
 }
 
 void ColonyEngine::handleTeleshowClick(int item) {
-	if (item == 2) { // Speaker
+	if (item == 2) { // Speaker — GANIMATE.C DoTeleShow: DoTestSound() + DoText
+		_sound->play(Sound::kTest);
 		doText(269 + _creature, 0);
 	} else if (item == 5) { // Prev
 		_creature--;
@@ -1512,6 +1514,8 @@ void ColonyEngine::handleControlsClick(int item) {
 	switch (item) {
 	case 4: // Accelerator
 		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--) {
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 52d82d363a3..52921c0a545 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -92,6 +92,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 		if (_fl == 1) {
 			// In empty forklift  pick up the teleporter itself
 			if (loadAnimation("lift")) {
+				_sound->play(Sound::kLift); // GANIMATE.C DoLift: DoLiftSound()
 				_animationResult = 0;
 				playAnimation();
 				if (_animationResult) {
@@ -217,6 +218,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 		if (_fl == 1) {
 			// In empty forklift  pick up object
 			if (loadAnimation("lift")) {
+				_sound->play(Sound::kLift); // GANIMATE.C DoLift: DoLiftSound()
 				_animationResult = 0;
 				playAnimation();
 				if (_animationResult) {
@@ -249,6 +251,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 		if (_fl == 1 && _coreState[_coreIndex] == 1) {
 			// Empty forklift at open reactor  pick up reactor core
 			if (loadAnimation("lift")) {
+				_sound->play(Sound::kLift); // GANIMATE.C DoLift: DoLiftSound()
 				_animationResult = 0;
 				playAnimation();
 				if (_animationResult) {
@@ -262,6 +265,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 		} else if (_fl == 2 && _carryType == kObjReactor && _coreState[_coreIndex] == 2) {
 			// Carrying reactor core  drop it into reactor
 			if (loadAnimation("lift")) {
+				_sound->play(Sound::kDrop); // GANIMATE.C DoLift: DoDropSound()
 				_animationResult = 0;
 				playAnimation();
 				if (!_animationResult) {
@@ -274,6 +278,8 @@ void ColonyEngine::interactWithObject(int objNum) {
 		}
 		break;
 	default:
+		// IBM_COMM.C: DoBonkSound() for unhandled object interactions
+		_sound->play(Sound::kBonk);
 		break;
 	}
 }
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 696f647388d..ee10da20b33 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -1122,16 +1122,17 @@ void ColonyEngine::dropCarriedObject() {
 	if (_fl != 2)
 		return;
 
-	// Special case: carrying reactor core
+	// Special case: carrying reactor core — IBM_COMM.C: DoGlassSound()
 	if (_carryType == kObjReactor) {
-		_sound->play(Sound::kChime); // glass break sound
+		_sound->play(Sound::kGlass);
 		_carryType = 0;
 		_fl = 1;
 		return;
 	}
 
-	// Play the drop animation
+	// Play the drop animation — GANIMATE.C DoLift: DoDropSound()
 	if (loadAnimation("lift")) {
+		_sound->play(Sound::kDrop);
 		_animationResult = 0;
 		playAnimation();
 		if (!_animationResult) {


Commit: 25e0bde88c6c6df861168796529332be9237cef6
    https://github.com/scummvm/scummvm/commit/25e0bde88c6c6df861168796529332be9237cef6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:48+02:00

Commit Message:
COLONY: fixed some missing triangles

Changed paths:
    engines/colony/render_objects.cpp


diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 5b121ccd8d9..c7fa97a5de9 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -222,15 +222,21 @@ static const int kDeskScreenSurf[1][8] = {{kColorMacScreen, 4, 3, 2, 1, 0, 0, 0}
 static const int kCSeatPts[4][3] = {
 	{-40, 40, 60}, {40, 40, 60}, {40, -40, 60}, {-40, -40, 60}
 };
-static const int kCSeatSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCSeatSurf[2][8] = {
+	{kColorChair, 4, 3, 2, 1, 0, 0, 0}, {kColorChair, 4, 0, 1, 2, 3, 0, 0}
+};
 static const int kCArmLeftPts[4][3] = {
 	{-50, 40, 90}, {-40, 40, 60}, {-40, -40, 60}, {-50, -40, 90}
 };
-static const int kCArmLeftSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCArmLeftSurf[2][8] = {
+	{kColorChair, 4, 3, 2, 1, 0, 0, 0}, {kColorChair, 4, 0, 1, 2, 3, 0, 0}
+};
 static const int kCArmRightPts[4][3] = {
 	{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
 };
-static const int kCArmRightSurf[1][8] = {{kColorChair, 4, 3, 2, 1, 0, 0, 0}};
+static const int kCArmRightSurf[2][8] = {
+	{kColorChair, 4, 3, 2, 1, 0, 0, 0}, {kColorChair, 4, 0, 1, 2, 3, 0, 0}
+};
 static const int kCBackPts[4][3] = {
 	{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
 };
@@ -412,9 +418,9 @@ static const Colony::ColonyEngine::PrismPartDef kDeskParts[10] = {
 	{4, kDeskScreenPts, 1, kDeskScreenSurf}
 };
 static const Colony::ColonyEngine::PrismPartDef kCChairParts[5] = {
-	{4, kCSeatPts, 1, kCSeatSurf},
-	{4, kCArmLeftPts, 1, kCArmLeftSurf},
-	{4, kCArmRightPts, 1, kCArmRightSurf},
+	{4, kCSeatPts, 2, kCSeatSurf},
+	{4, kCArmLeftPts, 2, kCArmLeftSurf},
+	{4, kCArmRightPts, 2, kCArmRightSurf},
 	{4, kCBackPts, 1, kCBackSurf},
 	{8, kCBasePts, 4, kCBaseSurf}
 };
@@ -1168,7 +1174,9 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	case kObjCChair:
 		for (int i = 0; i < 5; i++) {
 			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
-			draw3DPrism(obj, kCChairParts[i], false, -1, true, false);
+			// Flat quad parts (seat, arms, back) need forceVisible to avoid
+			// edge-on culling; box base (i==4) uses normal culling.
+			draw3DPrism(obj, kCChairParts[i], false, -1, true, i < 4);
 		}
 		_gfx->setDepthRange(0.0, 1.0);
 		break;


Commit: 8febde607530e8f31c19cdf96ba22bfee7c11b35
    https://github.com/scummvm/scummvm/commit/8febde607530e8f31c19cdf96ba22bfee7c11b35
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:49+02:00

Commit Message:
COLONY: corner walls fix

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


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 9a0265bfec3..57052e89464 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -570,7 +570,17 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 			} else if (lit) {
 				if (_renderMode == Common::kRenderMacintosh) {
 					// Mac B&W: stipple dither pattern fill + black outline
-					int pattern = lookupMacPattern(colorIdx, _level);
+					int pattern;
+					if (colorIdx == kColorCorridorWall) {
+						// Original Mac c_lwall: WHITE pattern = white fill + black outline,
+						// matching the grid wall rendering.
+						if (!_wireframe) {
+							_gfx->setWireframe(true, 255);
+						}
+						_gfx->draw3DPolygon(px, py, pz, count, 0);
+						continue;
+					}
+					pattern = lookupMacPattern(colorIdx, _level);
 					if (pattern == kPatternClear)
 						continue;
 					if (!_wireframe) {
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index c7fa97a5de9..3156a97397a 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1211,16 +1211,16 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kObjFWall:
-		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
-			draw3DPrism(obj, kFWallPart, false, kColorCorridorWall, true, false);
+		if (_renderMode == Common::kRenderMacintosh)
+			draw3DPrism(obj, kFWallPart, false, kColorCorridorWall, true, true);
 		else
-			draw3DPrism(obj, kFWallPart, false, -1, true, false);
+			draw3DPrism(obj, kFWallPart, false, -1, true, true);
 		break;
 	case kObjCWall:
-		if (_renderMode == Common::kRenderMacintosh && _hasMacColors)
-			draw3DPrism(obj, kCWallPart, false, kColorCorridorWall, true, false);
+		if (_renderMode == Common::kRenderMacintosh)
+			draw3DPrism(obj, kCWallPart, false, kColorCorridorWall, true, true);
 		else
-			draw3DPrism(obj, kCWallPart, false, -1, true, false);
+			draw3DPrism(obj, kCWallPart, false, -1, true, true);
 		break;
 	case kObjScreen:
 		draw3DPrism(obj, kScreenPart, false, -1, true, false);


Commit: 0041e8e57e961dd01792ee6ed9a2cbb692f5d377
    https://github.com/scummvm/scummvm/commit/0041e8e57e961dd01792ee6ed9a2cbb692f5d377
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:49+02:00

Commit Message:
COLONY: better collision with corner walls

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 2e8cb7e510c..92d0ec8efe9 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -388,6 +388,7 @@ public:
 	void quadrant();
 	bool hasInteractiveWallFeature(int cx, int cy, int dir) const;
 	void clampToWalls(Locate *p);
+	void clampToDiagonalWalls(Locate *p);
 	int checkwallMoveTo(int xnew, int ynew, int xind2, int yind2, Locate *pobject, uint8 trailCode);
 	int checkwallTryFeature(int xnew, int ynew, int xind2, int yind2, Locate *pobject, int dir);
 	int checkwall(int xnew, int ynew, Locate *pobject);
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index d5478e19456..8d8498a935c 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -138,7 +138,9 @@ void ColonyEngine::loadMap(int mnum) {
 								}
 								_objects.push_back(obj);
 								const int objNum = (int)_objects.size(); // 1-based, DOS-style robot slots
-								if (objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
+								// CWall/FWall use diagonal collision, not cell-based blocking.
+								if (obj.type != kObjFWall && obj.type != kObjCWall &&
+								    objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
 									_robotArray[i][j] = (uint8)objNum;
 							}
 					} else {
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index ee10da20b33..5352ee929fc 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -252,6 +252,67 @@ void ColonyEngine::clampToWalls(Locate *p) {
 		p->xloc = MIN(p->xloc, cellMaxX - kWallPad);
 }
 
+void ColonyEngine::clampToDiagonalWalls(Locate *p) {
+	// CWall/FWall objects are diagonal corner fills not registered in _robotArray.
+	// Enforce geometric collision: the CWall inner face is the line lx+ly=kThreshold
+	// in the object's local coordinate space.  The player must stay on the room side.
+	static const int kThreshold = 120; // inner face ~112 + padding
+	for (uint i = 0; i < _objects.size(); i++) {
+		const Thing &obj = _objects[i];
+		if (!obj.alive)
+			continue;
+		if (obj.type != kObjCWall && obj.type != kObjFWall)
+			continue;
+
+		// Quick reject: skip objects more than 1 cell away
+		if (ABS(p->xloc - obj.where.xloc) > 300 || ABS(p->yloc - obj.where.yloc) > 300)
+			continue;
+
+		// Transform player position into object's local space (inverse rotation)
+		const int wx = p->xloc - obj.where.xloc;
+		const int wy = p->yloc - obj.where.yloc;
+		const uint8 invAng = (uint8)(0 - obj.where.ang);
+		const int lx = (int)(((long)wx * _cost[invAng] - (long)wy * _sint[invAng]) >> 7);
+		const int ly = (int)(((long)wx * _sint[invAng] + (long)wy * _cost[invAng]) >> 7);
+
+		// Also reject if clearly outside the cell (local coords span -128..128)
+		if (lx < -140 || lx > 140 || ly < -140 || ly > 140)
+			continue;
+
+		const int diag = lx + ly;
+		if (obj.type == kObjCWall) {
+			if (diag >= kThreshold)
+				continue; // already on room side
+
+			// Push player along normal (1,1) in local space to reach threshold
+			const int push = (kThreshold - diag + 1) / 2;
+			const int nlx = lx + push;
+			const int nly = ly + push;
+
+			// Transform back to world space
+			const uint8 ang = obj.where.ang;
+			p->xloc = obj.where.xloc + (int)(((long)nlx * _cost[ang] - (long)nly * _sint[ang]) >> 7);
+			p->yloc = obj.where.yloc + (int)(((long)nlx * _sint[ang] + (long)nly * _cost[ang]) >> 7);
+			p->xindex = p->xloc >> 8;
+			p->yindex = p->yloc >> 8;
+		} else { // kObjFWall — flat wall along the diagonal
+			static const int kFWallThreshold = 20;
+			if (diag >= kFWallThreshold)
+				continue;
+
+			const int push = (kFWallThreshold - diag + 1) / 2;
+			const int nlx = lx + push;
+			const int nly = ly + push;
+
+			const uint8 ang = obj.where.ang;
+			p->xloc = obj.where.xloc + (int)(((long)nlx * _cost[ang] - (long)nly * _sint[ang]) >> 7);
+			p->yloc = obj.where.yloc + (int)(((long)nlx * _sint[ang] + (long)nly * _cost[ang]) >> 7);
+			p->xindex = p->xloc >> 8;
+			p->yindex = p->yloc >> 8;
+		}
+	}
+}
+
 int ColonyEngine::checkwallMoveTo(int xnew, int ynew, int xind2, int yind2, Locate *pobject, uint8 trailCode) {
 	const int rnum = occupiedObjectAt(xind2, yind2, pobject);
 	if (rnum)
@@ -267,6 +328,7 @@ int ColonyEngine::checkwallMoveTo(int xnew, int ynew, int xind2, int yind2, Loca
 	pobject->xloc = xnew;
 	pobject->yloc = ynew;
 	clampToWalls(pobject);
+	clampToDiagonalWalls(pobject);
 	return 0;
 }
 


Commit: abf4977f6b04eb44158091ceb4554121eee046ad
    https://github.com/scummvm/scummvm/commit/abf4977f6b04eb44158091ceb4554121eee046ad
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:49+02:00

Commit Message:
COLONY: forklift fixes

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/debugger.cpp
    engines/colony/debugger.h
    engines/colony/interaction.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 0546c34c9b7..01924704976 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -989,6 +989,18 @@ void ColonyEngine::handleAnimationClick(int item) {
 		handleElevatorClick(item);
 	} else if (_animationName == "controls") {
 		handleControlsClick(item);
+	} else if (_animationName == "forklift") {
+		// Original DoForkLift: waits for item==1 (entrance click) to confirm entry
+		if (item == 1) {
+			_animationResult = 1;
+			_animationRunning = false;
+		}
+	} else if (_animationName == "lift") {
+		// Original DoLift: item==8 puts down (when carrying), item==9 picks up (when empty)
+		if (item == 8 || item == 9) {
+			_animationResult = 1;
+			_animationRunning = false;
+		}
 	}
 }
 
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 5c368d12859..cf846603674 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -1015,7 +1015,7 @@ Common::Error ColonyEngine::run() {
 				if (_gameMode == kModeBattle)
 					_me.ang = _me.look;
 			}
-			if (mouseDY != 0) {
+			if (mouseDY != 0 && !_fl) {
 				_me.lookY = (int8)CLIP<int>((int)_me.lookY - (mouseDY * _mouseSensitivity), -64, 64);
 			}
 			// Warp back to center and purge remaining mouse events
@@ -1086,6 +1086,7 @@ Common::Error ColonyEngine::run() {
 		} else {
 			_gfx->clear((_corePower[_coreIndex] > 0) ? 15 : 0);
 			corridor();
+			drawForkliftOverlay();
 			drawDashboardStep1();
 			drawCrosshair();
 			checkCenter();
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 92d0ec8efe9..e59db455946 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -632,6 +632,7 @@ private:
 	bool hasRobotAt(int x, int y) const;
 	bool hasFoodAt(int x, int y) const;
 	void drawMiniMap(uint32 lineColor);
+	void drawForkliftOverlay();
 	void drawCrosshair();
 	bool clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const;
 	void wallLine(const float corners[4][3], float u1, float v1, float u2, float v2, uint32 color);
diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
index 49b6d558608..68bd1742828 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/debugger.cpp
@@ -79,6 +79,7 @@ Debugger::Debugger(ColonyEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("core", WRAP_METHOD(Debugger, cmdCore));
 	registerCmd("battle", WRAP_METHOD(Debugger, cmdBattle));
 	registerCmd("colony", WRAP_METHOD(Debugger, cmdColony));
+	registerCmd("forklift", WRAP_METHOD(Debugger, cmdForklift));
 }
 
 bool Debugger::cmdTeleport(int argc, const char **argv) {
@@ -397,4 +398,40 @@ bool Debugger::cmdColony(int argc, const char **argv) {
 	return false;
 }
 
+bool Debugger::cmdForklift(int argc, const char **argv) {
+	if (_vm->_gameMode != kModeColony) {
+		debugPrintf("Must be in colony mode\n");
+		return true;
+	}
+
+	if (argc >= 2) {
+		int state = atoi(argv[1]);
+		if (state < 0 || state > 2) {
+			debugPrintf("Invalid state %d (must be 0-2)\n", state);
+			return true;
+		}
+		_vm->_fl = state;
+		if (state > 0)
+			_vm->_me.lookY = 0; // reset vertical look
+		if (state == 0) {
+			_vm->_carryType = 0;
+			debugPrintf("Exited forklift (fl=0)\n");
+		} else if (state == 1) {
+			_vm->_carryType = 0;
+			debugPrintf("Entered empty forklift (fl=1)\n");
+		} else {
+			if (_vm->_carryType == 0)
+				_vm->_carryType = kObjBox1;
+			debugPrintf("In forklift carrying type %d (fl=2)\n", _vm->_carryType);
+		}
+	} else {
+		debugPrintf("Usage: forklift <state>\n");
+		debugPrintf("  0 = exit forklift\n");
+		debugPrintf("  1 = enter empty forklift\n");
+		debugPrintf("  2 = enter forklift carrying object\n");
+		debugPrintf("Current: fl=%d carryType=%d\n", _vm->_fl, _vm->_carryType);
+	}
+	return true;
+}
+
 } // End of namespace Colony
diff --git a/engines/colony/debugger.h b/engines/colony/debugger.h
index 379dc1dec14..08512a29f87 100644
--- a/engines/colony/debugger.h
+++ b/engines/colony/debugger.h
@@ -44,6 +44,7 @@ private:
 	bool cmdCore(int argc, const char **argv);
 	bool cmdBattle(int argc, const char **argv);
 	bool cmdColony(int argc, const char **argv);
+	bool cmdForklift(int argc, const char **argv);
 };
 
 }
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 52921c0a545..c2d0368086d 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -41,6 +41,20 @@ void ColonyEngine::interactWithObject(int objNum) {
 	_action1 = _mapData[x][y][4][4];
 	_creature = 1;
 
+	// Original command.c: interaction is filtered by forklift state.
+	// fl==1 (empty forklift): only CRYO/BOX1/BOX2/TELEPORT (pick up), REACTOR, SCREEN
+	// fl==2 (carrying): only REACTOR (drop), TELEPORT (use), SCREEN; others → bonk
+	if (_fl == 1) {
+		if (obj.type != kObjBox1 && obj.type != kObjBox2 && obj.type != kObjCryo &&
+		    obj.type != kObjTeleport && obj.type != kObjReactor && obj.type != kObjScreen)
+			return; // silently ignore — original has no default for fl==1
+	} else if (_fl == 2) {
+		if (obj.type != kObjReactor && obj.type != kObjTeleport && obj.type != kObjScreen) {
+			_sound->play(Sound::kBonk);
+			return;
+		}
+	}
+
 	switch (obj.type) {
 	case kObjDesk:
 		if (loadAnimation("desk"))
@@ -207,6 +221,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 					_robotArray[obj.where.xindex][obj.where.yindex] = 0;
 					_objects[objNum - 1].alive = 0;
 					_me.look = _me.ang = obj.where.ang;
+					_me.lookY = 0;
 					_fl = 1;
 				}
 			}
@@ -322,6 +337,10 @@ void ColonyEngine::cShoot() {
 		return;
 	}
 
+	// Original command.c: space bar disabled when in forklift
+	if (_fl)
+		return;
+
 	if (_me.power[0] <= 0 || _weapons <= 0)
 		return;
 
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index cb6de9ffbe9..b81b79011b2 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -800,6 +800,32 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 	}
 }
 
+void ColonyEngine::drawForkliftOverlay() {
+	if (_fl <= 0 || _screenR.width() <= 0 || _screenR.height() <= 0)
+		return;
+
+	// Original display.c: two diagonal fork arm lines when fl > 0.
+	// Left arm:  (centerX/4, 0) to (centerX/2, Height)
+	// Right arm: (Width - centerX/4, 0) to (Width - centerX/2, Height)
+	// Drawn with PenSize(2,2) in black (white in battle mode).
+	const int left = _screenR.left;
+	const int top = _screenR.top;
+	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 int tx2 = cx >> 2;  // centerX/4
+	const int tx1 = cx >> 1;  // centerX/2
+
+	// Left fork arm
+	_gfx->drawLine(left + tx2, top, left + tx1, top + h - 1, color);
+	_gfx->drawLine(left + tx2 + 1, top, left + tx1 + 1, top + h - 1, color);
+	// Right fork arm
+	_gfx->drawLine(left + w - tx2, top, left + w - tx1, top + h - 1, color);
+	_gfx->drawLine(left + w - tx2 - 1, top, left + w - tx1 - 1, top + h - 1, color);
+}
+
 void ColonyEngine::drawCrosshair() {
 	if (!_crosshair || _screenR.width() <= 0 || _screenR.height() <= 0)
 		return;


Commit: 451888ee5e7e0569b07c80e5f6118e0474107a4e
    https://github.com/scummvm/scummvm/commit/451888ee5e7e0569b07c80e5f6118e0474107a4e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:50+02:00

Commit Message:
COLONY: more forklift fixes

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


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 01924704976..acbc5fa55d8 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -522,6 +522,45 @@ void ColonyEngine::playAnimation() {
 			setObjectOnOff(13, false);
 			setObjectOnOff(4, false);
 		}
+	} else if (_animationName == "lift") {
+		// Original DoLift: set up initial state based on forklift mode.
+		// _fl==1 → picking up (up=0, object starts at bottom)
+		// _fl==2 → putting down (up=1, object starts at top)
+		// Object sprite mapping: BOX1/BOX2→2, TELEPORT→1, CRYO→3, REACTOR→4
+		_liftUp = (_fl == 2);
+		switch (_fl == 2 ? _carryType : 0) {
+		case kObjBox1: case kObjBox2: _liftObject = 2; break;
+		case kObjTeleport: _liftObject = 1; break;
+		case kObjCryo: _liftObject = 3; break;
+		case kObjReactor: _liftObject = 4; break;
+		default: _liftObject = 2; break; // pickup: we don't know yet, but dolSprite handles it
+		}
+		// For pickup, determine object from what we're about to pick up
+		if (!_liftUp) {
+			// The interaction code sets _carryType AFTER the animation,
+			// but the object type is in the Thing we're interacting with.
+			// We can infer from which sprites are visible.
+			for (int i = 1; i <= 4; i++) {
+				if (i < (int)_lSprites.size() && _lSprites[i - 1] && _lSprites[i - 1]->onoff)
+					_liftObject = i;
+			}
+		}
+		// Hide all object sprites except the active one
+		for (int i = 1; i <= 4; i++) {
+			if (i != _liftObject)
+				setObjectOnOff(i, false);
+		}
+		if (_liftUp) {
+			setObjectState(9, 2); // up arrow OFF
+			setObjectState(8, 1); // down arrow ON
+			setObjectState(_liftObject, 5); // object at top
+		} else {
+			setObjectState(9, 1); // up arrow ON
+			setObjectState(8, 2); // down arrow OFF
+			setObjectState(_liftObject, 1); // object at bottom
+		}
+		drawAnimation();
+		_gfx->copyToScreen();
 	}
 
 	while (_animationRunning && !shouldQuit()) {
@@ -996,10 +1035,33 @@ void ColonyEngine::handleAnimationClick(int item) {
 			_animationRunning = false;
 		}
 	} else if (_animationName == "lift") {
-		// Original DoLift: item==8 puts down (when carrying), item==9 picks up (when empty)
-		if (item == 8 || item == 9) {
+		// Original DoLift state machine:
+		// item 8 = lower button (active when _liftUp)
+		// item 9 = raise button (active when !_liftUp)
+		if (item == 8 && _liftUp) {
+			// Lower the object: animate states 5→1
+			setObjectState(8, 2); // lower arrow OFF
+			setObjectState(9, 1); // raise arrow ON
+			for (int i = 5; i >= 1; i--) {
+				setObjectState(_liftObject, i);
+				drawAnimation();
+				_gfx->copyToScreen();
+				responsiveAnimationDelay(_system, 50);
+			}
+			_liftUp = false;
+			_animationResult = 1;
+		} else if (item == 9 && !_liftUp) {
+			// Raise the object: animate states 1→5
+			setObjectState(9, 2); // raise arrow OFF
+			setObjectState(8, 1); // lower arrow ON
+			for (int i = 1; i <= 5; i++) {
+				setObjectState(_liftObject, i);
+				drawAnimation();
+				_gfx->copyToScreen();
+				responsiveAnimationDelay(_system, 50);
+			}
+			_liftUp = true;
 			_animationResult = 1;
-			_animationRunning = false;
 		}
 	}
 }
@@ -1147,6 +1209,12 @@ void ColonyEngine::handleKeypadClick(int item) {
 					_coreState[_coreIndex] = 0;
 				_gametest = true;
 				debug("Reactor: code accepted! New coreState=%d", _coreState[_coreIndex]);
+			} else {
+				// Original ganimate.c: wrong reactor code → Terminate(TRUE)
+				debug("Reactor: WRONG CODE — terminating game");
+				_animationRunning = false;
+				terminateGame(true);
+				return;
 			}
 			_animationRunning = false;
 		} else if (_animationName == "security") {
@@ -1157,6 +1225,16 @@ void ColonyEngine::handleKeypadClick(int item) {
 					match = false;
 			}
 			if (match || isBackdoorCode111111(_animDisplay)) {
+				// Original ganimate.c: flash all 10 keypad objects on success
+				for (int i = 1; i <= 10; i++)
+					setObjectState(i, 2);
+				drawAnimation();
+				_gfx->copyToScreen();
+				responsiveAnimationDelay(_system, 200);
+				for (int i = 1; i <= 10; i++)
+					setObjectState(i, 1);
+				drawAnimation();
+				_gfx->copyToScreen();
 				_unlocked = true;
 				_gametest = true;
 			}
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index e59db455946..07e0ad83456 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -663,6 +663,8 @@ private:
 	bool _animationRunning;
 	int _animationResult;
 	bool _doorOpen;
+	int _liftObject;  // sprite index for the carried object in lift animation
+	bool _liftUp;     // current lift state: true=raised, false=lowered
 	int _elevatorFloor;
 	int _airlockX = -1;
 	int _airlockY = -1;
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index c2d0368086d..359d5e3dca8 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -283,7 +283,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 				_sound->play(Sound::kDrop); // GANIMATE.C DoLift: DoDropSound()
 				_animationResult = 0;
 				playAnimation();
-				if (!_animationResult) {
+				if (_animationResult) {
 					_carryType = 0;
 					_coreState[_coreIndex] = 1;
 					_corePower[_coreIndex] = _corePower[2];


Commit: 85b854024e76e5d3077aed8c7cd7eb646bac27b0
    https://github.com/scummvm/scummvm/commit/85b854024e76e5d3077aed8c7cd7eb646bac27b0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:50+02:00

Commit Message:
COLONY: projector fixes

Changed paths:
    engines/colony/render_objects.cpp


diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 3156a97397a..25231b01181 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -106,16 +106,93 @@ static bool isProjectedSurfaceVisible(const int *surface, int pointCount, const
 		const long dy = screenY[cur] - screenY[next];
 		const long dxp = screenX[next2] - screenX[next];
 		const long dyp = screenY[next2] - screenY[next];
-		const long cross = dx * dyp - dy * dxp;
-		if (cross < 0)
-			return true;
-		if (cross > 0)
-			return false;
+
+		if (dx < 0) {
+			if (dy == 0) {
+				if (dyp > 0)
+					return false;
+				if (dyp < 0)
+					return true;
+			} else {
+				const long b = dy * dxp - dx * dyp;
+				if (b > 0)
+					return false;
+				if (b < 0)
+					return true;
+			}
+		} else if (dx > 0) {
+			if (dy == 0) {
+				if (dyp < 0)
+					return false;
+				if (dyp > 0)
+					return true;
+			} else {
+				const long b = dx * dyp - dy * dxp;
+				if (b < 0)
+					return false;
+				if (b > 0)
+					return true;
+			}
+		} else {
+			if (dy < 0) {
+				if (dxp > 0)
+					return true;
+				if (dxp < 0)
+					return false;
+			}
+			if (dy > 0) {
+				if (dxp < 0)
+					return true;
+				if (dxp > 0)
+					return false;
+			}
+		}
 	}
 
 	return false;
 }
 
+static bool isProjectedPrismSurfaceVisible(const Common::Rect &screenR, const Colony::Thing &thing,
+		const Colony::ColonyEngine::PrismPartDef &def, bool useLook, int surfaceIndex,
+		uint8 cameraLook, int8 cameraLookY, int cameraX, int cameraY,
+		const int *sint, const int *cost) {
+	if (surfaceIndex < 0 || surfaceIndex >= def.surfaceCount)
+		return false;
+
+	const int pointCount = def.surfaces[surfaceIndex][1];
+	if (pointCount < 3)
+		return false;
+
+	const uint8 ang = (useLook ? thing.where.look : thing.where.ang) + 32;
+	const long rotCos = cost[ang];
+	const long rotSin = sint[ang];
+	int projectedX[32];
+	int projectedY[32];
+	bool projected[32];
+
+	assert(def.pointCount <= ARRAYSIZE(projectedX));
+
+	for (int i = 0; i < def.pointCount; ++i) {
+		const int ox = def.points[i][0];
+		const int oy = def.points[i][1];
+		const int oz = def.points[i][2];
+		const long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
+		const long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+		projected[i] = projectCorridorPointRaw(screenR, cameraLook, cameraLookY, sint, cost, cameraX, cameraY,
+												   (float)(rx + thing.where.xloc), (float)(ry + thing.where.yloc), (float)(oz - 160),
+												   projectedX[i], projectedY[i]);
+	}
+
+	const int *surface = &def.surfaces[surfaceIndex][2];
+	for (int i = 0; i < pointCount; ++i) {
+		const int pointIdx = surface[i];
+		if (pointIdx < 0 || pointIdx >= def.pointCount || !projected[pointIdx])
+			return false;
+	}
+
+	return isProjectedSurfaceVisible(surface, pointCount, projectedX, projectedY);
+}
+
 static const int kScreenPts[8][3] = {
 	{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
 	{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
@@ -1370,19 +1447,32 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		_gfx->setDepthRange(0.0, 1.0);
 		draw3DPrism(obj, kCryoParts[0], false, -1, true, false); // top second
 		break;
-	case kObjProjector:
-		// Projector sits on table  draw table first, then projector parts
+	case kObjProjector: {
+		// Match the original Mac MakeProjector() ordering.
+		// The body/lens order flips depending on whether the front lens cap
+		// surface is facing the camera, because each part uses a separate
+		// depth range bucket rather than true inter-part depth sorting.
+		const bool lensFrontVisible = isProjectedPrismSurfaceVisible(_screenR, obj, kProjectorParts[2], false, 6,
+															_me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
 		_gfx->setDepthRange(0.008, 1.0);
-		draw3DPrism(obj, kTableParts[0], false, -1, true, false); // table base
+		draw3DPrism(obj, kTableParts[1], false, -1, true, false); // table base
 		_gfx->setDepthRange(0.006, 1.0);
-		draw3DPrism(obj, kTableParts[1], false, -1, true, false); // table top
+		draw3DPrism(obj, kTableParts[0], false, -1, true, false); // table top
 		_gfx->setDepthRange(0.004, 1.0);
-		draw3DPrism(obj, kProjectorParts[1], false, -1, true, false); // stand
-		_gfx->setDepthRange(0.002, 1.0);
-		draw3DPrism(obj, kProjectorParts[0], false, -1, true, false); // body
-		_gfx->setDepthRange(0.0, 1.0);
-		draw3DPrism(obj, kProjectorParts[2], false, -1, true, false); // lens
+		draw3DPrism(obj, kProjectorParts[1], false, -1, true, true); // stand
+		if (lensFrontVisible) {
+			_gfx->setDepthRange(0.002, 1.0);
+			draw3DPrism(obj, kProjectorParts[0], false, -1, true, false); // body
+			_gfx->setDepthRange(0.0, 1.0);
+			draw3DPrism(obj, kProjectorParts[2], false, -1, true, false); // lens
+		} else {
+			_gfx->setDepthRange(0.002, 1.0);
+			draw3DPrism(obj, kProjectorParts[2], false, -1, true, false); // lens
+			_gfx->setDepthRange(0.0, 1.0);
+			draw3DPrism(obj, kProjectorParts[0], false, -1, true, false); // body
+		}
 		break;
+	}
 	case kObjTub:
 		for (int i = 0; i < 2; i++) {
 			_gfx->setDepthRange((1 - i) * 0.002, 1.0);


Commit: 9dee3813e1ea2726d948e46ca68b33a575a2fe30
    https://github.com/scummvm/scummvm/commit/9dee3813e1ea2726d948e46ca68b33a575a2fe30
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:50+02:00

Commit Message:
COLONY: pyramid/eyeball fixes

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


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 07e0ad83456..299beb2e86d 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -567,7 +567,9 @@ private:
 	void draw3DLeaf(const Thing &obj, const PrismPartDef &def);
 	void draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 	                  int pt1x, int pt1y, int pt1z, uint32 fillColor, uint32 outlineColor, bool accumulateBounds = false);
-	void drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride);
+	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);
 	bool drawStaticObjectPrisms3D(Thing &obj);
 	void initRobots();
 	void renderCorridor3D();
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 25231b01181..3d938cb373d 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -20,13 +20,11 @@
  */
 
 #include "colony/colony.h"
+#include "colony/render_internal.h"
 #include "common/system.h"
 
 namespace Colony {
 
-// Must match the value in render_internal.h.
-static const int kColorCorridorWall = 1000;
-
 static uint32 packEyeOverlayMacColor(const uint16 rgb[3]) {
 	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
 }
@@ -35,6 +33,7 @@ static int mapEyeOverlayColorToMacColor(int colorIdx, int level) {
 	switch (colorIdx) {
 	case kColorPupil:        return 36; // c_pupil
 	case kColorEyeball:      return 34; // c_eyeball
+	case kColorIris:         return 35; // c_iris
 	case kColorEyeIris:      return 32; // c_eye
 	case kColorMiniEyeIris:  return 33; // c_meye
 	case kColorQueenEye:     return 49; // c_equeen
@@ -49,6 +48,7 @@ static uint8 mapEyeOverlayColorToDOSFill(int colorIdx, int level) {
 		return 0;
 	case kColorEyeball:
 		return 15;
+	case kColorIris:
 	case kColorEyeIris:
 	case kColorMiniEyeIris:
 	case kColorQueenEye:
@@ -62,6 +62,22 @@ static uint8 mapEyeOverlayColorToDOSFill(int colorIdx, int level) {
 	}
 }
 
+static int mapEyeOverlayColorToMacPattern(int colorIdx) {
+	switch (colorIdx) {
+	case kColorBlack:
+	case kColorPupil:
+		return kPatternBlack;
+	case kColorEyeball:
+		return kPatternWhite;
+	case kColorIris:
+	case kColorEyeIris:
+	case kColorMiniEyeIris:
+	case kColorQueenEye:
+	default:
+		return kPatternGray;
+	}
+}
+
 static bool projectCorridorPointRaw(const Common::Rect &screenR, uint8 look, int8 lookY,
                                     const int *sint, const int *cost, int camX, int camY,
                                     float worldX, float worldY, float worldZ,
@@ -874,7 +890,7 @@ static const int kPyramidSurf[4][8] = {
 static const int kPShadowPts[4][3] = {
 	{-75, 75, 0}, {75, 75, 0}, {75, -75, 0}, {-75, -75, 0}
 };
-static const int kPShadowSurf[1][8] = {{kColorShadow, 4, 3, 2, 1, 0, 3, 0}};
+static const int kPShadowSurf[1][8] = {{kColorBlack, 4, 3, 2, 1, 0, 3, 0}};
 // Pyramid eye (ball on top)
 static const int kPIrisPts[4][3] = {
 	{15, 0, 185}, {15, 15, 200}, {15, 0, 215}, {15, -15, 200}
@@ -914,7 +930,7 @@ static const int kUPyramidSurf[5][8] = {
 static const int kUPShadowPts[4][3] = {
 	{-25, 25, 0}, {25, 25, 0}, {25, -25, 0}, {-25, -25, 0}
 };
-static const int kUPShadowSurf[1][8] = {{kColorShadow, 4, 3, 2, 1, 0, 3, 0}};
+static const int kUPShadowSurf[1][8] = {{kColorBlack, 4, 3, 2, 1, 0, 3, 0}};
 
 static const Colony::ColonyEngine::PrismPartDef kUPyramidBodyDef = {5, kUPyramidPts, 5, kUPyramidSurf};
 static const Colony::ColonyEngine::PrismPartDef kUPShadowDef = {4, kUPShadowPts, 1, kUPShadowSurf};
@@ -1172,13 +1188,17 @@ void ColonyEngine::drawStaticObjects() {
 	}
 }
 
-void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride) {
+void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride, bool forceVisible) {
 	if (def.pointCount < 4 || def.surfaceCount < 1)
 		return;
 
 	const uint8 ang = (useLook ? thing.where.look : thing.where.ang) + 32;
 	const long rotCos = _cost[ang];
 	const long rotSin = _sint[ang];
+	const bool lit = (_corePower[_coreIndex] > 0);
+	float transformedX[32];
+	float transformedY[32];
+	float transformedZ[32];
 	int projectedX[32];
 	int projectedY[32];
 	bool projected[32];
@@ -1191,8 +1211,11 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 		const int oz = def.points[i][2];
 		const long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
 		const long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+		transformedX[i] = (float)(rx + thing.where.xloc);
+		transformedY[i] = (float)(ry + thing.where.yloc);
+		transformedZ[i] = (float)(oz - 160);
 		projected[i] = projectCorridorPointRaw(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
-		                                       (float)(rx + thing.where.xloc), (float)(ry + thing.where.yloc), (float)(oz - 160),
+		                                       transformedX[i], transformedY[i], transformedZ[i],
 		                                       projectedX[i], projectedY[i]);
 	}
 
@@ -1206,7 +1229,7 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 			return;
 	}
 
-	if (!isProjectedSurfaceVisible(surface, pointCount, projectedX, projectedY))
+	if (!forceVisible && !isProjectedSurfaceVisible(surface, pointCount, projectedX, projectedY))
 		return;
 
 	const int left = projectedX[3];
@@ -1216,23 +1239,67 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 	if (right <= left || bottom <= top)
 		return;
 
-	const int cx = (left + right) / 2;
-	const int cy = (top + bottom) / 2;
-	const int rx = MAX(1, (right - left) / 2);
-	const int ry = MAX(1, (bottom - top) / 2);
 	const int fillColorIdx = (colorOverride >= 0) ? colorOverride : def.surfaces[0][0];
-	uint32 fillColor;
+
+	const float centerX = (transformedX[0] + transformedX[1] + transformedX[2] + transformedX[3]) * 0.25f;
+	const float centerY = (transformedY[0] + transformedY[1] + transformedY[2] + transformedY[3]) * 0.25f;
+	const float centerZ = (transformedZ[0] + transformedZ[1] + transformedZ[2] + transformedZ[3]) * 0.25f;
+	const float axisHX = (transformedX[1] - transformedX[3]) * 0.5f;
+	const float axisHY = (transformedY[1] - transformedY[3]) * 0.5f;
+	const float axisHZ = (transformedZ[1] - transformedZ[3]) * 0.5f;
+	const float axisVX = (transformedX[2] - transformedX[0]) * 0.5f;
+	const float axisVY = (transformedY[2] - transformedY[0]) * 0.5f;
+	const float axisVZ = (transformedZ[2] - transformedZ[0]) * 0.5f;
+
+	static const int kSegments = 20;
+	float px[kSegments];
+	float py[kSegments];
+	float pz[kSegments];
+	for (int i = 0; i < kSegments; ++i) {
+		const float angleRad = 2.0f * (float)M_PI * (float)i / (float)kSegments;
+		const float ca = cosf(angleRad);
+		const float sa = sinf(angleRad);
+		px[i] = centerX + ca * axisHX + sa * axisVX;
+		py[i] = centerY + ca * axisHY + sa * axisVY;
+		pz[i] = centerZ + ca * axisHZ + sa * axisVZ;
+	}
+
 	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
 		const int macColorIdx = mapEyeOverlayColorToMacColor(fillColorIdx, _level);
-		const bool useForeground = (fillColorIdx == kColorPupil || fillColorIdx == kColorBlack);
-		fillColor = packEyeOverlayMacColor(useForeground ? _macColors[macColorIdx].fg : _macColors[macColorIdx].bg);
+		int pattern = _macColors[macColorIdx].pattern;
+		uint32 fg = packEyeOverlayMacColor(_macColors[macColorIdx].fg);
+		uint32 bg = packEyeOverlayMacColor(_macColors[macColorIdx].bg);
+		uint32 line = 0xFF000000;
+		if (!lit) {
+			fg = 0xFF000000;
+			bg = 0xFF000000;
+			line = 0xFF000000;
+			pattern = kPatternBlack;
+		}
+		const byte *stipple = setupMacPattern(_gfx, pattern, fg, bg);
+		_gfx->draw3DPolygon(px, py, pz, kSegments, line);
+		if (stipple)
+			_gfx->setStippleData(nullptr);
+	} else if (lit) {
+		if (_renderMode == Common::kRenderMacintosh) {
+			int pattern = mapEyeOverlayColorToMacPattern(fillColorIdx);
+			if (pattern == kPatternClear)
+				return;
+			if (!_wireframe)
+				_gfx->setWireframe(true, pattern == kPatternBlack ? 0 : 255);
+			_gfx->setStippleData(kMacStippleData[pattern]);
+			_gfx->draw3DPolygon(px, py, pz, kSegments, 0);
+			_gfx->setStippleData(nullptr);
+		} else {
+			if (!_wireframe)
+				_gfx->setWireframe(true, mapEyeOverlayColorToDOSFill(fillColorIdx, _level));
+			_gfx->draw3DPolygon(px, py, pz, kSegments, 0);
+		}
 	} else {
-		fillColor = mapEyeOverlayColorToDOSFill(fillColorIdx, _level);
+		if (!_wireframe)
+			_gfx->setWireframe(true, 0);
+		_gfx->draw3DPolygon(px, py, pz, kSegments, 15);
 	}
-	const uint32 outlineColor = (_renderMode == Common::kRenderMacintosh) ? 0xFF000000 : (uint32)kColorBlack;
-
-	_gfx->fillEllipse(cx, cy, rx, ry, fillColor);
-	_gfx->drawEllipse(cx, cy, rx, ry, outlineColor);
 
 	thing.where.xmn = MIN(thing.where.xmn, left);
 	thing.where.xmx = MAX(thing.where.xmx, right);
@@ -1240,6 +1307,19 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 	thing.where.zmx = MAX(thing.where.zmx, bottom);
 }
 
+void ColonyEngine::drawEyeOverlays3D(Thing &thing, const PrismPartDef &irisDef, int irisColorOverride,
+		const PrismPartDef &pupilDef, int pupilColorOverride, bool useLook) {
+	if (!isProjectedPrismSurfaceVisible(_screenR, thing, irisDef, useLook, 0,
+			_me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost)) {
+		return;
+	}
+
+	// Original Mac eye rendering uses the iris quad as the single visibility
+	// gate, then draws both iris and pupil unconditionally.
+	drawPrismOval3D(thing, irisDef, useLook, irisColorOverride, true);
+	drawPrismOval3D(thing, pupilDef, useLook, pupilColorOverride, true);
+}
+
 bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	const int eyeballColor = (_level == 1 || _level == 7) ? kColorPupil : kColorEyeball;
 	const int pupilColor = (_level == 1 || _level == 7) ? kColorEyeball : kColorPupil;
@@ -1523,15 +1603,20 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			break;
 		}
 		draw3DSphere(obj, 0, 0, 100, 0, 0, 200, eyeballColor, kColorBlack, true);
-		drawPrismOval3D(obj, kEyeIrisDef, false, -1);
-		drawPrismOval3D(obj, kEyePupilDef, false, pupilColor);
+		drawEyeOverlays3D(obj, kEyeIrisDef, -1, kEyePupilDef, pupilColor, false);
 		break;
 	case kRobPyramid:
+		_gfx->setDepthRange(0.030, 1.0);
 		draw3DPrism(obj, kPShadowDef, false, -1, true, false);
+		_gfx->setDepthRange(0.020, 1.0);
 		draw3DPrism(obj, kPyramidBodyDef, false, -1, true, false);
+		_gfx->setDepthRange(0.004, 1.0);
+		_gfx->setDepthState(true, false);
+		_gfx->setDepthRange(0.0, 1.0);
 		draw3DSphere(obj, 0, 0, 175, 0, 0, 200, eyeballColor, kColorBlack, true);
-		drawPrismOval3D(obj, kPIrisDef, false, -1);
-		drawPrismOval3D(obj, kPPupilDef, false, pupilColor);
+		drawEyeOverlays3D(obj, kPIrisDef, -1, kPPupilDef, pupilColor, false);
+		_gfx->setDepthState(true, true);
+		_gfx->setDepthRange(0.0, 1.0);
 		break;
 	case kRobCube:
 		draw3DPrism(obj, kCubeBodyDef, false, -1, true, false);
@@ -1542,8 +1627,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kRobFEye:
 		draw3DSphere(obj, 0, 0, 0, 0, 0, 100, eyeballColor, kColorBlack, true);
-		drawPrismOval3D(obj, kFEyeIrisDef, false, -1);
-		drawPrismOval3D(obj, kFEyePupilDef, false, pupilColor);
+		drawEyeOverlays3D(obj, kFEyeIrisDef, -1, kFEyePupilDef, pupilColor, false);
 		break;
 	case kRobFPyramid:
 		draw3DPrism(obj, kFPyramidBodyDef, false, -1, true, false);
@@ -1556,8 +1640,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kRobSEye:
 		draw3DSphere(obj, 0, 0, 0, 0, 0, 50, eyeballColor, kColorBlack, true);
-		drawPrismOval3D(obj, kSEyeIrisDef, false, -1);
-		drawPrismOval3D(obj, kSEyePupilDef, false, pupilColor);
+		drawEyeOverlays3D(obj, kSEyeIrisDef, -1, kSEyePupilDef, pupilColor, false);
 		break;
 	case kRobSPyramid:
 		draw3DPrism(obj, kSPyramidBodyDef, false, -1, true, false);
@@ -1570,8 +1653,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kRobMEye:
 		draw3DSphere(obj, 0, 0, 0, 0, 0, 25, eyeballColor, kColorBlack, true);
-		drawPrismOval3D(obj, kMEyeIrisDef, false, kColorMiniEyeIris);
-		drawPrismOval3D(obj, kMEyePupilDef, false, pupilColor);
+		drawEyeOverlays3D(obj, kMEyeIrisDef, kColorMiniEyeIris, kMEyePupilDef, pupilColor, false);
 		break;
 	case kRobMPyramid:
 		draw3DPrism(obj, kMPyramidBodyDef, false, -1, true, false);
@@ -1614,8 +1696,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 
 			draw3DPrism(obj, farWing, false, wingColor, true, true);
 			draw3DSphere(farEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
-			drawPrismOval3D(farEye, kQIrisDef, true, -1);
-			drawPrismOval3D(farEye, kQPupilDef, true, pupilColor);
+			drawEyeOverlays3D(farEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
 			if (farEye.where.xmn < obj.where.xmn)
 				obj.where.xmn = farEye.where.xmn;
 			if (farEye.where.xmx > obj.where.xmx)
@@ -1630,8 +1711,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 
 			draw3DPrism(obj, nearWing, false, wingColor, true, true);
 			draw3DSphere(nearEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
-			drawPrismOval3D(nearEye, kQIrisDef, true, -1);
-			drawPrismOval3D(nearEye, kQPupilDef, true, pupilColor);
+			drawEyeOverlays3D(nearEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
 			if (nearEye.where.xmn < obj.where.xmn)
 				obj.where.xmn = nearEye.where.xmn;
 			if (nearEye.where.xmx > obj.where.xmx)
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 3bdee5b012e..e1d58af1431 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -551,6 +551,23 @@ void OpenGLRenderer::scroll(int dx, int dy, uint32 background) {
 }
 
 void OpenGLRenderer::drawEllipse(int x, int y, int rx, int ry, uint32 color) {
+	GLint savedViewport[4];
+	GLint savedScissor[4];
+	glGetIntegerv(GL_VIEWPORT, savedViewport);
+	glGetIntegerv(GL_SCISSOR_BOX, savedScissor);
+	const int32 screenHeight = _system->getHeight();
+	glViewport(_screenViewport.left, screenHeight - _screenViewport.bottom, _screenViewport.width(), _screenViewport.height());
+	glScissor(_screenViewport.left, screenHeight - _screenViewport.bottom, _screenViewport.width(), _screenViewport.height());
+
+	glMatrixMode(GL_PROJECTION);
+	glPushMatrix();
+	glLoadIdentity();
+	glOrtho(0, _width, _height, 0, -1, 1);
+	glMatrixMode(GL_MODELVIEW);
+	glPushMatrix();
+	glLoadIdentity();
+	glDisable(GL_DEPTH_TEST);
+
 	useColor(color);
 	glBegin(GL_LINE_LOOP);
 	for (int i = 0; i < 360; i += 10) {
@@ -558,9 +575,34 @@ void OpenGLRenderer::drawEllipse(int x, int y, int rx, int ry, uint32 color) {
 		glVertex2f(x + cos(rad) * (float)rx, y + sin(rad) * (float)ry);
 	}
 	glEnd();
+
+	glEnable(GL_DEPTH_TEST);
+	glPopMatrix();
+	glMatrixMode(GL_PROJECTION);
+	glPopMatrix();
+	glMatrixMode(GL_MODELVIEW);
+	glViewport(savedViewport[0], savedViewport[1], savedViewport[2], savedViewport[3]);
+	glScissor(savedScissor[0], savedScissor[1], savedScissor[2], savedScissor[3]);
 }
 
 void OpenGLRenderer::fillEllipse(int x, int y, int rx, int ry, uint32 color) {
+	GLint savedViewport[4];
+	GLint savedScissor[4];
+	glGetIntegerv(GL_VIEWPORT, savedViewport);
+	glGetIntegerv(GL_SCISSOR_BOX, savedScissor);
+	const int32 screenHeight = _system->getHeight();
+	glViewport(_screenViewport.left, screenHeight - _screenViewport.bottom, _screenViewport.width(), _screenViewport.height());
+	glScissor(_screenViewport.left, screenHeight - _screenViewport.bottom, _screenViewport.width(), _screenViewport.height());
+
+	glMatrixMode(GL_PROJECTION);
+	glPushMatrix();
+	glLoadIdentity();
+	glOrtho(0, _width, _height, 0, -1, 1);
+	glMatrixMode(GL_MODELVIEW);
+	glPushMatrix();
+	glLoadIdentity();
+	glDisable(GL_DEPTH_TEST);
+
 	useColor(color);
 	glBegin(GL_POLYGON);
 	for (int i = 0; i < 360; i += 10) {
@@ -568,6 +610,14 @@ void OpenGLRenderer::fillEllipse(int x, int y, int rx, int ry, uint32 color) {
 		glVertex2f(x + cos(rad) * (float)rx, y + sin(rad) * (float)ry);
 	}
 	glEnd();
+
+	glEnable(GL_DEPTH_TEST);
+	glPopMatrix();
+	glMatrixMode(GL_PROJECTION);
+	glPopMatrix();
+	glMatrixMode(GL_MODELVIEW);
+	glViewport(savedViewport[0], savedViewport[1], savedViewport[2], savedViewport[3]);
+	glScissor(savedScissor[0], savedScissor[1], savedScissor[2], savedScissor[3]);
 }
 
 void OpenGLRenderer::fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) {


Commit: 237fda5338c8ad3f7a6a68382cb2ec3233ba5fe5
    https://github.com/scummvm/scummvm/commit/237fda5338c8ad3f7a6a68382cb2ec3233ba5fe5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:51+02:00

Commit Message:
COLONY: slower energy drain

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/map.cpp
    engines/colony/movement.cpp
    engines/colony/think.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index cf846603674..a90bb4bab9b 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -838,20 +838,19 @@ Common::Error ColonyEngine::run() {
 			cThink();
 		}
 
-		// Periodic equipment power drain every FCOUNT(32) game ticks at ~8fps.
-		// Mac display.c implements this; DOS IBM_DISP.C defines FCOUNT but
-		// never applies the drain — a bug in the original DOS port that
-		// removed the intended equipment energy cost trade-off.
-		if (_gameMode == kModeColony && now - lastCenterTick >= 125) {
+		// Periodic equipment power drain from Mac display.c.
+		// Original: foodcount-- once per Display() call at hardware frame
+		// rate (~5fps on Mac Plus), fires SetPower every FCOUNT(32) frames
+		// → drain period ≈ 32/5 = 6.4 seconds.  DOS IBM_DISP.C defines
+		// FCOUNT but never applies the drain.
+		// We use a fixed 6400ms wall-clock interval to match the original
+		// Mac Plus cadence regardless of our render frame rate.
+		if (_gameMode == kModeColony && now - lastCenterTick >= 6400) {
 			lastCenterTick = now;
-			_foodCount--;
-			if (_foodCount <= 0) {
-				const int a2 = _armor * _armor;
-				const int w2 = _weapons * _weapons;
-				if (a2 > 0 || w2 > 0)
-					setPower(-_level * a2, -_level * (a2 + w2), -_level * w2);
-				_foodCount = 32;
-			}
+			const int a2 = _armor * _armor;
+			const int w2 = _weapons * _weapons;
+			if (a2 > 0 || w2 > 0)
+				setPower(-_level * a2, -_level * (a2 + w2), -_level * w2);
 		}
 
 		// Original Mac gmain.c: BThink()+Display() both at hardware frame rate.
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 299beb2e86d..097a90eae9d 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -109,6 +109,27 @@ enum RobotType {
 	kRobSnoop = 20
 };
 
+// 3D bounding radius (max XY extent from center) for each robot type.
+// Used by clampToWalls to keep robot geometry from intersecting wall polygons.
+// The original 2D wireframe renderer drew walls over robots (painter's algorithm);
+// in OpenGL 3D both exist as real geometry, so padding is needed.
+// Capped at 112 so the robot keeps at least 32 units of movement freedom
+// within a 256-unit cell (256 - 2*112 = 32).
+inline int robotWallPad(int robotType) {
+	static const int kMaxPad = 112;
+	switch (robotType) {
+	case kRobEye:      return 66;
+	case kRobPyramid:
+	case kRobUPyramid: return 85;
+	case kRobCube:     return 100;
+	case kRobQueen:    return kMaxPad; // actual 120
+	case kRobDrone:
+	case kRobSoldier:  return kMaxPad; // actual 130
+	case kRobSnoop:    return kMaxPad; // actual 180
+	default:           return 40;      // eggs and other small types
+	}
+}
+
 enum ObjectType {
 	kObjDesk = 21,
 	kObjPlant = 22,
@@ -265,6 +286,7 @@ struct Locate {
 	int type;
 	int dx, dy;
 	int dist;
+	int wallPad; // 3D bounding radius for wall clamping (0 = use default kWallPad)
 };
 
 struct Thing {
@@ -473,7 +495,6 @@ private:
 	uint32 _blackoutColor = 0;
 	uint32 _lastClickTime = 0;
 	uint32 _displayCount = 0; // Frame counter for COLOR wall animation (Mac: count)
-	int _foodCount = 32;      // Mac display.c: periodic power drain counter (FCOUNT=32)
 	uint32 _lastHotfootTime = 0;  // Time-gate for HOTFOOT damage (~8fps)
 	uint32 _lastAnimUpdate = 0;
 	uint32 _lastWarningChimeTime = 0;
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 8d8498a935c..cf9f03f0171 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -188,6 +188,7 @@ void ColonyEngine::resetObjectSlot(int slot, int type, int xloc, int yloc, uint8
 	obj.where.ang = ang;
 	obj.where.look = ang;
 	obj.where.lookx = 0;
+	obj.where.wallPad = robotWallPad(type);
 	obj.count = 0;
 	obj.time = 10 + (_randomSource.getRandomNumber(0x3F) & 0x3F);
 	obj.grow = 0;
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 5352ee929fc..231d5029ca0 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -230,7 +230,9 @@ bool ColonyEngine::hasInteractiveWallFeature(int cx, int cy, int dir) const {
 }
 
 void ColonyEngine::clampToWalls(Locate *p) {
-	static const int kWallPad = 40;
+	static const int kPlayerWallPad = 40;
+	const bool isPlayer = (p == &_me);
+	const int pad = isPlayer ? kPlayerWallPad : p->wallPad;
 	int cx = p->xindex;
 	int cy = p->yindex;
 	int cellMinX = cx << 8;
@@ -239,17 +241,19 @@ void ColonyEngine::clampToWalls(Locate *p) {
 	int cellMaxY = cellMinY + 255;
 
 	// South wall of this cell (at cellMinY boundary)
-	if ((wallAt(cx, cy) & 0x01) && !hasInteractiveWallFeature(cx, cy, kDirSouth))
-		p->yloc = MAX(p->yloc, cellMinY + kWallPad);
+	// Player can skip clamping near interactive features (doors etc.) to walk through;
+	// robots cannot use features, so they always get clamped.
+	if ((wallAt(cx, cy) & 0x01) && !(isPlayer && hasInteractiveWallFeature(cx, cy, kDirSouth)))
+		p->yloc = MAX(p->yloc, cellMinY + pad);
 	// North wall (at cellMaxY+1 boundary = south wall of cell above)
-	if ((wallAt(cx, cy + 1) & 0x01) && !hasInteractiveWallFeature(cx, cy, kDirNorth))
-		p->yloc = MIN(p->yloc, cellMaxY - kWallPad);
+	if ((wallAt(cx, cy + 1) & 0x01) && !(isPlayer && hasInteractiveWallFeature(cx, cy, kDirNorth)))
+		p->yloc = MIN(p->yloc, cellMaxY - pad);
 	// West wall of this cell (at cellMinX boundary)
-	if ((wallAt(cx, cy) & 0x02) && !hasInteractiveWallFeature(cx, cy, kDirWest))
-		p->xloc = MAX(p->xloc, cellMinX + kWallPad);
+	if ((wallAt(cx, cy) & 0x02) && !(isPlayer && hasInteractiveWallFeature(cx, cy, kDirWest)))
+		p->xloc = MAX(p->xloc, cellMinX + pad);
 	// East wall (at cellMaxX+1 boundary = west wall of cell to right)
-	if ((wallAt(cx + 1, cy) & 0x02) && !hasInteractiveWallFeature(cx, cy, kDirEast))
-		p->xloc = MIN(p->xloc, cellMaxX - kWallPad);
+	if ((wallAt(cx + 1, cy) & 0x02) && !(isPlayer && hasInteractiveWallFeature(cx, cy, kDirEast)))
+		p->xloc = MIN(p->xloc, cellMaxX - pad);
 }
 
 void ColonyEngine::clampToDiagonalWalls(Locate *p) {
@@ -328,7 +332,8 @@ int ColonyEngine::checkwallMoveTo(int xnew, int ynew, int xind2, int yind2, Loca
 	pobject->xloc = xnew;
 	pobject->yloc = ynew;
 	clampToWalls(pobject);
-	clampToDiagonalWalls(pobject);
+	if (pobject == &_me)
+		clampToDiagonalWalls(pobject);
 	return 0;
 }
 
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index e96cc31a33f..4ac5954f362 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -312,6 +312,7 @@ void ColonyEngine::droneThink(int num) {
 		copyOverflowObjectToSlot(num);
 	} else {
 		obj.type = kRobDrone;
+		obj.where.wallPad = robotWallPad(kRobDrone);
 		obj.grow = 0;
 		obj.where.power[1] = 10 + ((_randomSource.getRandomNumber(15) & 0x0F) << _level);
 	}
@@ -599,6 +600,7 @@ void ColonyEngine::bigGrow(int num) {
 		if (_robotArray[xindex][yindex] == num)
 			_robotArray[xindex][yindex] = 0;
 		obj.type += 4;
+		obj.where.wallPad = robotWallPad(obj.type);
 		obj.count = 0;
 		obj.time = 10 + (_randomSource.getRandomNumber(63) & 0x3F);
 	} else {
@@ -628,6 +630,7 @@ void ColonyEngine::growRobot(int num) {
 			} else {
 				obj.type += 4;
 			}
+			obj.where.wallPad = robotWallPad(obj.type);
 		}
 		break;
 
@@ -643,6 +646,7 @@ void ColonyEngine::growRobot(int num) {
 				obj.grow = 0;
 				obj.type += 4;
 			}
+			obj.where.wallPad = robotWallPad(obj.type);
 		}
 		break;
 
@@ -655,6 +659,7 @@ void ColonyEngine::growRobot(int num) {
 		    _robotArray[obj.where.xindex][obj.where.yindex] == 0) {
 			obj.count = 0;
 			obj.type -= 4;
+			obj.where.wallPad = robotWallPad(obj.type);
 			_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)num;
 			if (_foodArray[obj.where.xindex][obj.where.yindex] == num)
 				_foodArray[obj.where.xindex][obj.where.yindex] = 0;


Commit: 72e96111862acc7115184c8ed29b9fdf90fd3f23
    https://github.com/scummvm/scummvm/commit/72e96111862acc7115184c8ed29b9fdf90fd3f23
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:51+02:00

Commit Message:
COLONY: add robot eye/pupil rendering and fix savegame field

Changed paths:
    engines/colony/render_objects.cpp
    engines/colony/savegame.cpp


diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 3d938cb373d..89d65104e19 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1619,11 +1619,27 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		_gfx->setDepthRange(0.0, 1.0);
 		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);
 		break;
 	case kRobUPyramid:
+		// DOS UPYRAMID.C: draweye() drawn first, then shadow, then body.
+		// Same eye geometry as Pyramid (shared draweye function).
+		_gfx->setDepthRange(0.030, 1.0);
 		draw3DPrism(obj, kUPShadowDef, false, -1, true, false);
+		_gfx->setDepthRange(0.020, 1.0);
 		draw3DPrism(obj, kUPyramidBodyDef, false, -1, true, false);
+		_gfx->setDepthRange(0.004, 1.0);
+		_gfx->setDepthState(true, false);
+		_gfx->setDepthRange(0.0, 1.0);
+		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.0, 1.0);
 		break;
 	case kRobFEye:
 		draw3DSphere(obj, 0, 0, 0, 0, 0, 100, eyeballColor, kColorBlack, true);
@@ -1723,14 +1739,40 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		}
 		break;
 	case kRobDrone:
-		draw3DPrism(obj, kDAbdomenDef, false, -1, true, false);
-		draw3DPrism(obj, kDLLPincerDef, false, -1, true, false);
-		draw3DPrism(obj, kDRRPincerDef, false, -1, true, false);
-		draw3DPrism(obj, kDLEyeDef, false, -1, true, false);
-		draw3DPrism(obj, kDREyeDef, false, -1, true, false);
+		{
+			// DOS DRONE.C: body + seteyes()/draweyes() — same two-eye
+			// system as Queen, positioned at offsets from body center.
+			const long s1 = _sint[obj.where.ang] >> 1;
+			const long c1 = _cost[obj.where.ang] >> 1;
+			const long s2 = s1 >> 1;
+			const long c2 = c1 >> 1;
+			const long eyeBaseX = obj.where.xloc + c1;
+			const long eyeBaseY = obj.where.yloc + s1;
+
+			Thing leftEye = obj;
+			leftEye.where.xloc = (int)(eyeBaseX - s2);
+			leftEye.where.yloc = (int)(eyeBaseY + c2);
+			resetObjectBounds(_screenR, leftEye.where);
+			Thing rightEye = obj;
+			rightEye.where.xloc = (int)(eyeBaseX + s2);
+			rightEye.where.yloc = (int)(eyeBaseY - c2);
+			resetObjectBounds(_screenR, rightEye.where);
+
+			draw3DPrism(obj, kDAbdomenDef, false, -1, true, false);
+			draw3DPrism(obj, kDLLPincerDef, false, -1, true, false);
+			draw3DPrism(obj, kDRRPincerDef, false, -1, true, false);
+			draw3DPrism(obj, kDLEyeDef, false, -1, true, false);
+			draw3DPrism(obj, kDREyeDef, false, -1, true, false);
+			draw3DSphere(leftEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			drawEyeOverlays3D(leftEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
+			draw3DSphere(rightEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			drawEyeOverlays3D(rightEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
+		}
 		break;
 	case kRobSoldier:
 		{
+			// DOS DRONE.C MakeSoldier(): body + animated pincers +
+			// seteyes()/draweyes() — same two-eye system as Queen.
 			int leftPincerPts[4][3];
 			int rightPincerPts[4][3];
 			const int lookAmount = (obj.where.lookx < 0) ? -obj.where.lookx : obj.where.lookx;
@@ -1747,11 +1789,31 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			const PrismPartDef leftPincerDef = {4, leftPincerPts, 4, kDLPincerSurf};
 			const PrismPartDef rightPincerDef = {4, rightPincerPts, 4, kDRPincerSurf};
 
+			const long s1 = _sint[obj.where.ang] >> 1;
+			const long c1 = _cost[obj.where.ang] >> 1;
+			const long s2 = s1 >> 1;
+			const long c2 = c1 >> 1;
+			const long eyeBaseX = obj.where.xloc + c1;
+			const long eyeBaseY = obj.where.yloc + s1;
+
+			Thing leftEye = obj;
+			leftEye.where.xloc = (int)(eyeBaseX - s2);
+			leftEye.where.yloc = (int)(eyeBaseY + c2);
+			resetObjectBounds(_screenR, leftEye.where);
+			Thing rightEye = obj;
+			rightEye.where.xloc = (int)(eyeBaseX + s2);
+			rightEye.where.yloc = (int)(eyeBaseY - c2);
+			resetObjectBounds(_screenR, rightEye.where);
+
 			draw3DPrism(obj, kDAbdomenDef, false, kColorSoldierBody, true, false);
 			draw3DPrism(obj, leftPincerDef, false, -1, true, false);
 			draw3DPrism(obj, rightPincerDef, false, -1, true, false);
 			draw3DPrism(obj, kDLEyeDef, false, kColorSoldierEye, true, false);
 			draw3DPrism(obj, kDREyeDef, false, kColorSoldierEye, true, false);
+			draw3DSphere(leftEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			drawEyeOverlays3D(leftEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
+			draw3DSphere(rightEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			drawEyeOverlays3D(rightEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
 		}
 		break;
 	case kRobSnoop:
diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
index 27787fb3e41..c0961e9c197 100644
--- a/engines/colony/savegame.cpp
+++ b/engines/colony/savegame.cpp
@@ -479,8 +479,11 @@ Common::Error ColonyEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	if (objectCount > kMaxSaveObjects)
 		return makeCorruptSaveError(Common::String::format("object count %u exceeds max %u", objectCount, kMaxSaveObjects).c_str());
 	_objects.resize(objectCount);
-	for (uint i = 0; i < objectCount; i++)
+	for (uint i = 0; i < objectCount; i++) {
 		_objects[i] = readThing(stream);
+		// wallPad is not serialized — recompute from object type
+		_objects[i].where.wallPad = robotWallPad(_objects[i].type);
+	}
 
 	for (int i = 0; i < 16; i++)
 		_bfight[i] = readLocate(stream);


Commit: 8512bfd3fc9042b780a6f2ff1d534bdf18962343
    https://github.com/scummvm/scummvm/commit/8512bfd3fc9042b780a6f2ff1d534bdf18962343
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:51+02:00

Commit Message:
COLONY: movement fixes

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 acbc5fa55d8..23cd0377b39 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -573,6 +573,7 @@ void ColonyEngine::playAnimation() {
 				}
 			} else if (event.type == Common::EVENT_RBUTTONDOWN) {
 				// DOS: right-click exits animation (AnimControl returns FALSE on button-up)
+				debugC(1, kColonyDebugAnimation, "Animation: RBUTTONDOWN exit at pos=%d,%d", event.mouse.x, event.mouse.y);
 				_animationRunning = false;
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
 				debugC(5, kColonyDebugAnimation, "Animation Mouse: %d, %d", event.mouse.x, event.mouse.y);
@@ -606,6 +607,19 @@ void ColonyEngine::playAnimation() {
 	_system->showMouse(false);
 	CursorMan.showMouse(false);
 	CursorMan.popAllCursors();
+
+	// Purge pending mouse/keyboard events AFTER re-locking the mouse,
+	// so that any synthetic events from the lock transition are also
+	// drained and don't trigger actions (e.g. cShoot) in the main loop.
+	_system->getEventManager()->purgeMouseEvents();
+	_system->getEventManager()->purgeKeyboardEvents();
+
+	// Suppress collision sound on the first few wall hits 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.
+	_suppressCollisionSound = true;
+
 	deleteAnimation();
 }
 
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index a90bb4bab9b..a395f9b0061 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -160,6 +160,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_strafeRight = false;
 	_rotateLeft = false;
 	_rotateRight = false;
+	_sprint = false;
 	_wm = nullptr;
 	_macMenu = nullptr;
 	_menuSurface = nullptr;
@@ -980,6 +981,8 @@ Common::Error ColonyEngine::run() {
 					break;
 				}
 			} else if (event.type == Common::EVENT_KEYDOWN) {
+				if (event.kbd.keycode == Common::KEYCODE_LSHIFT || event.kbd.keycode == Common::KEYCODE_RSHIFT)
+					_sprint = true;
 				// Speed keys 1-5 remain as raw keyboard events
 				switch (event.kbd.keycode) {
 				case Common::KEYCODE_1:
@@ -993,6 +996,9 @@ Common::Error ColonyEngine::run() {
 				default:
 					break;
 				}
+			} else if (event.type == Common::EVENT_KEYUP) {
+				if (event.kbd.keycode == Common::KEYCODE_LSHIFT || event.kbd.keycode == Common::KEYCODE_RSHIFT)
+					_sprint = false;
 			} else if (event.type == Common::EVENT_LBUTTONDOWN && (_mouseLocked || _cursorShoot)) {
 				cShoot();
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
@@ -1029,8 +1035,9 @@ Common::Error ColonyEngine::run() {
 		// throttled to ~15 ticks/sec to match original key-repeat feel
 		if (now - lastMoveTick >= 66) {
 			lastMoveTick = now;
-			const int moveX = (_cost[_me.look] * (1 << _speedShift)) >> 4;
-			const int moveY = (_sint[_me.look] * (1 << _speedShift)) >> 4;
+			const int spd = _sprint ? _speedShift + 1 : _speedShift;
+			const int moveX = (_cost[_me.look] * (1 << spd)) >> 4;
+			const int moveY = (_sint[_me.look] * (1 << spd)) >> 4;
 			const int rotSpeed = 1 << (_speedShift - 1);
 
 			if (_gameMode == kModeBattle) {
@@ -1040,14 +1047,14 @@ Common::Error ColonyEngine::run() {
 					battleCommand(_me.xloc - moveX, _me.yloc - moveY);
 				if (_strafeLeft) {
 					uint8 strafeAngle = (uint8)((int)_me.look + 64);
-					int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
-					int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+					int sx = (_cost[strafeAngle] * (1 << spd)) >> 4;
+					int sy = (_sint[strafeAngle] * (1 << spd)) >> 4;
 					battleCommand(_me.xloc + sx, _me.yloc + sy);
 				}
 				if (_strafeRight) {
 					uint8 strafeAngle = (uint8)((int)_me.look - 64);
-					int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
-					int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+					int sx = (_cost[strafeAngle] * (1 << spd)) >> 4;
+					int sy = (_sint[strafeAngle] * (1 << spd)) >> 4;
 					battleCommand(_me.xloc + sx, _me.yloc + sy);
 				}
 			} else {
@@ -1057,14 +1064,14 @@ Common::Error ColonyEngine::run() {
 					cCommand(_me.xloc - moveX, _me.yloc - moveY, true);
 				if (_strafeLeft) {
 					uint8 strafeAngle = (uint8)((int)_me.look + 64);
-					int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
-					int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+					int sx = (_cost[strafeAngle] * (1 << spd)) >> 4;
+					int sy = (_sint[strafeAngle] * (1 << spd)) >> 4;
 					cCommand(_me.xloc + sx, _me.yloc + sy, true);
 				}
 				if (_strafeRight) {
 					uint8 strafeAngle = (uint8)((int)_me.look - 64);
-					int sx = (_cost[strafeAngle] * (1 << _speedShift)) >> 4;
-					int sy = (_sint[strafeAngle] * (1 << _speedShift)) >> 4;
+					int sx = (_cost[strafeAngle] * (1 << spd)) >> 4;
+					int sy = (_sint[strafeAngle] * (1 << spd)) >> 4;
 					cCommand(_me.xloc + sx, _me.yloc + sy, true);
 				}
 			}
@@ -1140,6 +1147,7 @@ bool ColonyEngine::waitForInput() {
 	_moveForward = _moveBackward = false;
 	_strafeLeft = _strafeRight = false;
 	_rotateLeft = _rotateRight = false;
+	_sprint = false;
 
 	while (!shouldQuit()) {
 		Common::Event event;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 097a90eae9d..f27bc724ca1 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -477,6 +477,7 @@ private:
 	bool _strafeRight;
 	bool _rotateLeft;
 	bool _rotateRight;
+	bool _sprint;
 
 	Common::RandomSource _randomSource;
 	Common::Point _mousePos;
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 231d5029ca0..1659dd980e6 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -1136,6 +1136,8 @@ 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] = kMeNum;
+
+	_suppressCollisionSound = false;
 }
 
 // DOS ExitFL(): step back one cell and drop the forklift.


Commit: 18fc8d827c9c9ddfa32030968e39f8d7d1477bf8
    https://github.com/scummvm/scummvm/commit/18fc8d827c9c9ddfa32030968e39f8d7d1477bf8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:52+02:00

Commit Message:
COLONY: button fixes

Changed paths:
    engines/colony/animation.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 23cd0377b39..bc1833d1dc8 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -1256,9 +1256,9 @@ void ColonyEngine::handleKeypadClick(int item) {
 		}
 	}
 	if (item <= 12) {
-		// setObjectState(item, 1); // Reset to ensure animation runs Off -> On - handled by dolSprite
-		if (item > 10) // Clear/Enter should return to Off
-			setObjectState(item, 1);
+		// Original GANIMATE.C: SetObjectState(item,1) + AnimNextFram()
+		// resets button to unpressed after processing the digit.
+		setObjectState(item, 1);
 		drawAnimation();
 		_gfx->copyToScreen();
 	} else if (item == 25 && _animationName == "security") {


Commit: 25850145ac1d04bb36c440081e5b420b23ee709c
    https://github.com/scummvm/scummvm/commit/25850145ac1d04bb36c440081e5b420b23ee709c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:52+02:00

Commit Message:
COLONY: battle fixes

Changed paths:
    engines/colony/battle.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index aff9e9b7392..efef86eb873 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -782,8 +782,15 @@ void ColonyEngine::battleDrawTanks() {
 			_battleEnter.dist = forward;
 			draw3DBattlePrism(kEntDef, _battleEnter.xloc, _battleEnter.yloc,
 			                  _battleEnter.ang, -kFloor);
+			// Draw door with depth test disabled so it renders fully
+			// on top of the coplanar entrance wall (same fill color,
+			// but the black outline becomes clearly visible).
+			_gfx->setDepthState(true, false);
+			_gfx->setDepthRange(0.0, 0.999);
 			draw3DBattlePrism(kEntDoorDef, _battleEnter.xloc, _battleEnter.yloc,
 			                  _battleEnter.ang, -kFloor);
+			_gfx->setDepthRange(0.0, 1.0);
+			_gfx->setDepthState(true, true);
 			if (_battleMaxP < 100) {
 				_battlePwh[_battleMaxP] = &_battleEnter;
 				_battleMaxP++;
@@ -833,8 +840,12 @@ void ColonyEngine::battleDrawTanks() {
 			                  _battleShip.ang, -kFloor);
 			draw3DBattlePrism(kFRightDef, _battleShip.xloc, _battleShip.yloc,
 			                  _battleShip.ang, -kFloor);
+			_gfx->setDepthState(true, false);
+			_gfx->setDepthRange(0.0, 0.999);
 			draw3DBattlePrism(kSDoorDef, _battleShip.xloc, _battleShip.yloc,
 			                  _battleShip.ang, -kFloor);
+			_gfx->setDepthRange(0.0, 1.0);
+			_gfx->setDepthState(true, true);
 			if (_battleMaxP < 100) {
 				_battlePwh[_battleMaxP] = &_battleShip;
 				_battleMaxP++;


Commit: cc66da725b9aed4cc895a4dcffe76720cf9d9a87
    https://github.com/scummvm/scummvm/commit/cc66da725b9aed4cc895a4dcffe76720cf9d9a87
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:52+02:00

Commit Message:
COLONY: add debugger "spawn" command for placing robots

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


diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
index 68bd1742828..56f05cf5c82 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/debugger.cpp
@@ -80,6 +80,7 @@ Debugger::Debugger(ColonyEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("battle", WRAP_METHOD(Debugger, cmdBattle));
 	registerCmd("colony", WRAP_METHOD(Debugger, cmdColony));
 	registerCmd("forklift", WRAP_METHOD(Debugger, cmdForklift));
+	registerCmd("spawn", WRAP_METHOD(Debugger, cmdSpawn));
 }
 
 bool Debugger::cmdTeleport(int argc, const char **argv) {
@@ -434,4 +435,92 @@ bool Debugger::cmdForklift(int argc, const char **argv) {
 	return true;
 }
 
+bool Debugger::cmdSpawn(int argc, const char **argv) {
+	if (_vm->_gameMode != kModeColony) {
+		debugPrintf("Must be in colony mode\n");
+		return true;
+	}
+
+	static const struct { const char *name; int type; } spawnTypes[] = {
+		{"eye",       kRobEye},
+		{"pyramid",   kRobPyramid},
+		{"cube",      kRobCube},
+		{"upyramid",  kRobUPyramid},
+		{"queen",     kRobQueen},
+		{"drone",     kRobDrone},
+		{"soldier",   kRobSoldier},
+		{"snoop",     kRobSnoop},
+		{"feye",      kRobFEye},
+		{"fpyramid",  kRobFPyramid},
+		{"fcube",     kRobFCube},
+		{"fupyramid", kRobFUPyramid},
+		{"seye",      kRobSEye},
+		{"spyramid",  kRobSPyramid},
+		{"scube",     kRobSCube},
+		{"supyramid", kRobSUPyramid},
+		{"meye",      kRobMEye},
+		{"mpyramid",  kRobMPyramid},
+		{"mcube",     kRobMCube},
+		{"mupyramid", kRobMUPyramid},
+	};
+
+	if (argc < 2) {
+		debugPrintf("Usage: spawn <type>\n");
+		debugPrintf("Spawns an enemy in the cell in front of the player.\n\n");
+		debugPrintf("Full robots:\n");
+		debugPrintf("  eye, pyramid, cube, upyramid\n");
+		debugPrintf("  queen, drone, soldier, snoop\n");
+		debugPrintf("Egg stages (floating/small/mini):\n");
+		debugPrintf("  feye, fpyramid, fcube, fupyramid\n");
+		debugPrintf("  seye, spyramid, scube, supyramid\n");
+		debugPrintf("  meye, mpyramid, mcube, mupyramid\n");
+		return true;
+	}
+
+	Common::String name(argv[1]);
+	name.toLowercase();
+
+	int type = -1;
+	for (uint i = 0; i < ARRAYSIZE(spawnTypes); i++) {
+		if (name == spawnTypes[i].name) {
+			type = spawnTypes[i].type;
+			break;
+		}
+	}
+
+	if (type < 0) {
+		debugPrintf("Unknown enemy type '%s'. Use 'spawn' with no arguments to see valid types.\n", argv[1]);
+		return true;
+	}
+
+	// Compute the cell in front of the player
+	const int dx = _vm->_cost[_vm->_me.ang] >> 2;
+	const int dy = _vm->_sint[_vm->_me.ang] >> 2;
+	int targetX = _vm->_me.xindex + (dx > 0 ? 1 : (dx < 0 ? -1 : 0));
+	int targetY = _vm->_me.yindex + (dy > 0 ? 1 : (dy < 0 ? -1 : 0));
+
+	if (targetX < 1 || targetX > 30 || targetY < 1 || targetY > 30) {
+		debugPrintf("Target cell (%d,%d) is out of bounds\n", targetX, targetY);
+		return true;
+	}
+
+	if (_vm->_robotArray[targetX][targetY] != 0) {
+		debugPrintf("Target cell (%d,%d) is already occupied\n", targetX, targetY);
+		return true;
+	}
+
+	int xloc = (targetX << 8) + 128;
+	int yloc = (targetY << 8) + 128;
+	uint8 ang = _vm->_me.ang + 128; // face the player
+
+	if (!_vm->createObject(type, xloc, yloc, ang)) {
+		debugPrintf("Failed to create object (no free slot?)\n");
+		return true;
+	}
+
+	debugPrintf("Spawned %s (type %d) at cell (%d,%d) facing player\n",
+	            robotTypeName(type), type, targetX, targetY);
+	return false;
+}
+
 } // End of namespace Colony
diff --git a/engines/colony/debugger.h b/engines/colony/debugger.h
index 08512a29f87..e13e649f917 100644
--- a/engines/colony/debugger.h
+++ b/engines/colony/debugger.h
@@ -45,6 +45,7 @@ private:
 	bool cmdBattle(int argc, const char **argv);
 	bool cmdColony(int argc, const char **argv);
 	bool cmdForklift(int argc, const char **argv);
+	bool cmdSpawn(int argc, const char **argv);
 };
 
 }


Commit: 4378aa1a38563828591fbf7c089f3d67d986ddd0
    https://github.com/scummvm/scummvm/commit/4378aa1a38563828591fbf7c089f3d67d986ddd0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:53+02:00

Commit Message:
COLONY: fix regression in animation state initialization

Changed paths:
    engines/colony/interaction.cpp


diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 359d5e3dca8..d0bea39049b 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -433,7 +433,7 @@ void ColonyEngine::destroyRobot(int num) {
 
 	if (obj.where.power[1] <= 0) {
 		if (obj.count != 0) {
-			// Robot fully destroyed: remove and drop egg
+			// Robot fully destroyed: remove from grid and recycle slot
 			obj.alive = 0;
 			int gx = obj.where.xindex;
 			int gy = obj.where.yindex;
@@ -444,16 +444,16 @@ void ColonyEngine::destroyRobot(int num) {
 				} else if (_robotArray[gx][gy] == num) {
 					_robotArray[gx][gy] = 0;
 				}
-				}
-				_sound->play(Sound::kExplode);
-				copyOverflowObjectToSlot(num);
-				debugC(1, kColonyDebugAnimation, "Robot %d destroyed!", num);
-			} else {
+			}
+			_sound->play(Sound::kExplode);
+			copyOverflowObjectToSlot(num);
+			debugC(1, kColonyDebugCombat, "Robot %d destroyed!", num);
+		} else {
 			// Robot regresses to egg form
 			obj.where.power[1] = 10 + ((_randomSource.getRandomNumber(15)) << _level);
 			obj.grow = -1;
 			obj.count = 0;
-			debugC(1, kColonyDebugAnimation, "Robot %d regressed to egg", num);
+			debugC(1, kColonyDebugCombat, "Robot %d regressed to egg", num);
 		}
 	}
 }


Commit: ab92b64bc6aceb6997812c550f0d9df033b73142
    https://github.com/scummvm/scummvm/commit/ab92b64bc6aceb6997812c550f0d9df033b73142
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:53+02:00

Commit Message:
COLONY: queen eyes

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


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 57052e89464..1685764da72 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -158,7 +158,7 @@ static const DOSColorEntry &lookupDOSColor(int colorIdx, int level) {
 	case kColorEyeball: return g_dosColors[kColorEye];
 	case kColorEyeIris:
 	case kColorMiniEyeIris:
-	case kColorQueenEye: return g_dosColors[kColorIris];
+	case kColorQueenEye: return g_dosColors[3]; // DOS draweyes(): hardcoded vGREEN (cGREEN=3)
 	case kColorDroneEye:
 	case kColorSoldierEye: return g_dosColors[kColorEyes];
 	case kColorSoldierBody: return g_dosColors[kColorDrone];
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 89d65104e19..a687428f0d0 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1710,8 +1710,11 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			const PrismPartDef &nearWing = leftFirst ? kQRWingDef : kQLWingDef;
 			const int wingColor = (_renderMode != Common::kRenderMacintosh && _level == 7) ? kColorQueenWingRed : kColorClear;
 
+			// DOS draweyes(): queen eyeball is RED fill + WHITE outline
+			// (hardcoded, not from color table). Iris is GREEN.
+			const int queenEyeballColor = 5; // vRED (EGA index 4 = kColorRed... use raw 5 = cRED index)
 			draw3DPrism(obj, farWing, false, wingColor, true, true);
-			draw3DSphere(farEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			draw3DSphere(farEye, 0, 0, 130, 0, 0, 155, queenEyeballColor, kColorBlack, true);
 			drawEyeOverlays3D(farEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
 			if (farEye.where.xmn < obj.where.xmn)
 				obj.where.xmn = farEye.where.xmn;
@@ -1726,7 +1729,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			draw3DPrism(obj, kQAbdomenDef, false, -1, true, false);
 
 			draw3DPrism(obj, nearWing, false, wingColor, true, true);
-			draw3DSphere(nearEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			draw3DSphere(nearEye, 0, 0, 130, 0, 0, 155, queenEyeballColor, kColorBlack, true);
 			drawEyeOverlays3D(nearEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
 			if (nearEye.where.xmn < obj.where.xmn)
 				obj.where.xmn = nearEye.where.xmn;
@@ -1758,14 +1761,15 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			rightEye.where.yloc = (int)(eyeBaseY - c2);
 			resetObjectBounds(_screenR, rightEye.where);
 
+			// DOS draweyes(): shared with Queen — RED eyeball, GREEN iris
 			draw3DPrism(obj, kDAbdomenDef, false, -1, true, false);
 			draw3DPrism(obj, kDLLPincerDef, false, -1, true, false);
 			draw3DPrism(obj, kDRRPincerDef, false, -1, true, false);
 			draw3DPrism(obj, kDLEyeDef, false, -1, true, false);
 			draw3DPrism(obj, kDREyeDef, false, -1, true, false);
-			draw3DSphere(leftEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			draw3DSphere(leftEye, 0, 0, 130, 0, 0, 155, 5, kColorBlack, true);
 			drawEyeOverlays3D(leftEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
-			draw3DSphere(rightEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			draw3DSphere(rightEye, 0, 0, 130, 0, 0, 155, 5, kColorBlack, true);
 			drawEyeOverlays3D(rightEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
 		}
 		break;
@@ -1808,11 +1812,12 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			draw3DPrism(obj, kDAbdomenDef, false, kColorSoldierBody, true, false);
 			draw3DPrism(obj, leftPincerDef, false, -1, true, false);
 			draw3DPrism(obj, rightPincerDef, false, -1, true, false);
+			// DOS draweyes(): shared with Queen — RED eyeball, GREEN iris
 			draw3DPrism(obj, kDLEyeDef, false, kColorSoldierEye, true, false);
 			draw3DPrism(obj, kDREyeDef, false, kColorSoldierEye, true, false);
-			draw3DSphere(leftEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			draw3DSphere(leftEye, 0, 0, 130, 0, 0, 155, 5, kColorBlack, true);
 			drawEyeOverlays3D(leftEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
-			draw3DSphere(rightEye, 0, 0, 130, 0, 0, 155, eyeballColor, kColorBlack, true);
+			draw3DSphere(rightEye, 0, 0, 130, 0, 0, 155, 5, kColorBlack, true);
 			drawEyeOverlays3D(rightEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
 		}
 		break;


Commit: 2dd5ba5709da33fb55c5fd6a98c69ad734f84a0d
    https://github.com/scummvm/scummvm/commit/2dd5ba5709da33fb55c5fd6a98c69ad734f84a0d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:53+02:00

Commit Message:
COLONY: automap

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


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index a395f9b0061..9368a896f3f 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -171,6 +171,8 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	memset(_robotArray, 0, sizeof(_robotArray));
 	memset(_foodArray, 0, sizeof(_foodArray));
 	memset(_dirXY, 0, sizeof(_dirXY));
+	memset(_visited, 0, sizeof(_visited));
+	_showAutomap = false;
 
 	// PATCH.C init
 	memset(_levelData, 0, sizeof(_levelData));
@@ -983,6 +985,8 @@ Common::Error ColonyEngine::run() {
 			} else if (event.type == Common::EVENT_KEYDOWN) {
 				if (event.kbd.keycode == Common::KEYCODE_LSHIFT || event.kbd.keycode == Common::KEYCODE_RSHIFT)
 					_sprint = true;
+				if (event.kbd.keycode == Common::KEYCODE_TAB)
+					_showAutomap = !_showAutomap;
 				// Speed keys 1-5 remain as raw keyboard events
 				switch (event.kbd.keycode) {
 				case Common::KEYCODE_1:
@@ -1096,8 +1100,12 @@ Common::Error ColonyEngine::run() {
 			drawDashboardStep1();
 			drawCrosshair();
 			checkCenter();
+			markVisited();
 		}
 
+		if (_showAutomap && _gameMode == kModeColony)
+			drawAutomap();
+
 		// Draw Mac menu bar overlay (render directly to our surface, skip WM's
 		// g_system->copyRectToScreen which conflicts with the OpenGL backend)
 		if (_macMenu && _menuSurface && !_fullscreen) {
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index f27bc724ca1..27d5bea2403 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -436,6 +436,8 @@ private:
 	uint8 _robotArray[32][32];
 	uint8 _foodArray[32][32];
 	uint8 _dirXY[32][32];
+	bool _visited[8][32][32];  // per-level fog-of-war: _visited[level-1][x][y]
+	bool _showAutomap;
 
 	Locate _me;
 	Common::Array<Thing> _objects;
@@ -652,10 +654,16 @@ private:
 	void drawDOSBarGraph(int x, int y, int height);
 	void updateDOSPowerBars();
 	static int qlog(int32 x);
-	void drawMiniMapMarker(int x, int y, int halfSize, uint32 color, bool isMac);
+	void drawMiniMapMarker(int x, int y, int halfSize, uint32 color, bool isMac, const Common::Rect *clip = nullptr);
 	bool hasRobotAt(int x, int y) const;
 	bool hasFoodAt(int x, int y) const;
 	void drawMiniMap(uint32 lineColor);
+	void drawAutomap();
+	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);
+	int automapWallFeature(int fx, int fy, int dir);
+	void automapDrawWallWithFeature(const Common::Rect &vp, int wx1, int wy1, int wx2, int wy2, int feat, int lExt, uint32 color);
 	void drawForkliftOverlay();
 	void drawCrosshair();
 	bool clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Common::Rect &clip) const;
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index cf9f03f0171..0fd253b8c53 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -240,7 +240,7 @@ bool ColonyEngine::createObject(int type, int xloc, int yloc, uint8 ang) {
 			}
 		}
 		if (slot < 0) {
-			warning("createObject: no free dynamic slot for type %d on level %d", type, _level);
+			debugC(2, kColonyDebugMap, "createObject: no free dynamic slot for type %d on level %d", type, _level);
 			return false;
 		}
 	} else {
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index b81b79011b2..a0bf3b677b2 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -659,17 +659,18 @@ void ColonyEngine::drawDashboardMac() {
 	}
 }
 
-void ColonyEngine::drawMiniMapMarker(int x, int y, int halfSize, uint32 color, bool isMac) {
-	if (x < _headsUpRect.left + 1 || x >= _headsUpRect.right - 1 ||
-	    y < _headsUpRect.top + 1 || y >= _headsUpRect.bottom - 1)
+void ColonyEngine::drawMiniMapMarker(int x, int y, int halfSize, uint32 color, bool isMac, const Common::Rect *clip) {
+	const Common::Rect &cr = clip ? *clip : _headsUpRect;
+	if (x < cr.left + 1 || x >= cr.right - 1 ||
+	    y < cr.top + 1 || y >= cr.bottom - 1)
 		return;
 	if (isMac) {
 		_gfx->drawEllipse(x, y, halfSize, halfSize, color);
 	} else {
-		const int l = MAX<int>(_headsUpRect.left + 1, x - halfSize);
-		const int t = MAX<int>(_headsUpRect.top + 1, y - halfSize);
-		const int r = MIN<int>(_headsUpRect.right - 1, x + halfSize + 1);
-		const int b = MIN<int>(_headsUpRect.bottom - 1, y + halfSize + 1);
+		const int l = MAX<int>(cr.left + 1, x - halfSize);
+		const int t = MAX<int>(cr.top + 1, y - halfSize);
+		const int r = MIN<int>(cr.right - 1, x + halfSize + 1);
+		const int b = MIN<int>(cr.bottom - 1, y + halfSize + 1);
 		if (l >= r || t >= b)
 			return;
 		_gfx->drawRect(Common::Rect(l, t, r, b), color);
@@ -800,6 +801,215 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 	}
 }
 
+void ColonyEngine::markVisited() {
+	if (_level < 1 || _level > 8)
+		return;
+	const int lv = _level - 1;
+	const int cx = _me.xindex;
+	const int cy = _me.yindex;
+	if (cx < 0 || cx >= 32 || cy < 0 || cy >= 32)
+		return;
+
+	// Always mark the player's own cell
+	_visited[lv][cx][cy] = true;
+
+	// Check cardinal neighbors: blocked by wall bit OR door/airlock feature
+	bool canN = false, canS = false, canE = false, canW = false;
+
+	// North: wall at south edge of cell (cx, cy+1)
+	if (cy + 1 < 32) {
+		canN = !(_wall[cx][cy + 1] & 0x01);
+		if (canN && cx < 31 && cy < 31 &&
+		    (_mapData[cx][cy][kDirNorth][0] == kWallFeatureDoor || _mapData[cx][cy][kDirNorth][0] == kWallFeatureAirlock))
+			canN = false;
+	}
+	// South: wall at south edge of cell (cx, cy)
+	if (cy - 1 >= 0) {
+		canS = !(_wall[cx][cy] & 0x01);
+		if (canS && cx < 31 && cy < 31 &&
+		    (_mapData[cx][cy][kDirSouth][0] == kWallFeatureDoor || _mapData[cx][cy][kDirSouth][0] == kWallFeatureAirlock))
+			canS = false;
+	}
+	// East: wall at west edge of cell (cx+1, cy)
+	if (cx + 1 < 32) {
+		canE = !(_wall[cx + 1][cy] & 0x02);
+		if (canE && cx < 31 && cy < 31 &&
+		    (_mapData[cx][cy][kDirEast][0] == kWallFeatureDoor || _mapData[cx][cy][kDirEast][0] == kWallFeatureAirlock))
+			canE = false;
+	}
+	// West: wall at west edge of cell (cx, cy)
+	if (cx - 1 >= 0) {
+		canW = !(_wall[cx][cy] & 0x02);
+		if (canW && cx < 31 && cy < 31 &&
+		    (_mapData[cx][cy][kDirWest][0] == kWallFeatureDoor || _mapData[cx][cy][kDirWest][0] == kWallFeatureAirlock))
+			canW = false;
+	}
+
+	if (canN) _visited[lv][cx][cy + 1] = true;
+	if (canS) _visited[lv][cx][cy - 1] = true;
+	if (canE) _visited[lv][cx + 1][cy] = true;
+	if (canW) _visited[lv][cx - 1][cy] = true;
+
+	// Diagonal neighbors: visible only if both adjacent cardinal directions are open
+	if (canN && canE && cx + 1 < 32 && cy + 1 < 32) _visited[lv][cx + 1][cy + 1] = true;
+	if (canN && canW && cx - 1 >= 0 && cy + 1 < 32) _visited[lv][cx - 1][cy + 1] = true;
+	if (canS && canE && cx + 1 < 32 && cy - 1 >= 0) _visited[lv][cx + 1][cy - 1] = true;
+	if (canS && canW && cx - 1 >= 0 && cy - 1 >= 0) _visited[lv][cx - 1][cy - 1] = true;
+}
+
+static bool isPassableFeature(int feat) {
+	return feat == kWallFeatureDoor || feat == kWallFeatureAirlock ||
+	       feat == kWallFeatureUpStairs || feat == kWallFeatureDnStairs ||
+	       feat == kWallFeatureTunnel || feat == kWallFeatureElevator;
+}
+
+void ColonyEngine::automapCellCorner(int dx, int dy, int xloc, int yloc, int lExt, int tsin, int tcos, int ccx, int ccy, int &sx, int &sy) {
+	const long ox = xloc + (long)dx * lExt;
+	const long oy = yloc + (long)dy * lExt;
+	sx = ccx + (int)((ox * tsin - oy * tcos) >> 8);
+	sy = ccy - (int)((oy * tsin + ox * tcos) >> 8);
+}
+
+void ColonyEngine::automapDrawWall(const Common::Rect &vp, int x1, int y1, int x2, int y2, uint32 color) {
+	if (clipLineToRect(x1, y1, x2, y2, vp)) {
+		_gfx->drawLine(x1, y1, x2, y2, color);
+		// Thicker wall: offset by 1 pixel perpendicular
+		int ox = y2 - y1, oy = x1 - x2;
+		int len = (int)sqrtf((float)(ox * ox + oy * oy));
+		if (len > 0) {
+			ox = ox / len;
+			oy = oy / len;
+			int ax1 = x1 + ox, ay1 = y1 + oy, ax2 = x2 + ox, ay2 = y2 + oy;
+			if (clipLineToRect(ax1, ay1, ax2, ay2, vp))
+				_gfx->drawLine(ax1, ay1, ax2, ay2, color);
+		}
+	}
+}
+
+int ColonyEngine::automapWallFeature(int fx, int fy, int dir) {
+	if (fx >= 0 && fx < 31 && fy >= 0 && fy < 31) {
+		int feat = _mapData[fx][fy][dir][0];
+		if (isPassableFeature(feat))
+			return feat;
+	}
+	int nx = fx, ny = fy, opp = -1;
+	switch (dir) {
+	case kDirNorth: ny = fy + 1; opp = kDirSouth; break;
+	case kDirSouth: ny = fy - 1; opp = kDirNorth; break;
+	case kDirEast:  nx = fx + 1; opp = kDirWest;  break;
+	case kDirWest:  nx = fx - 1; opp = kDirEast;  break;
+	default: return 0;
+	}
+	if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31) {
+		int feat = _mapData[nx][ny][opp][0];
+		if (isPassableFeature(feat))
+			return feat;
+	}
+	return 0;
+}
+
+void ColonyEngine::automapDrawWallWithFeature(const Common::Rect &vp, int wx1, int wy1, int wx2, int wy2, int feat, int lExt, uint32 color) {
+	automapDrawWall(vp, wx1, wy1, wx2, wy2, color);
+
+	if (isPassableFeature(feat)) {
+		const int ppx = (wy2 - wy1);
+		const int ppy = (wx1 - wx2);
+		const int tickLen = MAX(2, lExt / 8);
+		const int plen = (int)sqrtf((float)(ppx * ppx + ppy * ppy));
+		if (plen > 0) {
+			const int tx = (ppx * tickLen) / plen;
+			const int ty = (ppy * tickLen) / plen;
+			const int ax = wx1 + (wx2 - wx1) / 4;
+			const int ay = wy1 + (wy2 - wy1) / 4;
+			const int bx = wx1 + 3 * (wx2 - wx1) / 4;
+			const int by = wy1 + 3 * (wy2 - wy1) / 4;
+			automapDrawWall(vp, ax - tx, ay - ty, ax + tx, ay + ty, color);
+			automapDrawWall(vp, bx - tx, by - ty, bx + tx, by + ty, color);
+		}
+	}
+}
+
+void ColonyEngine::drawAutomap() {
+	if (_level < 1 || _level > 8)
+		return;
+
+	const int lv = _level - 1;
+	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+
+	const Common::Rect vp(0, _menuBarHeight, _width, _height);
+	const int vpW = vp.width();
+	const int vpH = vp.height();
+	if (vpW <= 0 || vpH <= 0)
+		return;
+
+	_gfx->fillRect(vp, isMac ? 0xFFA0D0FF : 15);
+	_gfx->drawRect(vp, 0);
+
+	const int lExt = MIN(vpW, vpH) / 12;
+	if (lExt < 8)
+		return;
+
+	const int xloc = (lExt * ((_me.xindex << 8) - _me.xloc)) >> 8;
+	const int yloc = (lExt * ((_me.yindex << 8) - _me.yloc)) >> 8;
+	const int ccx = (vp.left + vp.right) >> 1;
+	const int ccy = (vp.top + vp.bottom) >> 1;
+	const int tsin = _sint[_me.look];
+	const int tcos = _cost[_me.look];
+	const uint32 lineColor = 0;
+
+	const int radius = (int)(sqrtf((float)(vpW * vpW + vpH * vpH)) / (2.0f * lExt)) + 2;
+	const int px = _me.xindex;
+	const int py = _me.yindex;
+	const int markerR = isMac ? 5 : 3;
+	const int foodR = isMac ? 3 : 2;
+
+	for (int dy = -radius; dy <= radius; dy++) {
+		for (int dx = -radius; dx <= radius; dx++) {
+			const int cx = px + dx;
+			const int cy = py + dy;
+			if (cx < 0 || cx >= 32 || cy < 0 || cy >= 32)
+				continue;
+			if (!_visited[lv][cx][cy])
+				continue;
+
+			int x0, y0, x1, y1, x2, y2, x3, y3;
+			automapCellCorner(dx, dy, xloc, yloc, lExt, tsin, tcos, ccx, ccy, x0, y0);
+			automapCellCorner(dx + 1, dy, xloc, yloc, lExt, tsin, tcos, ccx, ccy, x1, y1);
+			automapCellCorner(dx + 1, dy + 1, xloc, yloc, lExt, tsin, tcos, ccx, ccy, x2, y2);
+			automapCellCorner(dx, dy + 1, xloc, yloc, lExt, tsin, tcos, ccx, ccy, x3, y3);
+
+			if (_wall[cx][cy] & 0x01)
+				automapDrawWallWithFeature(vp, x0, y0, x1, y1, automapWallFeature(cx, cy, kDirSouth), lExt, lineColor);
+			if (_wall[cx][cy] & 0x02)
+				automapDrawWallWithFeature(vp, x0, y0, x3, y3, automapWallFeature(cx, cy, kDirWest), lExt, lineColor);
+			if (cy + 1 < 32 && (_wall[cx][cy + 1] & 0x01))
+				automapDrawWallWithFeature(vp, x3, y3, x2, y2, automapWallFeature(cx, cy, kDirNorth), lExt, lineColor);
+			if (cx + 1 < 32 && (_wall[cx + 1][cy] & 0x02))
+				automapDrawWallWithFeature(vp, x1, y1, x2, y2, automapWallFeature(cx, cy, kDirEast), lExt, lineColor);
+
+			if (ABS(dx) <= 6 && ABS(dy) <= 6) {
+				int mx, my;
+				automapCellCorner(dx, dy, xloc, yloc, lExt, tsin, tcos, ccx, ccy, mx, my);
+				int mx2, my2;
+				automapCellCorner(dx + 1, dy + 1, xloc, yloc, lExt, tsin, tcos, ccx, ccy, mx2, my2);
+				mx = (mx + mx2) >> 1;
+				my = (my + my2) >> 1;
+
+				const uint8 rnum = _robotArray[cx][cy];
+				if (rnum > 0 && rnum != kMeNum && rnum <= _objects.size() && _objects[rnum - 1].alive)
+					drawMiniMapMarker(mx, my, markerR, lineColor, isMac, &vp);
+				if (_foodArray[cx][cy] > 0)
+					drawMiniMapMarker(mx, my, foodR, lineColor, isMac, &vp);
+			}
+		}
+	}
+
+	// Player eye icon at center
+	const int eyeRx = lExt >> 2;
+	const int eyeRy = lExt >> 3;
+	_gfx->drawEllipse(ccx, ccy, eyeRx, eyeRy, lineColor);
+	_gfx->fillEllipse(ccx, ccy, eyeRx >> 1, eyeRy, lineColor);
+}
 void ColonyEngine::drawForkliftOverlay() {
 	if (_fl <= 0 || _screenR.width() <= 0 || _screenR.height() <= 0)
 		return;


Commit: 5fb8d0315a6176fc4f399cae23ea82b927e85d4d
    https://github.com/scummvm/scummvm/commit/5fb8d0315a6176fc4f399cae23ea82b927e85d4d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:54+02:00

Commit Message:
COLONY: disable long DOS intro sound

Changed paths:
    engines/colony/intro.cpp


diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 203d4fba63c..3bbb94c2917 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -385,17 +385,27 @@ void ColonyEngine::playIntro() {
 		}
 
 		// Final crash: DOS IBM_INTR.C lines 119-131
-		// Original: while(!SoundDone()) { EraseRect; PaintRect; } at ~8fps
+		// Original DOS: while(!SoundDone()) { EraseRect; PaintRect; } — flashes
+		// for the full Explode2 sound (~7s). On Mac the digitized sample is shorter.
 		_sound->stop();
-		_sound->play(Sound::kExplode);
-		int frame = 0;
-		while (!shouldQuit() && _sound->isPlaying()) {
-			_gfx->clear(frame % 2 ? _gfx->black() : _gfx->white());
+		if (getPlatform() == Common::kPlatformMacintosh) {
+			// Mac: play short digitized crash sample with brief flash
+			_sound->play(Sound::kExplode);
+			uint32 crashStart = _system->getMillis();
+			int frame = 0;
+			while (!shouldQuit() && _sound->isPlaying() && _system->getMillis() - crashStart < 2000) {
+				_gfx->clear(frame % 2 ? _gfx->black() : _gfx->white());
+				_gfx->copyToScreen();
+				_system->delayMillis(125);
+				frame++;
+			}
+			_sound->stop();
+		} else {
+			// DOS: skip the harsh 7-second PC speaker explode
+			_gfx->clear(_gfx->black());
 			_gfx->copyToScreen();
-			_system->delayMillis(125);
-			frame++;
+			_system->delayMillis(1000);
 		}
-		_sound->stop();
 		_gfx->clear(_gfx->black());
 		_gfx->copyToScreen();
 


Commit: a334827550bbbf8cc8ac6db6b06857b403072bca
    https://github.com/scummvm/scummvm/commit/a334827550bbbf8cc8ac6db6b06857b403072bca
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:54+02:00

Commit Message:
COLONY: initialize power suit animation switches from current armor state

Changed paths:
    engines/colony/interaction.cpp


diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index d0bea39049b..1838e37ca47 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -98,8 +98,14 @@ void ColonyEngine::interactWithObject(int objNum) {
 		// GANIMATE.C DoPowerSuit(): if(!corepower[coreindex])return;
 		if (!_corePower[_coreIndex])
 			break;
-		if (loadAnimation("suit"))
+		if (loadAnimation("suit")) {
+			// DOS DoPowerSuit: initialize switch positions from current armor/weapons
+			setObjectState(1, _armor * 2 + 1);
+			setObjectState(3, _armor + 1);
+			setObjectState(2, _weapons * 2 + 1);
+			setObjectState(4, _weapons + 1);
 			playAnimation();
+		}
 		break;
 	case kObjTeleport:
 	{


Commit: c2337c5ff1dc8729cc2b28f324e5ef92f8e4291c
    https://github.com/scummvm/scummvm/commit/c2337c5ff1dc8729cc2b28f324e5ef92f8e4291c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:54+02:00

Commit Message:
COLONY: fixed setupMacPattern warning using inline

Changed paths:
    engines/colony/render_internal.h


diff --git a/engines/colony/render_internal.h b/engines/colony/render_internal.h
index d1518ff7dc9..16d18e8b528 100644
--- a/engines/colony/render_internal.h
+++ b/engines/colony/render_internal.h
@@ -95,7 +95,7 @@ static const byte *kMacStippleData[] = {
 };
 
 // Pack Mac 16-bit RGB into 32-bit ARGB with 0xFF000000 marker for direct RGB.
-static uint32 packMacColor(const uint16 rgb[3]) {
+inline uint32 packMacColor(const uint16 rgb[3]) {
 	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
 }
 


Commit: 377c72dfd4b783fcfbbaf534fdefe6c858a673fa
    https://github.com/scummvm/scummvm/commit/377c72dfd4b783fcfbbaf534fdefe6c858a673fa
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:55+02:00

Commit Message:
COLONY: added extended copyright years/author to all the files

Changed paths:
    engines/colony/animation.cpp
    engines/colony/battle.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/debugger.cpp
    engines/colony/debugger.h
    engines/colony/detection.cpp
    engines/colony/detection.h
    engines/colony/gfx.cpp
    engines/colony/interaction.cpp
    engines/colony/intro.cpp
    engines/colony/map.cpp
    engines/colony/metaengine.cpp
    engines/colony/movement.cpp
    engines/colony/render.cpp
    engines/colony/render_features.cpp
    engines/colony/render_internal.h
    engines/colony/render_objects.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp
    engines/colony/savegame.cpp
    engines/colony/sound.cpp
    engines/colony/sound.h
    engines/colony/think.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index bc1833d1dc8..6d8c0dccbfc 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"
diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index efef86eb873..f55d3de1b87 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 // battle.cpp: Outdoor planet surface battle system.
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 9368a896f3f..549fbd391ee 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 27d5bea2403..b89cd24008f 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #ifndef COLONY_COLONY_H
diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
index 56f05cf5c82..121ba823340 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/debugger.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/debugger.h"
diff --git a/engines/colony/debugger.h b/engines/colony/debugger.h
index e13e649f917..7a7b14bbd63 100644
--- a/engines/colony/debugger.h
+++ b/engines/colony/debugger.h
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #ifndef COLONY_DEBUGGER_H
diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
index 5fc64ca5218..50e86e0ae30 100644
--- a/engines/colony/detection.cpp
+++ b/engines/colony/detection.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/detection.h"
diff --git a/engines/colony/detection.h b/engines/colony/detection.h
index d397d6f43e9..144d843dadd 100644
--- a/engines/colony/detection.h
+++ b/engines/colony/detection.h
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #ifndef COLONY_DETECTION_H
diff --git a/engines/colony/gfx.cpp b/engines/colony/gfx.cpp
index 0f0e524b255..6bd2886d2e7 100644
--- a/engines/colony/gfx.cpp
+++ b/engines/colony/gfx.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "common/system.h"
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 1838e37ca47..6e07eb0346a 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 3bbb94c2917..a1dad089833 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 0fd253b8c53..2e67f78e8b7 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"
diff --git a/engines/colony/metaengine.cpp b/engines/colony/metaengine.cpp
index d6d183bccf8..f3b055eb290 100644
--- a/engines/colony/metaengine.cpp
+++ b/engines/colony/metaengine.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 1659dd980e6..9df0e00abe4 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 1685764da72..79a08d6d9c4 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"
diff --git a/engines/colony/render_features.cpp b/engines/colony/render_features.cpp
index c85f2fc7f66..f4aa52aa9d9 100644
--- a/engines/colony/render_features.cpp
+++ b/engines/colony/render_features.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"
diff --git a/engines/colony/render_internal.h b/engines/colony/render_internal.h
index 16d18e8b528..b19de4098a4 100644
--- a/engines/colony/render_internal.h
+++ b/engines/colony/render_internal.h
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #ifndef COLONY_RENDER_INTERNAL_H
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index a687428f0d0..a2f4aa2b489 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index a370b008356..fc9fb696c7a 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #ifndef COLONY_RENDERER_H
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index e1d58af1431..a07462bbcaf 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "common/system.h"
diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
index c0961e9c197..a11f3dee9bc 100644
--- a/engines/colony/savegame.cpp
+++ b/engines/colony/savegame.cpp
@@ -13,6 +13,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index 840d7d1aa0f..e9449cd0a94 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/sound.h"
diff --git a/engines/colony/sound.h b/engines/colony/sound.h
index c16bea3b449..30980c18961 100644
--- a/engines/colony/sound.h
+++ b/engines/colony/sound.h
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #ifndef COLONY_SOUND_H
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index 4ac5954f362..871f02c0a2d 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -1,3 +1,30 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
+ */
+
 #include "colony/colony.h"
 #include "common/debug.h"
 
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index a0bf3b677b2..13118ce62c4 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -17,6 +17,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
+ * Based on the original sources
+ *   https://github.com/Croquetx/thecolony
+ * Copyright (C) 1988, David A. Smith
+ *
+ * Distributed under Apache Version 2.0 License
+ *
  */
 
 #include "colony/colony.h"


Commit: d015904e93f58e93e21d90cccfbb9bd4e201aed3
    https://github.com/scummvm/scummvm/commit/d015904e93f58e93e21d90cccfbb9bd4e201aed3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:55+02:00

Commit Message:
COLONY: use SeekableReadStreamEndian instead of read wrappers

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


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 6d8c0dccbfc..37c4528fb46 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -304,65 +304,67 @@ bool ColonyEngine::loadAnimation(const Common::String &name) {
 		return false;
 	}
 
+	bool isBE = (getPlatform() == Common::kPlatformMacintosh);
+	Common::SeekableReadStreamEndianWrapper stream(file, isBE, DisposeAfterUse::YES);
+
 	deleteAnimation();
 
 	// Read background data
-	file->read(_topBG, 8);
-	file->read(_bottomBG, 8);
-	_divideBG = readSint16(*file);
-	_backgroundActive = readSint16(*file) != 0;
+	stream.read(_topBG, 8);
+	stream.read(_bottomBG, 8);
+	_divideBG = stream.readSint16();
+	_backgroundActive = stream.readSint16() != 0;
 	if (_backgroundActive) {
-		_backgroundClip = readRect(*file);
-		_backgroundLocate = readRect(*file);
-		_backgroundMask = loadImage(*file);
-		_backgroundFG = loadImage(*file);
+		_backgroundClip = readRect(stream);
+		_backgroundLocate = readRect(stream);
+		_backgroundMask = loadImage(stream);
+		_backgroundFG = loadImage(stream);
 	}
 
 	// Read sprite data
-	int16 maxsprite = readSint16(*file);
-	readSint16(*file); // locSprite
+	int16 maxsprite = stream.readSint16();
+	stream.readSint16(); // locSprite
 	for (int i = 0; i < maxsprite; i++) {
 		Sprite *s = new Sprite();
-		s->fg = loadImage(*file);
-		s->mask = loadImage(*file);
-		s->used = readSint16(*file) != 0;
-		s->clip = readRect(*file);
-		s->locate = readRect(*file);
+		s->fg = loadImage(stream);
+		s->mask = loadImage(stream);
+		s->used = stream.readSint16() != 0;
+		s->clip = readRect(stream);
+		s->locate = readRect(stream);
 		_cSprites.push_back(s);
 	}
 
 	// Read complex sprite data
-	int16 maxLSprite = readSint16(*file);
-	readSint16(*file); // anum
+	int16 maxLSprite = stream.readSint16();
+	stream.readSint16(); // anum
 	for (int i = 0; i < maxLSprite; i++) {
 		ComplexSprite *ls = new ComplexSprite();
-		int16 size = readSint16(*file);
+		int16 size = stream.readSint16();
 		for (int j = 0; j < size; j++) {
 			ComplexSprite::SubObject sub;
-			sub.spritenum = readSint16(*file);
-			sub.xloc = readSint16(*file);
-			sub.yloc = readSint16(*file);
+			sub.spritenum = stream.readSint16();
+			sub.xloc = stream.readSint16();
+			sub.yloc = stream.readSint16();
 			ls->objects.push_back(sub);
 		}
-		ls->bounds = readRect(*file);
-		ls->visible = readSint16(*file) != 0;
-		ls->current = readSint16(*file);
-		ls->xloc = readSint16(*file);
-		ls->yloc = readSint16(*file);
-		ls->acurrent = readSint16(*file);
-		ls->axloc = readSint16(*file);
-		ls->ayloc = readSint16(*file);
-		ls->type = file->readByte();
-		ls->frozen = file->readByte();
-		ls->locked = file->readByte();
-		ls->link = readSint16(*file);
-		ls->key = readSint16(*file);
-		ls->lock = readSint16(*file);
+		ls->bounds = readRect(stream);
+		ls->visible = stream.readSint16() != 0;
+		ls->current = stream.readSint16();
+		ls->xloc = stream.readSint16();
+		ls->yloc = stream.readSint16();
+		ls->acurrent = stream.readSint16();
+		ls->axloc = stream.readSint16();
+		ls->ayloc = stream.readSint16();
+		ls->type = stream.readByte();
+		ls->frozen = stream.readByte();
+		ls->locked = stream.readByte();
+		ls->link = stream.readSint16();
+		ls->key = stream.readSint16();
+		ls->lock = stream.readSint16();
 		ls->onoff = true;
 		_lSprites.push_back(ls);
 	}
 
-	delete file;
 	return true;
 }
 
@@ -824,11 +826,11 @@ void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y, uin
 	}
 }
 
-Image *ColonyEngine::loadImage(Common::SeekableReadStream &file) {
+Image *ColonyEngine::loadImage(Common::SeekableReadStreamEndian &file) {
 	Image *im = new Image();
 	if (getPlatform() == Common::kPlatformMacintosh) {
-		readUint32(file); // baseAddr placeholder
-		im->rowBytes = readSint16(file);
+		file.readUint32(); // baseAddr placeholder
+		im->rowBytes = file.readSint16();
 		Common::Rect r = readRect(file);
 		im->width = r.width();
 		im->height = r.height();
@@ -836,22 +838,22 @@ Image *ColonyEngine::loadImage(Common::SeekableReadStream &file) {
 		im->bits = 1;
 		im->planes = 1;
 	} else {
-		im->width = readSint16(file);
-		im->height = readSint16(file);
-		im->align = readSint16(file);
-		im->rowBytes = readSint16(file);
+		im->width = file.readSint16();
+		im->height = file.readSint16();
+		im->align = file.readSint16();
+		im->rowBytes = file.readSint16();
 		im->bits = file.readByte();
 		im->planes = file.readByte();
 	}
 
-	int16 tf = readSint16(file);
+	int16 tf = file.readSint16();
 	uint32 size;
 	if (tf) {
 		// Mac original loadbitmap: reads bsize bytes into a buffer, then
 		// decompresses from that buffer. We must read exactly bsize bytes
 		// from the stream to keep file position aligned.
-		uint32 bsize = readUint32(file);
-		size = readUint32(file);
+		uint32 bsize = file.readUint32();
+		size = file.readUint32();
 		im->data = new byte[size];
 		byte *packed = new byte[bsize + 8](); // +8 matches original NewPtr(bsize+8)
 		file.read(packed, bsize);
@@ -871,14 +873,14 @@ Image *ColonyEngine::loadImage(Common::SeekableReadStream &file) {
 		}
 		delete[] packed;
 	} else {
-		size = readUint32(file);
+		size = file.readUint32();
 		im->data = new byte[size];
 		file.read(im->data, size);
 	}
 	return im;
 }
 
-void ColonyEngine::unpackBytes(Common::SeekableReadStream &file, byte *dst, uint32 len) {
+void ColonyEngine::unpackBytes(Common::SeekableReadStreamEndian &file, byte *dst, uint32 len) {
 	uint32 i = 0;
 	while (i < len) {
 		byte count = file.readByte();
@@ -889,18 +891,18 @@ void ColonyEngine::unpackBytes(Common::SeekableReadStream &file, byte *dst, uint
 	}
 }
 
-Common::Rect ColonyEngine::readRect(Common::SeekableReadStream &file) {
+Common::Rect ColonyEngine::readRect(Common::SeekableReadStreamEndian &file) {
 	int16 top, left, bottom, right;
 	if (getPlatform() == Common::kPlatformMacintosh) {
-		top = readSint16(file);
-		left = readSint16(file);
-		bottom = readSint16(file);
-		right = readSint16(file);
+		top = file.readSint16();
+		left = file.readSint16();
+		bottom = file.readSint16();
+		right = file.readSint16();
 	} else {
-		left = readSint16(file);
-		top = readSint16(file);
-		right = readSint16(file);
-		bottom = readSint16(file);
+		left = file.readSint16();
+		top = file.readSint16();
+		right = file.readSint16();
+		bottom = file.readSint16();
 	}
 	// Guard against invalid rects from animation data
 	if (left > right || top > bottom)
@@ -908,23 +910,6 @@ Common::Rect ColonyEngine::readRect(Common::SeekableReadStream &file) {
 	return Common::Rect(left, top, right, bottom);
 }
 
-int16 ColonyEngine::readSint16(Common::SeekableReadStream &s) {
-	if (getPlatform() == Common::kPlatformMacintosh)
-		return s.readSint16BE();
-	return s.readSint16LE();
-}
-
-uint16 ColonyEngine::readUint16(Common::SeekableReadStream &s) {
-	if (getPlatform() == Common::kPlatformMacintosh)
-		return s.readUint16BE();
-	return s.readUint16LE();
-}
-
-uint32 ColonyEngine::readUint32(Common::SeekableReadStream &s) {
-	if (getPlatform() == Common::kPlatformMacintosh)
-		return s.readUint32BE();
-	return s.readUint32LE();
-}
 
 int ColonyEngine::whichSprite(const Common::Point &p) {
 	int ox = _screenR.left + (_screenR.width() - 416) / 2;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index b89cd24008f..6f55792b842 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -33,6 +33,7 @@
 #include "common/array.h"
 #include "common/random.h"
 #include "common/rect.h"
+#include "common/stream.h"
 #include "graphics/framelimiter.h"
 #include "common/rendermode.h"
 #include "colony/renderer.h"
@@ -726,12 +727,9 @@ private:
 	void drawComplexSprite(int index, int ox, int oy);
 	void drawAnimationImage(Image *img, Image *mask, int x, int y, uint32 fillColor = 0xFFFFFFFF);
 	uint32 resolveAnimColor(int16 bmEntry) const;
-	Image *loadImage(Common::SeekableReadStream &file);
-	void unpackBytes(Common::SeekableReadStream &file, byte *dst, uint32 len);
-	Common::Rect readRect(Common::SeekableReadStream &file);
-	int16 readSint16(Common::SeekableReadStream &s);
-	uint16 readUint16(Common::SeekableReadStream &s);
-	uint32 readUint32(Common::SeekableReadStream &s);
+	Image *loadImage(Common::SeekableReadStreamEndian &file);
+	void unpackBytes(Common::SeekableReadStreamEndian &file, byte *dst, uint32 len);
+	Common::Rect readRect(Common::SeekableReadStreamEndian &file);
 	int whichSprite(const Common::Point &p);
 	void handleAnimationClick(int item);
 	void handleDeskClick(int item);


Commit: 94119aee30b24060944e9268bc20d803aa30a078
    https://github.com/scummvm/scummvm/commit/94119aee30b24060944e9268bc20d803aa30a078
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:55+02:00

Commit Message:
COLONY: avoid mixing spaces and tabs

Changed paths:
    engines/colony/animation.cpp
    engines/colony/battle.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/debugger.cpp
    engines/colony/detection.cpp
    engines/colony/interaction.cpp
    engines/colony/intro.cpp
    engines/colony/map.cpp
    engines/colony/movement.cpp
    engines/colony/render.cpp
    engines/colony/render_features.cpp
    engines/colony/render_objects.cpp
    engines/colony/renderer_opengl.cpp
    engines/colony/savegame.cpp
    engines/colony/think.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 37c4528fb46..a6e3f241fb4 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -224,7 +224,7 @@ static uint32 macSysColorToARGB(int sysColor) {
 
 static uint32 packMacColorBG(const uint16 rgb[3]) {
 	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
-	       ((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
+		((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
 }
 
 static int getAnimationStateCount(const Common::Array<ComplexSprite *> &sprites, int num) {
@@ -679,7 +679,7 @@ void ColonyEngine::drawAnimation() {
 	int oy = _screenR.top + (_screenR.height() - 264) / 2;
 
 	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh
-	                       && !_animBMColors.empty());
+		&& !_animBMColors.empty());
 
 	// Fill background patterns (416x264 area).
 	// Color mode: QuickDraw pattern bit 1 -> ForeColor (black), bit 0 -> BackColor.
@@ -736,8 +736,8 @@ void ColonyEngine::drawAnimation() {
 				bgFill = resolveAnimColor(_animBMColors[0]); // unpowered: inherits top
 		}
 		drawAnimationImage(_backgroundFG, _backgroundMask,
-		                   ox + _backgroundLocate.left, oy + _backgroundLocate.top,
-		                   bgFill);
+			ox + _backgroundLocate.left, oy + _backgroundLocate.top,
+			bgFill);
 	}
 
 	// Draw complex sprites
@@ -767,7 +767,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
-	                       && !_animBMColors.empty());
+		&& !_animBMColors.empty());
 	if (useColor) {
 		int bmIdx = index + 2;
 		if (bmIdx < (int)_animBMColors.size())
@@ -1721,7 +1721,7 @@ void ColonyEngine::moveObject(int index) {
 			const Sprite *s = _cSprites[spriteIdx];
 			Common::Rect r = s->clip;
 			r.translate(target->xloc + target->objects[cnum].xloc,
-			            target->yloc + target->objects[cnum].yloc);
+				target->yloc + target->objects[cnum].yloc);
 
 			if (pt.x < r.left || pt.x > r.right || pt.y < r.top || pt.y > r.bottom)
 				continue;
diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index f55d3de1b87..d63867520c8 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -104,9 +104,9 @@ static int wrapBattleCoord(int coord) {
 }
 
 static bool battleProjectPoint(const Common::Rect &screenR, uint8 look, int8 lookY, const int *sint,
-                               const int *cost, int camX, int camY,
-                               float worldX, float worldY, float worldZ,
-                               int &screenX, int &screenY) {
+	const int *cost, int camX, int camY,
+	float worldX, float worldY, float worldZ,
+	int &screenX, int &screenY) {
 	const float dx = worldX - camX;
 	const float dy = worldY - camY;
 	const float dz = worldZ;
@@ -143,9 +143,9 @@ static void battleResetBounds(const Common::Rect &screenR, Locate &loc) {
 }
 
 static bool battleAccumulateBounds(const Common::Rect &screenR, const ColonyEngine::PrismPartDef &def,
-                                   Locate &loc, int worldX, int worldY, uint8 ang, int zShift,
-                                   uint8 look, int8 lookY, const int *sint, const int *cost,
-                                   int camX, int camY) {
+	Locate &loc, int worldX, int worldY, uint8 ang, int zShift,
+	uint8 look, int8 lookY, const int *sint, const int *cost,
+	int camX, int camY) {
 	const uint8 rotAng = ang + 32;
 	const long rotCos = cost[rotAng];
 	const long rotSin = sint[rotAng];
@@ -162,7 +162,7 @@ static bool battleAccumulateBounds(const Common::Rect &screenR, const ColonyEngi
 		int sx = 0;
 		int sy = 0;
 		if (!battleProjectPoint(screenR, look, lookY, sint, cost, camX, camY,
-		                        (float)(rx + worldX), (float)(ry + worldY), (float)(oz + zShift), sx, sy))
+			(float)(rx + worldX), (float)(ry + worldY), (float)(oz + zShift), sx, sy))
 			continue;
 
 		loc.xmn = MIN(loc.xmn, sx);
@@ -497,13 +497,13 @@ void ColonyEngine::battleSet() {
 						fy = 0x2000 * j + kBattleSize + (0x1FFF & _randomSource.getRandomNumber(0x7FFF));
 				} while (
 					(fx > _battleEnter.xloc - 8 * kBattleSize &&
-					 fx < _battleEnter.xloc + 2 * kBattleSize &&
-					 fy > _battleEnter.yloc - 2 * kBattleSize &&
-					 fy < _battleEnter.yloc + 2 * kBattleSize) ||
+						fx < _battleEnter.xloc + 2 * kBattleSize &&
+						fy > _battleEnter.yloc - 2 * kBattleSize &&
+						fy < _battleEnter.yloc + 2 * kBattleSize) ||
 					(fx > _battleShip.xloc - 2 * kBattleSize &&
-					 fx < _battleShip.xloc + 2 * kBattleSize &&
-					 fy > _battleShip.yloc - 2 * kBattleSize &&
-					 fy < _battleShip.yloc + 2 * kBattleSize));
+						fx < _battleShip.xloc + 2 * kBattleSize &&
+						fy > _battleShip.yloc - 2 * kBattleSize &&
+						fy < _battleShip.yloc + 2 * kBattleSize));
 
 				_pyramids[i][j][p].xloc = fx;
 				_pyramids[i][j][p].yloc = fy;
@@ -541,7 +541,7 @@ void ColonyEngine::battleBackdrop() {
 	// Ground fill (below horizon)
 	uint32 groundColor = 0xFF404040;
 	Common::Rect ground(_screenR.left, CLIP<int>(horizonY, _screenR.top, _screenR.bottom),
-	                    _screenR.right, _screenR.bottom);
+		_screenR.right, _screenR.bottom);
 	if (ground.bottom > ground.top)
 		_gfx->fillRect(ground, groundColor);
 
@@ -621,7 +621,7 @@ void ColonyEngine::battleDrawPyramids() {
 			const int wy = battleNormalizeCoord(_me.yloc + relY);
 
 			battleAccumulateBounds(_screenR, kRockDef, pyr, wx, wy, pyr.ang, -kFloor,
-			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 			pyr.dist = forward;
 			draw3DBattlePrism(kRockDef, wx, wy, pyr.ang, -kFloor);
 
@@ -689,7 +689,7 @@ void ColonyEngine::battleDrawTanks() {
 
 		// Abdomen
 		battleAccumulateBounds(_screenR, kBDroneAbdDef, drone, drone.xloc, drone.yloc, droneAng, 0,
-		                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 		draw3DBattlePrism(kBDroneAbdDef, drone.xloc, drone.yloc, droneAng, 0);
 
 		// Animated pincers: rotate base points by lookx offset
@@ -713,7 +713,7 @@ void ColonyEngine::battleDrawTanks() {
 		}
 		PrismPartDef lPincerDef = {4, lPincerPts, 4, kBLPincerSurf};
 		battleAccumulateBounds(_screenR, lPincerDef, drone, drone.xloc, drone.yloc, droneAng, 0,
-		                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 		draw3DBattlePrism(lPincerDef, drone.xloc, drone.yloc, droneAng, 0);
 
 		// Build animated right pincer vertices
@@ -731,14 +731,14 @@ void ColonyEngine::battleDrawTanks() {
 		}
 		PrismPartDef rPincerDef = {4, rPincerPts, 4, kBRPincerSurf};
 		battleAccumulateBounds(_screenR, rPincerDef, drone, drone.xloc, drone.yloc, droneAng, 0,
-		                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 		draw3DBattlePrism(rPincerDef, drone.xloc, drone.yloc, droneAng, 0);
 
 		// Eyes
 		battleAccumulateBounds(_screenR, kBLEyeDef, drone, drone.xloc, drone.yloc, droneAng, 0,
-		                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 		battleAccumulateBounds(_screenR, kBREyeDef, drone, drone.xloc, drone.yloc, droneAng, 0,
-		                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+			_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 		draw3DBattlePrism(kBLEyeDef, drone.xloc, drone.yloc, droneAng, 0);
 		draw3DBattlePrism(kBREyeDef, drone.xloc, drone.yloc, droneAng, 0);
 		drone.dist = forward;
@@ -760,11 +760,11 @@ void ColonyEngine::battleDrawTanks() {
 			const int forward = (int)((relX * _cost[_me.look] + relY * _sint[_me.look]) >> 7);
 			battleResetBounds(_screenR, _battleProj);
 			battleAccumulateBounds(_screenR, kProjDef, _battleProj,
-			                       _battleProj.xloc, _battleProj.yloc, _battleProj.ang, -kFloor,
-			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleProj.xloc, _battleProj.yloc, _battleProj.ang, -kFloor,
+				_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 			_battleProj.dist = forward;
 			draw3DBattlePrism(kProjDef, _battleProj.xloc, _battleProj.yloc,
-			                  _battleProj.ang, -kFloor);
+				_battleProj.ang, -kFloor);
 			if (_battleMaxP < 100) {
 				_battlePwh[_battleMaxP] = &_battleProj;
 				_battleMaxP++;
@@ -780,21 +780,21 @@ void ColonyEngine::battleDrawTanks() {
 			const int forward = (int)((relX * _cost[_me.look] + relY * _sint[_me.look]) >> 7);
 			battleResetBounds(_screenR, _battleEnter);
 			battleAccumulateBounds(_screenR, kEntDef, _battleEnter,
-			                       _battleEnter.xloc, _battleEnter.yloc, _battleEnter.ang, -kFloor,
-			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleEnter.xloc, _battleEnter.yloc, _battleEnter.ang, -kFloor,
+				_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 			battleAccumulateBounds(_screenR, kEntDoorDef, _battleEnter,
-			                       _battleEnter.xloc, _battleEnter.yloc, _battleEnter.ang, -kFloor,
-			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleEnter.xloc, _battleEnter.yloc, _battleEnter.ang, -kFloor,
+				_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 			_battleEnter.dist = forward;
 			draw3DBattlePrism(kEntDef, _battleEnter.xloc, _battleEnter.yloc,
-			                  _battleEnter.ang, -kFloor);
+				_battleEnter.ang, -kFloor);
 			// Draw door with depth test disabled so it renders fully
 			// on top of the coplanar entrance wall (same fill color,
 			// but the black outline becomes clearly visible).
 			_gfx->setDepthState(true, false);
 			_gfx->setDepthRange(0.0, 0.999);
 			draw3DBattlePrism(kEntDoorDef, _battleEnter.xloc, _battleEnter.yloc,
-			                  _battleEnter.ang, -kFloor);
+				_battleEnter.ang, -kFloor);
 			_gfx->setDepthRange(0.0, 1.0);
 			_gfx->setDepthState(true, true);
 			if (_battleMaxP < 100) {
@@ -812,44 +812,44 @@ void ColonyEngine::battleDrawTanks() {
 			const int forward = (int)((relX * _cost[_me.look] + relY * _sint[_me.look]) >> 7);
 			battleResetBounds(_screenR, _battleShip);
 			battleAccumulateBounds(_screenR, kSBodyDef, _battleShip,
-			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 			battleAccumulateBounds(_screenR, kSFrontDef, _battleShip,
-			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 			battleAccumulateBounds(_screenR, kSBackDef, _battleShip,
-			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 			battleAccumulateBounds(_screenR, kFTopDef, _battleShip,
-			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 			battleAccumulateBounds(_screenR, kFLeftDef, _battleShip,
-			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 			battleAccumulateBounds(_screenR, kFRightDef, _battleShip,
-			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 			battleAccumulateBounds(_screenR, kSDoorDef, _battleShip,
-			                       _battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
-			                       _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
+				_battleShip.xloc, _battleShip.yloc, _battleShip.ang, -kFloor,
+				_me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc);
 			_battleShip.dist = forward;
 			draw3DBattlePrism(kSBodyDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
+				_battleShip.ang, -kFloor);
 			draw3DBattlePrism(kSFrontDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
+				_battleShip.ang, -kFloor);
 			draw3DBattlePrism(kSBackDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
+				_battleShip.ang, -kFloor);
 			// Fins: force-draw (no backface cull) — single-sided surfaces
 			draw3DBattlePrism(kFTopDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
+				_battleShip.ang, -kFloor);
 			draw3DBattlePrism(kFLeftDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
+				_battleShip.ang, -kFloor);
 			draw3DBattlePrism(kFRightDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
+				_battleShip.ang, -kFloor);
 			_gfx->setDepthState(true, false);
 			_gfx->setDepthRange(0.0, 0.999);
 			draw3DBattlePrism(kSDoorDef, _battleShip.xloc, _battleShip.yloc,
-			                  _battleShip.ang, -kFloor);
+				_battleShip.ang, -kFloor);
 			_gfx->setDepthRange(0.0, 1.0);
 			_gfx->setDepthState(true, true);
 			if (_battleMaxP < 100) {
@@ -877,12 +877,12 @@ void ColonyEngine::battleThink() {
 
 	for (int i = 0; i < 16; i++) {
 		if (i != _battleRound &&
-		    xcheck > _bfight[i].xloc - kBattleSize &&
-		    xcheck < _bfight[i].xloc + kBattleSize &&
-		    ycheck > _bfight[i].yloc - kBattleSize &&
-		    ycheck < _bfight[i].yloc + kBattleSize) {
+			xcheck > _bfight[i].xloc - kBattleSize &&
+			xcheck < _bfight[i].xloc + kBattleSize &&
+			ycheck > _bfight[i].yloc - kBattleSize &&
+			ycheck < _bfight[i].yloc + kBattleSize) {
 			while (_bfight[i].xloc - _me.xloc < 2000 &&
-			       _bfight[i].yloc - _me.yloc < 2000) {
+				_bfight[i].yloc - _me.yloc < 2000) {
 				_bfight[i].xloc = _randomSource.getRandomNumber(0x7FFF);
 				_bfight[i].yloc = _randomSource.getRandomNumber(0x7FFF);
 			}
@@ -936,15 +936,15 @@ void ColonyEngine::battleThink() {
 		const int fy = _bfight[i].yloc + (_sint[ang] >> 2);
 		if (distance > 250 || tooFar) {
 			if ((!_orbit) &&
-			    fx > _battleShip.xloc - 2 * kBattleSize &&
-			    fx < _battleShip.xloc + 2 * kBattleSize &&
-			    fy > _battleShip.yloc - 4 * kBattleSize &&
-			    fy < _battleShip.yloc + 4 * kBattleSize) {
+				fx > _battleShip.xloc - 2 * kBattleSize &&
+				fx < _battleShip.xloc + 2 * kBattleSize &&
+				fy > _battleShip.yloc - 4 * kBattleSize &&
+				fy < _battleShip.yloc + 4 * kBattleSize) {
 				ang += 8;
 			} else if (fx > _battleEnter.xloc - 2 * kBattleSize &&
-			           fx < _battleEnter.xloc + 2 * kBattleSize &&
-			           fy > _battleEnter.yloc - 2 * kBattleSize &&
-			           fy < _battleEnter.yloc + 2 * kBattleSize) {
+				fx < _battleEnter.xloc + 2 * kBattleSize &&
+				fy > _battleEnter.yloc - 2 * kBattleSize &&
+				fy < _battleEnter.yloc + 2 * kBattleSize) {
 				ang += 8;
 			} else {
 				_bfight[i].xloc = battleNormalizeCoord(fx);
@@ -996,40 +996,40 @@ void ColonyEngine::battleCommand(int xnew, int ynew) {
 
 	for (int i = 0; i < 16; i++) {
 		if (xnew > _bfight[i].xloc - kBattleSize &&
-		    xnew < _bfight[i].xloc + kBattleSize &&
-		    ynew > _bfight[i].yloc - kBattleSize &&
-		    ynew < _bfight[i].yloc + kBattleSize) {
+			xnew < _bfight[i].xloc + kBattleSize &&
+			ynew > _bfight[i].yloc - kBattleSize &&
+			ynew < _bfight[i].yloc + kBattleSize) {
 			bonk();
 			return;
 		}
 	}
 
 	if (!_orbit &&
-	    xnew > _battleShip.xloc - 2 * kBattleSize &&
-	    xnew < _battleShip.xloc &&
-	    ynew > _battleShip.yloc - kBattleSize / 2 &&
-	    ynew < _battleShip.yloc + kBattleSize / 2) {
+		xnew > _battleShip.xloc - 2 * kBattleSize &&
+		xnew < _battleShip.xloc &&
+		ynew > _battleShip.yloc - kBattleSize / 2 &&
+		ynew < _battleShip.yloc + kBattleSize / 2) {
 		enterColonyFromBattle(1, 900, 3000);
 		return;
 	}
 
 	if (xnew > _battleEnter.xloc - 2 * kBattleSize &&
-	    xnew < _battleEnter.xloc &&
-	    ynew > _battleEnter.yloc - kBattleSize / 2 &&
-	    ynew < _battleEnter.yloc + kBattleSize / 2) {
+		xnew < _battleEnter.xloc &&
+		ynew > _battleEnter.yloc - kBattleSize / 2 &&
+		ynew < _battleEnter.yloc + kBattleSize / 2) {
 		enterColonyFromBattle(2, 384, 640);
 		return;
 	}
 
 	if ((!_orbit &&
-	     xnew > _battleShip.xloc - 2 * kBattleSize &&
-	     xnew < _battleShip.xloc + 2 * kBattleSize &&
-	     ynew > _battleShip.yloc - 4 * kBattleSize &&
-	     ynew < _battleShip.yloc + 4 * kBattleSize) ||
-	    (xnew > _battleEnter.xloc - 2 * kBattleSize &&
-	     xnew < _battleEnter.xloc + 2 * kBattleSize &&
-	     ynew > _battleEnter.yloc - 2 * kBattleSize &&
-	     ynew < _battleEnter.yloc + 2 * kBattleSize)) {
+		xnew > _battleShip.xloc - 2 * kBattleSize &&
+		xnew < _battleShip.xloc + 2 * kBattleSize &&
+		ynew > _battleShip.yloc - 4 * kBattleSize &&
+		ynew < _battleShip.yloc + 4 * kBattleSize) ||
+		(xnew > _battleEnter.xloc - 2 * kBattleSize &&
+		xnew < _battleEnter.xloc + 2 * kBattleSize &&
+		ynew > _battleEnter.yloc - 2 * kBattleSize &&
+		ynew < _battleEnter.yloc + 2 * kBattleSize)) {
 		bonk();
 		return;
 	}
@@ -1041,7 +1041,7 @@ void ColonyEngine::battleCommand(int xnew, int ynew) {
 	Locate *pw = _pyramids[qx][qy];
 	for (int i = 0; i < kMaxQuad; i++) {
 		if (wrappedX > pw[i].xloc - kBattleSize && wrappedX < pw[i].xloc + kBattleSize &&
-		    wrappedY > pw[i].yloc - kBattleSize && wrappedY < pw[i].yloc + kBattleSize) {
+			wrappedY > pw[i].yloc - kBattleSize && wrappedY < pw[i].yloc + kBattleSize) {
 			bonk();
 			return;
 		}
@@ -1073,8 +1073,8 @@ void ColonyEngine::battleShoot() {
 	for (int i = 0; i < _battleMaxP; i++) {
 		Locate *target = _battlePwh[i];
 		if (target->xmn < cx && target->xmx > cx &&
-		    target->zmn < cy && target->zmx > cy &&
-		    target->dist < bestDist) {
+			target->zmn < cy && target->zmx > cy &&
+			target->dist < bestDist) {
 			bestDist = target->dist;
 			bestIndex = i;
 		}
@@ -1113,7 +1113,7 @@ void ColonyEngine::battleProjCommand(int xcheck, int ycheck) {
 	_battleProj.yloc = ycheck;
 
 	if (xcheck > _me.xloc - 200 && xcheck < _me.xloc + 200 &&
-	    ycheck > _me.yloc - 200 && ycheck < _me.yloc + 200) {
+		ycheck > _me.yloc - 200 && ycheck < _me.yloc + 200) {
 		debugC(1, kColonyDebugCombat,
 			"battleProjHitPlayer: proj=(%d,%d) player=(%d,%d) delta=[-4,-4,-4]",
 			xcheck, ycheck, _me.xloc, _me.yloc);
@@ -1127,11 +1127,11 @@ void ColonyEngine::battleProjCommand(int xcheck, int ycheck) {
 
 	for (int i = 0; i < 16; i++) {
 		if (xcheck > _bfight[i].xloc - kBattleSize &&
-		    xcheck < _bfight[i].xloc + kBattleSize &&
-		    ycheck > _bfight[i].yloc - kBattleSize &&
-		    ycheck < _bfight[i].yloc + kBattleSize) {
+			xcheck < _bfight[i].xloc + kBattleSize &&
+			ycheck > _bfight[i].yloc - kBattleSize &&
+			ycheck < _bfight[i].yloc + kBattleSize) {
 			while (_bfight[i].xloc - _me.xloc < 2000 &&
-			       _bfight[i].yloc - _me.yloc < 2000) {
+				_bfight[i].yloc - _me.yloc < 2000) {
 				_bfight[i].xloc = _randomSource.getRandomNumber(0x7FFF);
 				_bfight[i].yloc = _randomSource.getRandomNumber(0x7FFF);
 			}
@@ -1142,14 +1142,14 @@ void ColonyEngine::battleProjCommand(int xcheck, int ycheck) {
 	}
 
 	if ((!_orbit &&
-	     xcheck > _battleShip.xloc - 2 * kBattleSize &&
-	     xcheck < _battleShip.xloc + 2 * kBattleSize &&
-	     ycheck > _battleShip.yloc - 4 * kBattleSize &&
-	     ycheck < _battleShip.yloc + 4 * kBattleSize) ||
-	    (xcheck > _battleEnter.xloc - 2 * kBattleSize &&
-	     xcheck < _battleEnter.xloc + 2 * kBattleSize &&
-	     ycheck > _battleEnter.yloc - 2 * kBattleSize &&
-	     ycheck < _battleEnter.yloc + 2 * kBattleSize)) {
+		xcheck > _battleShip.xloc - 2 * kBattleSize &&
+		xcheck < _battleShip.xloc + 2 * kBattleSize &&
+		ycheck > _battleShip.yloc - 4 * kBattleSize &&
+		ycheck < _battleShip.yloc + 4 * kBattleSize) ||
+		(xcheck > _battleEnter.xloc - 2 * kBattleSize &&
+		xcheck < _battleEnter.xloc + 2 * kBattleSize &&
+		ycheck > _battleEnter.yloc - 2 * kBattleSize &&
+		ycheck < _battleEnter.yloc + 2 * kBattleSize)) {
 		_sound->play(Sound::kBonk);
 		_projon = false;
 		return;
@@ -1162,7 +1162,7 @@ void ColonyEngine::battleProjCommand(int xcheck, int ycheck) {
 	Locate *pw = _pyramids[qx][qy];
 	for (int i = 0; i < kMaxQuad; i++) {
 		if (wrappedX > pw[i].xloc - kBattleSize && wrappedX < pw[i].xloc + kBattleSize &&
-		    wrappedY > pw[i].yloc - kBattleSize && wrappedY < pw[i].yloc + kBattleSize) {
+			wrappedY > pw[i].yloc - kBattleSize && wrappedY < pw[i].yloc + kBattleSize) {
 			_sound->play(Sound::kBonk);
 			_projon = false;
 			return;
@@ -1189,9 +1189,9 @@ void ColonyEngine::renderBattle() {
 		_gfx->setDepthState(false, false);
 		_gfx->setWireframe(true, groundColor);
 		_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.0f,
-		                  100000.0f, -100000.0f, -160.0f,
-		                  100000.0f,  100000.0f, -160.0f,
-		                 -100000.0f,  100000.0f, -160.0f, groundColor);
+			100000.0f, -100000.0f, -160.0f,
+			100000.0f,  100000.0f, -160.0f,
+			-100000.0f,  100000.0f, -160.0f, groundColor);
 	}
 
 	// Phase 4: 3D objects with depth testing.
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 549fbd391ee..52f88d052f3 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -498,7 +498,7 @@ void ColonyEngine::updateMouseCapture(bool recenter) {
 Common::Point ColonyEngine::getAimPoint() const {
 	if (_cursorShoot && !_mouseLocked) {
 		return Common::Point(CLIP<int>(_mousePos.x, _screenR.left, _screenR.right - 1),
-		                     CLIP<int>(_mousePos.y, _screenR.top, _screenR.bottom - 1));
+			CLIP<int>(_mousePos.y, _screenR.top, _screenR.bottom - 1));
 	}
 
 	return Common::Point(_centerX, _centerY);
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 6f55792b842..a7ae60c546a 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -597,10 +597,10 @@ private:
 	void draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook, int colorOverride = -1, bool accumulateBounds = false, bool forceVisible = false);
 	void draw3DLeaf(const Thing &obj, const PrismPartDef &def);
 	void draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
-	                  int pt1x, int pt1y, int pt1z, uint32 fillColor, uint32 outlineColor, bool accumulateBounds = false);
+		int pt1x, int pt1y, int pt1z, uint32 fillColor, uint32 outlineColor, bool accumulateBounds = false);
 	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);
+		const PrismPartDef &pupilDef, int pupilColorOverride, bool useLook);
 	bool drawStaticObjectPrisms3D(Thing &obj);
 	void initRobots();
 	void renderCorridor3D();
diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
index 121ba823340..4102f737b2e 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/debugger.cpp
@@ -116,7 +116,7 @@ bool Debugger::cmdTeleport(int argc, const char **argv) {
 
 	// Clear player from current robot array position
 	if (_vm->_me.xindex >= 0 && _vm->_me.xindex < 32 &&
-	    _vm->_me.yindex >= 0 && _vm->_me.yindex < 32)
+		_vm->_me.yindex >= 0 && _vm->_me.yindex < 32)
 		_vm->_robotArray[_vm->_me.xindex][_vm->_me.yindex] = 0;
 
 	// Load the target level
@@ -131,7 +131,7 @@ bool Debugger::cmdTeleport(int argc, const char **argv) {
 				for (int d = 0; d < 5; d++) {
 					int feat = _vm->_mapData[x][y][d][0];
 					if (feat == kWallFeatureUpStairs || feat == kWallFeatureDnStairs ||
-					    feat == kWallFeatureTunnel || feat == kWallFeatureElevator) {
+						feat == kWallFeatureTunnel || feat == kWallFeatureElevator) {
 						targetX = x;
 						targetY = y;
 						const char *name = featureTypeName(feat);
@@ -173,20 +173,20 @@ bool Debugger::cmdPos(int argc, const char **argv) {
 bool Debugger::cmdInfo(int argc, const char **argv) {
 	debugPrintf("=== Colony Game State ===\n");
 	debugPrintf("Level: %d  Position: (%d, %d)  Angle: %d\n",
-	            _vm->_level, _vm->_me.xindex, _vm->_me.yindex, _vm->_me.ang);
+		_vm->_level, _vm->_me.xindex, _vm->_me.yindex, _vm->_me.ang);
 	debugPrintf("Core index: %d\n", _vm->_coreIndex);
 	debugPrintf("Core power:  [0]=%d  [1]=%d  [2]=%d\n",
-	            _vm->_corePower[0], _vm->_corePower[1], _vm->_corePower[2]);
+		_vm->_corePower[0], _vm->_corePower[1], _vm->_corePower[2]);
 	debugPrintf("Core state:  [0]=%d  [1]=%d\n",
-	            _vm->_coreState[0], _vm->_coreState[1]);
+		_vm->_coreState[0], _vm->_coreState[1]);
 	debugPrintf("Core height: [0]=%d  [1]=%d\n",
-	            _vm->_coreHeight[0], _vm->_coreHeight[1]);
+		_vm->_coreHeight[0], _vm->_coreHeight[1]);
 	debugPrintf("Keycard: %s  Unlocked: %s\n",
-	            _vm->_hasKeycard ? "yes" : "no",
-	            _vm->_unlocked ? "yes" : "no");
+		_vm->_hasKeycard ? "yes" : "no",
+		_vm->_unlocked ? "yes" : "no");
 	debugPrintf("Weapons: %d  Armor: %d\n", _vm->_weapons, _vm->_armor);
 	debugPrintf("Orbit: %d  Forklift: %d  CarryType: %d\n",
-	            _vm->_orbit, _vm->_fl, _vm->_carryType);
+		_vm->_orbit, _vm->_fl, _vm->_carryType);
 	debugPrintf("Robots: %d  Speed: %d\n", _vm->_robotNum, _vm->_speedShift);
 	return true;
 }
@@ -197,9 +197,9 @@ bool Debugger::cmdRobots(int argc, const char **argv) {
 		const Thing &obj = _vm->_objects[i];
 		if (obj.alive) {
 			debugPrintf("  #%d  type=%d (%s)  pos=(%d,%d)  alive=%d  visible=%d\n",
-			            i, obj.type, robotTypeName(obj.type),
-			            obj.where.xindex, obj.where.yindex,
-			            obj.alive, obj.visible);
+				i, obj.type, robotTypeName(obj.type),
+				obj.where.xindex, obj.where.yindex,
+				obj.alive, obj.visible);
 			count++;
 		}
 	}
@@ -274,7 +274,7 @@ bool Debugger::cmdGive(int argc, const char **argv) {
 bool Debugger::cmdPower(int argc, const char **argv) {
 	if (argc < 2) {
 		debugPrintf("Core power: [0]=%d  [1]=%d  [2]=%d\n",
-		            _vm->_corePower[0], _vm->_corePower[1], _vm->_corePower[2]);
+			_vm->_corePower[0], _vm->_corePower[1], _vm->_corePower[2]);
 		debugPrintf("Usage: power <core 0-2> <level 0-2>\n");
 		debugPrintf("  0=off, 1=emergency, 2=full\n");
 		return true;
@@ -295,7 +295,7 @@ bool Debugger::cmdPower(int argc, const char **argv) {
 		debugPrintf("Core %d power set to %d\n", core, level);
 	} else {
 		debugPrintf("Core power: [0]=%d  [1]=%d  [2]=%d\n",
-		            _vm->_corePower[0], _vm->_corePower[1], _vm->_corePower[2]);
+			_vm->_corePower[0], _vm->_corePower[1], _vm->_corePower[2]);
 	}
 	return true;
 }
@@ -357,10 +357,10 @@ bool Debugger::cmdBattle(int argc, const char **argv) {
 	prepareBattleDebugState(-500, 0, 96);
 
 	debugPrintf("Entered battle mode outside the ship at (%d, %d) ang=%d\n",
-	            _vm->_me.xloc, _vm->_me.yloc, _vm->_me.ang);
+		_vm->_me.xloc, _vm->_me.yloc, _vm->_me.ang);
 	debugPrintf("Suit: power=[%d,%d,%d] weapons=%d armor=%d\n",
-	            (int)_vm->_me.power[0], (int)_vm->_me.power[1],
-	            (int)_vm->_me.power[2], _vm->_weapons, _vm->_armor);
+		(int)_vm->_me.power[0], (int)_vm->_me.power[1],
+		(int)_vm->_me.power[2], _vm->_weapons, _vm->_armor);
 	return false;
 }
 
@@ -397,10 +397,10 @@ bool Debugger::cmdColony(int argc, const char **argv) {
 	prepareBattleDebugState(16000 - 500, 16000, 96);
 
 	debugPrintf("Entered battle mode outside the colony at (%d, %d) ang=%d\n",
-	            _vm->_me.xloc, _vm->_me.yloc, _vm->_me.ang);
+		_vm->_me.xloc, _vm->_me.yloc, _vm->_me.ang);
 	debugPrintf("Suit: power=[%d,%d,%d] weapons=%d armor=%d\n",
-	            (int)_vm->_me.power[0], (int)_vm->_me.power[1],
-	            (int)_vm->_me.power[2], _vm->_weapons, _vm->_armor);
+		(int)_vm->_me.power[0], (int)_vm->_me.power[1],
+		(int)_vm->_me.power[2], _vm->_weapons, _vm->_armor);
 	debugPrintf("Security state forced to unlocked for colony debug entry\n");
 	return false;
 }
@@ -525,7 +525,7 @@ bool Debugger::cmdSpawn(int argc, const char **argv) {
 	}
 
 	debugPrintf("Spawned %s (type %d) at cell (%d,%d) facing player\n",
-	            robotTypeName(type), type, targetX, targetY);
+		robotTypeName(type), type, targetX, targetY);
 	return false;
 }
 
diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
index 50e86e0ae30..8ab38d96028 100644
--- a/engines/colony/detection.cpp
+++ b/engines/colony/detection.cpp
@@ -51,7 +51,7 @@ const ADGameDescription gameDescriptions[] = {
 		"colony",
 		"",
 		AD_ENTRY2s("Colony", "50f79a9fd68055a49171eaf92f3f6b13", 229888,
-		           "Zounds", "9de09af0a8d62f30d6863ff86c95cc51", 552576),
+			"Zounds", "9de09af0a8d62f30d6863ff86c95cc51", 552576),
 		Common::EN_ANY,
 		Common::kPlatformMacintosh,
 		ADGF_NO_FLAGS,
@@ -61,7 +61,7 @@ const ADGameDescription gameDescriptions[] = {
 		"colony",
 		"",
 		AD_ENTRY2s("logo1.pic", "70d44e40ac19ea0413f1253b781399de", 6689,
-		           "MAP.1", "ab40dc3d9658e8cdc0bee63c2ca9c79b", 3350),
+			"MAP.1", "ab40dc3d9658e8cdc0bee63c2ca9c79b", 3350),
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 6e07eb0346a..eb0a4740bf2 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -52,7 +52,7 @@ void ColonyEngine::interactWithObject(int objNum) {
 	// fl==2 (carrying): only REACTOR (drop), TELEPORT (use), SCREEN; others → bonk
 	if (_fl == 1) {
 		if (obj.type != kObjBox1 && obj.type != kObjBox2 && obj.type != kObjCryo &&
-		    obj.type != kObjTeleport && obj.type != kObjReactor && obj.type != kObjScreen)
+			obj.type != kObjTeleport && obj.type != kObjReactor && obj.type != kObjScreen)
 			return; // silently ignore — original has no default for fl==1
 	} else if (_fl == 2) {
 		if (obj.type != kObjReactor && obj.type != kObjTeleport && obj.type != kObjScreen) {
@@ -379,7 +379,7 @@ void ColonyEngine::cShoot() {
 		if (obj.where.xmn > obj.where.xmx || obj.where.zmn > obj.where.zmx)
 			continue;
 		if (obj.where.xmn > cx || obj.where.xmx < cx ||
-		    obj.where.zmn > cy || obj.where.zmx < cy)
+			obj.where.zmn > cy || obj.where.zmx < cy)
 			continue;
 
 		int t = obj.type;
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index a1dad089833..105b6b6ea7e 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -183,7 +183,7 @@ void ColonyEngine::playIntro() {
 				Common::MacResIDArray nfntIDs = _resMan->getResIDArray(MKTAG('N', 'F', 'N', 'T'));
 				Common::MacResIDArray fontIDs = _resMan->getResIDArray(MKTAG('F', 'O', 'N', 'T'));
 				debugC(1, kColonyDebugUI, "playIntro: FONT/NFNT %d not found. Available NFNT IDs: %d, FONT IDs: %d",
-				      fontResID, nfntIDs.size(), fontIDs.size());
+					fontResID, nfntIDs.size(), fontIDs.size());
 				for (uint i = 0; i < nfntIDs.size(); i++)
 					debugC(1, kColonyDebugUI, "  NFNT %d", nfntIDs[i]);
 				for (uint i = 0; i < fontIDs.size(); i++)
@@ -664,7 +664,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 				int rr1 = rtable[d];
 				int d2 = d - deltapd;
 				if (d2 < 1)
-			d2 = 1;
+					d2 = 1;
 				int rr2 = rtable[d2];
 				int x1 = centerX + (int)(((long long)s * rr1) >> 7);
 				int y1 = centerY + (int)(((long long)c * rr1) >> 7);
@@ -1044,8 +1044,8 @@ bool ColonyEngine::drawPict(int resID) {
 			int clipY2 = y + surface->h - 1;
 
 			debugC(1, kColonyDebugUI, "drawPict(%d): %dx%d at (%d,%d), format=%dbpp, palette=%d entries",
-			      resID, surface->w, surface->h, x, y,
-			      surface->format.bytesPerPixel * 8, pictPal.size());
+				resID, surface->w, surface->h, x, y,
+				surface->format.bytesPerPixel * 8, pictPal.size());
 
 			// Draw PICT pixels using direct RGB (packRGB) for full color support.
 			for (int iy = 0; iy < surface->h; iy++) {
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 2e67f78e8b7..be6629f85ac 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -124,31 +124,31 @@ void ColonyEngine::loadMap(int mnum) {
 						for (int l = 0; l < 5; l++) {
 							_mapData[i][j][k][l] = buffer[c++];
 						}
-							// PACKIT.C: center feature type 6 marks static map objects.
-							if (k == 4 && _mapData[i][j][4][0] == 6 && i < 31 && j < 31) {
-								Thing obj;
-								clearThing(obj);
-								obj.alive = 1;
-								obj.visible = 0;
-								obj.type = _mapData[i][j][4][1] + kBaseObject;
-								obj.where.xloc = (i << 8) + 128;
-								obj.where.yloc = (j << 8) + 128;
-								obj.where.xindex = i;
-								obj.where.yindex = j;
-								obj.where.ang = (uint8)(_mapData[i][j][4][2] + 32);
-								obj.where.look = obj.where.ang;
-								if ((int)_objects.size() >= kMaxObjectSlots) {
-									warning("loadMap: object table full on level %d, skipping static object type %d at (%d,%d)",
-										mnum, obj.type, i, j);
-									continue;
-								}
-								_objects.push_back(obj);
-								const int objNum = (int)_objects.size(); // 1-based, DOS-style robot slots
-								// CWall/FWall use diagonal collision, not cell-based blocking.
-								if (obj.type != kObjFWall && obj.type != kObjCWall &&
-								    objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
-									_robotArray[i][j] = (uint8)objNum;
+						// PACKIT.C: center feature type 6 marks static map objects.
+						if (k == 4 && _mapData[i][j][4][0] == 6 && i < 31 && j < 31) {
+							Thing obj;
+							clearThing(obj);
+							obj.alive = 1;
+							obj.visible = 0;
+							obj.type = _mapData[i][j][4][1] + kBaseObject;
+							obj.where.xloc = (i << 8) + 128;
+							obj.where.yloc = (j << 8) + 128;
+							obj.where.xindex = i;
+							obj.where.yindex = j;
+							obj.where.ang = (uint8)(_mapData[i][j][4][2] + 32);
+							obj.where.look = obj.where.ang;
+							if ((int)_objects.size() >= kMaxObjectSlots) {
+								warning("loadMap: object table full on level %d, skipping static object type %d at (%d,%d)",
+									mnum, obj.type, i, j);
+								continue;
 							}
+							_objects.push_back(obj);
+							const int objNum = (int)_objects.size(); // 1-based, DOS-style robot slots
+							// CWall/FWall use diagonal collision, not cell-based blocking.
+							if (obj.type != kObjFWall && obj.type != kObjCWall &&
+								objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
+								_robotArray[i][j] = (uint8)objNum;
+						}
 					} else {
 						_mapData[i][j][k][0] = 0;
 					}
@@ -219,7 +219,7 @@ void ColonyEngine::resetObjectSlot(int slot, int type, int xloc, int yloc, uint8
 
 	const int objNum = slot + 1;
 	if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-	    obj.where.yindex >= 0 && obj.where.yindex < 32) {
+		obj.where.yindex >= 0 && obj.where.yindex < 32) {
 		if (type > kRobUPyramid && type < kRobQueen)
 			_foodArray[obj.where.xindex][obj.where.yindex] = (uint8)objNum;
 		else
@@ -370,8 +370,8 @@ void ColonyEngine::newPatch(int type, const PassPatch &from, const PassPatch &to
 	// Search for existing patch where 'from' matches an existing 'to'
 	for (uint i = 0; i < _patches.size(); i++) {
 		if (from.level == _patches[i].to.level &&
-		    from.xindex == _patches[i].to.xindex &&
-		    from.yindex == _patches[i].to.yindex) {
+			from.xindex == _patches[i].to.xindex &&
+			from.yindex == _patches[i].to.yindex) {
 			_patches[i].to.level = to.level;
 			_patches[i].to.xindex = to.xindex;
 			_patches[i].to.yindex = to.yindex;
@@ -408,8 +408,8 @@ void ColonyEngine::newPatch(int type, const PassPatch &from, const PassPatch &to
 bool ColonyEngine::patchMapTo(const PassPatch &to, uint8 *mapdata) {
 	for (uint i = 0; i < _patches.size(); i++) {
 		if (to.level == _patches[i].to.level &&
-		    to.xindex == _patches[i].to.xindex &&
-		    to.yindex == _patches[i].to.yindex) {
+			to.xindex == _patches[i].to.xindex &&
+			to.yindex == _patches[i].to.yindex) {
 			for (int j = 0; j < 5; j++)
 				mapdata[j] = _patches[i].mapdata[j];
 			return true;
@@ -422,8 +422,8 @@ bool ColonyEngine::patchMapTo(const PassPatch &to, uint8 *mapdata) {
 bool ColonyEngine::patchMapFrom(const PassPatch &from, uint8 *mapdata) {
 	for (uint i = 0; i < _patches.size(); i++) {
 		if (from.level == _patches[i].from.level &&
-		    from.xindex == _patches[i].from.xindex &&
-		    from.yindex == _patches[i].from.yindex) {
+			from.xindex == _patches[i].from.xindex &&
+			from.yindex == _patches[i].from.yindex) {
 			mapdata[2] = _patches[i].to.level;
 			mapdata[3] = _patches[i].to.xindex;
 			mapdata[4] = _patches[i].to.yindex;
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 9df0e00abe4..c7601fb2e31 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -64,7 +64,7 @@ static const int kTunnelStraight[60] = {0};
 
 static uint32 packTunnelMacColor(const uint16 rgb[3]) {
 	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
-	       ((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
+		((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
 }
 
 static void fillTunnelPattern(Renderer *gfx, const Common::Rect &rect, uint32 fg, uint32 bg, int pattern) {
@@ -328,8 +328,8 @@ int ColonyEngine::checkwallMoveTo(int xnew, int ynew, int xind2, int yind2, Loca
 	if (rnum)
 		return rnum;
 	if (trailCode != 0 && pobject->type == kMeNum &&
-	    pobject->xindex >= 0 && pobject->xindex < 32 &&
-	    pobject->yindex >= 0 && pobject->yindex < 32)
+		pobject->xindex >= 0 && pobject->xindex < 32 &&
+		pobject->yindex >= 0 && pobject->yindex < 32)
 		_dirXY[pobject->xindex][pobject->yindex] = trailCode;
 	pobject->yindex = yind2;
 	pobject->xindex = xind2;
@@ -375,8 +375,8 @@ int ColonyEngine::checkwallTryFeature(int xnew, int ynew, int xind2, int yind2,
 			break;
 		}
 		if (trailCode != 0 && pobject->type == kMeNum &&
-		    pobject->xindex >= 0 && pobject->xindex < 32 &&
-		    pobject->yindex >= 0 && pobject->yindex < 32)
+			pobject->xindex >= 0 && pobject->xindex < 32 &&
+			pobject->yindex >= 0 && pobject->yindex < 32)
 			_dirXY[pobject->xindex][pobject->yindex] = trailCode;
 		pobject->yindex = yind2;
 		pobject->xindex = xind2;
@@ -601,7 +601,7 @@ int ColonyEngine::goToDestination(const uint8 *map, Locate *pobject) {
 		}
 
 		if (_me.xindex >= 0 && _me.xindex < 32 &&
-		    _me.yindex >= 0 && _me.yindex < 32)
+			_me.yindex >= 0 && _me.yindex < 32)
 			_robotArray[_me.xindex][_me.yindex] = 0;
 
 		_gameMode = kModeBattle;
@@ -641,7 +641,7 @@ int ColonyEngine::goToDestination(const uint8 *map, Locate *pobject) {
 	}
 
 	if (pobject->xindex >= 0 && pobject->xindex < 32 &&
-	    pobject->yindex >= 0 && pobject->yindex < 32)
+		pobject->yindex >= 0 && pobject->yindex < 32)
 		_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
 
 	return 2;
@@ -726,9 +726,9 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 		const int result = goToDestination(map, pobject);
 		if (result == 2) {
 			debugC(1, kColonyDebugMove, "Level change via %s: level=%d pos=(%d,%d)",
-			      map[0] == kWallFeatureUpStairs ? "upstairs" :
-			      map[0] == kWallFeatureDnStairs ? "downstairs" : "tunnel",
-			      _level, pobject->xindex, pobject->yindex);
+				map[0] == kWallFeatureUpStairs ? "upstairs" :
+				map[0] == kWallFeatureDnStairs ? "downstairs" : "tunnel",
+				_level, pobject->xindex, pobject->yindex);
 		}
 		return result;
 	}
@@ -777,7 +777,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 			}
 
 			if (pobject->xindex >= 0 && pobject->xindex < 32 &&
-			    pobject->yindex >= 0 && pobject->yindex < 32)
+				pobject->yindex >= 0 && pobject->yindex < 32)
 				_robotArray[pobject->xindex][pobject->yindex] = kMeNum;
 
 			debugC(1, kColonyDebugMove, "Elevator: level=%d pos=(%d,%d)", _level, pobject->xindex, pobject->yindex);
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 79a08d6d9c4..b124bda0531 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -268,9 +268,9 @@ static int mapObjColorToMacColor(int colorIdx, int level) {
 }
 
 static void projectCorridorPointClamped(const Common::Rect &screenR, int look, int lookY,
-                                        const int *sint, const int *cost, int camX, int camY,
-                                        float worldX, float worldY, float worldZ,
-                                        int &screenX, int &screenY) {
+	const int *sint, const int *cost, int camX, int camY,
+	float worldX, float worldY, float worldZ,
+	int &screenX, int &screenY) {
 	const float dx = worldX - camX;
 	const float dy = worldY - camY;
 	const float dz = worldZ;
@@ -409,9 +409,9 @@ const uint8 *ColonyEngine::mapFeatureAt(int x, int y, int direction) const {
 }
 
 static bool projectCorridorPoint(const Common::Rect &screenR, uint8 look, int8 lookY,
-                                 const int *sint, const int *cost, int camX, int camY,
-                                 float worldX, float worldY, float worldZ,
-                                 int &screenX, int &screenY) {
+	const int *sint, const int *cost, int camX, int camY,
+	float worldX, float worldY, float worldZ,
+	int &screenX, int &screenY) {
 	const float dx = worldX - camX;
 	const float dy = worldY - camY;
 	const float dz = worldZ;
@@ -466,14 +466,14 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 		transformedY[i] = (float)(ry + obj.where.yloc);
 		transformedZ[i] = (float)(oz - 160);
 		projectCorridorPointClamped(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
-		                            transformedX[i], transformedY[i], transformedZ[i],
-		                            projectedX[i], projectedY[i]);
+			transformedX[i], transformedY[i], transformedZ[i],
+			projectedX[i], projectedY[i]);
 
 		if (accumulateBounds) {
 			int sx = 0;
 			int sy = 0;
 			if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
-			                         transformedX[i], transformedY[i], transformedZ[i], sx, sy)) {
+				transformedX[i], transformedY[i], transformedZ[i], sx, sy)) {
 				obj.where.xmn = MIN(obj.where.xmn, sx);
 				obj.where.xmx = MAX(obj.where.xmx, sx);
 				obj.where.zmn = MIN(obj.where.zmn, sy);
@@ -517,7 +517,7 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 				for (int j = 0; j < count; j++) {
 					int next = (j + 1) % count;
 					_gfx->draw3DLine(px[j], py[j], pz[j],
-					                 px[next], py[next], pz[next], color);
+						px[next], py[next], pz[next], color);
 				}
 				continue;
 			}
@@ -541,14 +541,14 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 						fg = bg;
 					}
 					debugC(5, kColonyDebugRender, "draw3DPrism Mac corridor wall: fg=0x%08X bg=0x%08X lit=%d",
-					      fg, bg, lit);
+						fg, bg, lit);
 				} else {
 					int mIdx = mapObjColorToMacColor(colorIdx, _level);
 					pattern = _macColors[mIdx].pattern;
 					fg = packMacColor(_macColors[mIdx].fg);
 					bg = packMacColor(_macColors[mIdx].bg);
 					debugC(5, kColonyDebugRender, "draw3DPrism Mac: colorIdx=%d mIdx=%d pat=%d fg=0x%08X bg=0x%08X lit=%d",
-					      colorIdx, mIdx, pattern, fg, bg, lit);
+						colorIdx, mIdx, pattern, fg, bg, lit);
 
 					if (!lit) {
 						// Mac unlit: all non-wall surfaces fill solid black
@@ -668,8 +668,8 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 }
 
 void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
-                                int pt1x, int pt1y, int pt1z,
-                                uint32 fillColor, uint32 outlineColor, bool accumulateBounds) {
+	int pt1x, int pt1y, int pt1z,
+	uint32 fillColor, uint32 outlineColor, bool accumulateBounds) {
 	// Original Colony eye/ball primitives store the bottom pole in pt0 and the
 	// sphere center in pt1. The classic renderer builds the oval from the
 	// screen-space delta between those two projected points, so the world-space
@@ -731,7 +731,7 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 			int sx = 0;
 			int sy = 0;
 			if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
-			                         px[i], py[i], pz[i], sx, sy)) {
+				px[i], py[i], pz[i], sx, sy)) {
 				obj.where.xmn = MIN(obj.where.xmn, sx);
 				obj.where.xmx = MAX(obj.where.xmx, sx);
 				obj.where.zmn = MIN(obj.where.zmn, sy);
@@ -914,15 +914,15 @@ void ColonyEngine::renderCorridor3D() {
 	// Set wireframe fill to each surface's own color so they aren't all wallFill.
 	_gfx->setWireframe(true, floorColor);
 	_gfx->draw3DQuad(-100000.0f, -100000.0f, -160.0f,
-	                100000.0f, -100000.0f, -160.0f,
-	                100000.0f, 100000.0f, -160.0f,
-	                -100000.0f, 100000.0f, -160.0f, floorColor);
+		100000.0f, -100000.0f, -160.0f,
+		100000.0f, 100000.0f, -160.0f,
+		-100000.0f, 100000.0f, -160.0f, floorColor);
 
 	_gfx->setWireframe(true, ceilColor);
 	_gfx->draw3DQuad(-100000.0f, -100000.0f, 160.0f,
-	                100000.0f, -100000.0f, 160.0f,
-	                100000.0f, 100000.0f, 160.0f,
-	                -100000.0f, 100000.0f, 160.0f, ceilColor);
+		100000.0f, -100000.0f, 160.0f,
+		100000.0f, 100000.0f, 160.0f,
+		-100000.0f, 100000.0f, 160.0f, ceilColor);
 
 	// Ceiling grid (Cuadricule) - DOS wireframe mode only.
 	// Mac color mode: original corridor renderer only showed ceiling edges at wall
diff --git a/engines/colony/render_features.cpp b/engines/colony/render_features.cpp
index f4aa52aa9d9..0e3ee6a0284 100644
--- a/engines/colony/render_features.cpp
+++ b/engines/colony/render_features.cpp
@@ -275,9 +275,9 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 				if (i == 0 && nextJ == 0)
 					continue;
 				if ((nearlyEqual(u[i], u[j]) && nearlyEqual(v[i], v[j])) ||
-				    (nearlyEqual(u[i], u[nextJ]) && nearlyEqual(v[i], v[nextJ])) ||
-				    (nearlyEqual(u[nextI], u[j]) && nearlyEqual(v[nextI], v[j])) ||
-				    (nearlyEqual(u[nextI], u[nextJ]) && nearlyEqual(v[nextI], v[nextJ])))
+					(nearlyEqual(u[i], u[nextJ]) && nearlyEqual(v[i], v[nextJ])) ||
+					(nearlyEqual(u[nextI], u[j]) && nearlyEqual(v[nextI], v[j])) ||
+					(nearlyEqual(u[nextI], u[nextJ]) && nearlyEqual(v[nextI], v[nextJ])))
 					continue;
 
 				float ix, iy;
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index a2f4aa2b489..c9011968932 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -85,9 +85,9 @@ static int mapEyeOverlayColorToMacPattern(int colorIdx) {
 }
 
 static bool projectCorridorPointRaw(const Common::Rect &screenR, uint8 look, int8 lookY,
-                                    const int *sint, const int *cost, int camX, int camY,
-                                    float worldX, float worldY, float worldZ,
-                                    int &screenX, int &screenY) {
+	const int *sint, const int *cost, int camX, int camY,
+	float worldX, float worldY, float worldZ,
+	int &screenX, int &screenY) {
 	const float dx = worldX - camX;
 	const float dy = worldY - camY;
 	const float dz = worldZ;
@@ -1187,7 +1187,7 @@ void ColonyEngine::drawStaticObjects() {
 		// (narrows crosshair brackets to indicate a target is in the line of fire)
 		int t = obj.type;
 		if ((t >= kRobEye && t <= kRobUPyramid) ||
-		    (t >= kRobQueen && t <= kRobSoldier)) {
+			(t >= kRobQueen && t <= kRobSoldier)) {
 			if (obj.where.xmn < _centerX && obj.where.xmx > _centerX)
 				_insight = true;
 		}
@@ -1221,8 +1221,8 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 		transformedY[i] = (float)(ry + thing.where.yloc);
 		transformedZ[i] = (float)(oz - 160);
 		projected[i] = projectCorridorPointRaw(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
-		                                       transformedX[i], transformedY[i], transformedZ[i],
-		                                       projectedX[i], projectedY[i]);
+			transformedX[i], transformedY[i], transformedZ[i],
+			projectedX[i], projectedY[i]);
 	}
 
 	const int *surface = &def.surfaces[0][2];
@@ -1605,7 +1605,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	// === Robot types (1-20) ===
 	case kRobEye:
 		if ((obj.where.xloc - _me.xloc) * (obj.where.xloc - _me.xloc) +
-		    (obj.where.yloc - _me.yloc) * (obj.where.yloc - _me.yloc) <= 64 * 64) {
+			(obj.where.yloc - _me.yloc) * (obj.where.yloc - _me.yloc) <= 64 * 64) {
 			break;
 		}
 		draw3DSphere(obj, 0, 0, 100, 0, 0, 200, eyeballColor, kColorBlack, true);
@@ -1706,9 +1706,9 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			resetObjectBounds(_screenR, rightEye.where);
 
 			const long leftDist = (leftEye.where.xloc - _me.xloc) * (leftEye.where.xloc - _me.xloc) +
-			                      (leftEye.where.yloc - _me.yloc) * (leftEye.where.yloc - _me.yloc);
+				(leftEye.where.yloc - _me.yloc) * (leftEye.where.yloc - _me.yloc);
 			const long rightDist = (rightEye.where.xloc - _me.xloc) * (rightEye.where.xloc - _me.xloc) +
-			                       (rightEye.where.yloc - _me.yloc) * (rightEye.where.yloc - _me.yloc);
+				(rightEye.where.yloc - _me.yloc) * (rightEye.where.yloc - _me.yloc);
 			const bool leftFirst = leftDist >= rightDist;
 			Thing &farEye = leftFirst ? leftEye : rightEye;
 			Thing &nearEye = leftFirst ? rightEye : leftEye;
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index a07462bbcaf..d0aa0de6511 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -544,7 +544,7 @@ void OpenGLRenderer::computeScreenViewport() {
 
 		// Pillarboxing/Letterboxing
 		_screenViewport.translate((screenWidth - viewportWidth) / 2,
-		                           (screenHeight - viewportHeight) / 2);
+			(screenHeight - viewportHeight) / 2);
 	} else {
 		_screenViewport = Common::Rect(screenWidth, screenHeight);
 	}
@@ -706,7 +706,7 @@ void OpenGLRenderer::drawSurface(const Graphics::Surface *surf, int x, int y) {
 	glLoadIdentity();
 
 	glViewport(_screenViewport.left, _system->getHeight() - _screenViewport.bottom,
-	           _screenViewport.width(), _screenViewport.height());
+		_screenViewport.width(), _screenViewport.height());
 
 	glDisable(GL_DEPTH_TEST);
 	glEnable(GL_TEXTURE_2D);
@@ -723,7 +723,7 @@ void OpenGLRenderer::drawSurface(const Graphics::Surface *surf, int x, int y) {
 	// GL_UNSIGNED_INT_8_8_8_8 reads a uint32 and maps bits 24..31→R, 16..23→G,
 	// 8..15→B, 0..7→A, matching our pixel layout regardless of endianness.
 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0,
-	             GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, surf->getPixels());
+		GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, surf->getPixels());
 
 	// Draw textured quad covering the specified region
 	float dx = x * scaleX;
@@ -752,7 +752,7 @@ void OpenGLRenderer::drawSurface(const Graphics::Surface *surf, int x, int y) {
 
 	// Restore the game's 2D ortho viewport
 	glViewport(_screenViewport.left, _system->getHeight() - _screenViewport.bottom,
-	           _screenViewport.width(), _screenViewport.height());
+		_screenViewport.width(), _screenViewport.height());
 }
 
 void OpenGLRenderer::copyToScreen() {
@@ -763,11 +763,11 @@ void OpenGLRenderer::copyToScreen() {
 Graphics::Surface *OpenGLRenderer::getScreenshot() {
 	Graphics::Surface *surface = new Graphics::Surface();
 	surface->create(_screenViewport.width(), _screenViewport.height(),
-	                Graphics::PixelFormat::createFormatRGBA32());
+		Graphics::PixelFormat::createFormatRGBA32());
 
 	glReadPixels(_screenViewport.left, _system->getHeight() - _screenViewport.bottom,
-	             _screenViewport.width(), _screenViewport.height(),
-	             GL_RGBA, GL_UNSIGNED_BYTE, surface->getPixels());
+		_screenViewport.width(), _screenViewport.height(),
+		GL_RGBA, GL_UNSIGNED_BYTE, surface->getPixels());
 	surface->flipVertical(Common::Rect(surface->w, surface->h));
 	return surface;
 }
diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
index a11f3dee9bc..ddc6b08914c 100644
--- a/engines/colony/savegame.cpp
+++ b/engines/colony/savegame.cpp
@@ -276,8 +276,8 @@ bool findInvalidActiveObjectSlot(const Common::Array<Thing> &objects, uint32 &in
 
 bool ColonyEngine::hasFeature(EngineFeature f) const {
 	return f == kSupportsReturnToLauncher ||
-	       f == kSupportsLoadingDuringRuntime ||
-	       f == kSupportsSavingDuringRuntime;
+		f == kSupportsLoadingDuringRuntime ||
+		f == kSupportsSavingDuringRuntime;
 }
 
 bool ColonyEngine::canSaveGameStateCurrently(Common::U32String *msg) {
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index 871f02c0a2d..04f7a8685d3 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -100,7 +100,7 @@ void ColonyEngine::copyOverflowObjectToSlot(int num) {
 			continue;
 
 		if (source.where.xindex >= 0 && source.where.xindex < 32 &&
-		    source.where.yindex >= 0 && source.where.yindex < 32) {
+			source.where.yindex >= 0 && source.where.yindex < 32) {
 			if (isEggType(source.type)) {
 				if (_foodArray[source.where.xindex][source.where.yindex] == objectNum)
 					_foodArray[source.where.xindex][source.where.yindex] = (uint8)num;
@@ -123,7 +123,7 @@ void ColonyEngine::respawnObject(int num, int type) {
 
 	Thing &obj = _objects[num - 1];
 	if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-	    obj.where.yindex >= 0 && obj.where.yindex < 32) {
+		obj.where.yindex >= 0 && obj.where.yindex < 32) {
 		if (isEggType(obj.type)) {
 			if (_foodArray[obj.where.xindex][obj.where.yindex] == num)
 				_foodArray[obj.where.xindex][obj.where.yindex] = 0;
@@ -299,8 +299,8 @@ void ColonyEngine::queenThink(int num) {
 		return;
 
 	if (updatedObj.where.xindex >= 0 && updatedObj.where.xindex < 32 &&
-	    updatedObj.where.yindex >= 0 && updatedObj.where.yindex < 32 &&
-	    _robotArray[updatedObj.where.xindex][updatedObj.where.yindex] == num)
+		updatedObj.where.yindex >= 0 && updatedObj.where.yindex < 32 &&
+		_robotArray[updatedObj.where.xindex][updatedObj.where.yindex] == num)
 		_robotArray[updatedObj.where.xindex][updatedObj.where.yindex] = 0;
 
 	updatedObj.alive = 0;
@@ -331,8 +331,8 @@ void ColonyEngine::droneThink(int num) {
 
 	if (obj.type == kRobDrone) {
 		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-		    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
-		    _robotArray[obj.where.xindex][obj.where.yindex] == num)
+			obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+			_robotArray[obj.where.xindex][obj.where.yindex] == num)
 			_robotArray[obj.where.xindex][obj.where.yindex] = 0;
 		obj.alive = 0;
 		_sound->play(Sound::kExplode);
@@ -353,8 +353,8 @@ void ColonyEngine::eggThink(int num) {
 	if (_allGrow) {
 		obj.time--;
 	} else if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-	           obj.where.yindex >= 0 && obj.where.yindex < 32 &&
-	           _foodArray[obj.where.xindex][obj.where.yindex] == 0) {
+		obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+		_foodArray[obj.where.xindex][obj.where.yindex] == 0) {
 		_foodArray[obj.where.xindex][obj.where.yindex] = (uint8)num;
 		if (_robotArray[obj.where.xindex][obj.where.yindex] == num)
 			_robotArray[obj.where.xindex][obj.where.yindex] = 0;
@@ -375,10 +375,10 @@ bool ColonyEngine::layEgg(int type, int xindex, int yindex) {
 	};
 
 	if (hasFood(xindex, yindex) ||
-	    hasFood(xindex + 1, yindex) ||
-	    hasFood(xindex - 1, yindex) ||
-	    hasFood(xindex, yindex + 1) ||
-	    hasFood(xindex, yindex - 1))
+		hasFood(xindex + 1, yindex) ||
+		hasFood(xindex - 1, yindex) ||
+		hasFood(xindex, yindex + 1) ||
+		hasFood(xindex, yindex - 1))
 		return false;
 
 	return createObject(type, (xindex << 8) + 128, (yindex << 8) + 128, _randomSource.getRandomNumber(255));
@@ -432,8 +432,8 @@ void ColonyEngine::moveThink(int num) {
 		}
 
 		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-		    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
-		    _robotArray[obj.where.xindex][obj.where.yindex] == num)
+			obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+			_robotArray[obj.where.xindex][obj.where.yindex] == num)
 			_robotArray[obj.where.xindex][obj.where.yindex] = 0;
 
 		_suppressCollisionSound = true;
@@ -447,15 +447,15 @@ void ColonyEngine::moveThink(int num) {
 		}
 
 		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-		    obj.where.yindex >= 0 && obj.where.yindex < 32)
+			obj.where.yindex >= 0 && obj.where.yindex < 32)
 			_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)num;
 		return;
 	}
 
 	case kOpcodeFShoot: {
 		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-		    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
-		    _robotArray[obj.where.xindex][obj.where.yindex] == num)
+			obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+			_robotArray[obj.where.xindex][obj.where.yindex] == num)
 			_robotArray[obj.where.xindex][obj.where.yindex] = 0;
 
 		_suppressCollisionSound = true;
@@ -469,7 +469,7 @@ void ColonyEngine::moveThink(int num) {
 		}
 
 		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-		    obj.where.yindex >= 0 && obj.where.yindex < 32)
+			obj.where.yindex >= 0 && obj.where.yindex < 32)
 			_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)num;
 
 		if (scanForPlayer(num) == kMeNum) {
@@ -570,12 +570,12 @@ void ColonyEngine::snoopThink(int num) {
 		}
 
 		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-		    obj.where.yindex >= 0 && obj.where.yindex < 32)
+			obj.where.yindex >= 0 && obj.where.yindex < 32)
 			_robotArray[obj.where.xindex][obj.where.yindex] = (uint8)num;
 
 		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-		    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
-		    _dirXY[obj.where.xindex][obj.where.yindex]) {
+			obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+			_dirXY[obj.where.xindex][obj.where.yindex]) {
 			obj.opcode = kOpcodeSnoop;
 			obj.where.ang &= 0xF8;
 			obj.where.look = obj.where.ang;
@@ -597,7 +597,7 @@ void ColonyEngine::snoopThink(int num) {
 
 		obj.opcode = kOpcodeForward;
 		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-		    obj.where.yindex >= 0 && obj.where.yindex < 32)
+			obj.where.yindex >= 0 && obj.where.yindex < 32)
 			_dirXY[obj.where.xindex][obj.where.yindex] = 0;
 		obj.counter = 0x3F;
 		break;
@@ -682,8 +682,8 @@ void ColonyEngine::growRobot(int num) {
 	case kRobMCube:
 	case kRobMUPyramid:
 		if (obj.where.xindex >= 0 && obj.where.xindex < 32 &&
-		    obj.where.yindex >= 0 && obj.where.yindex < 32 &&
-		    _robotArray[obj.where.xindex][obj.where.yindex] == 0) {
+			obj.where.yindex >= 0 && obj.where.yindex < 32 &&
+			_robotArray[obj.where.xindex][obj.where.yindex] == 0) {
 			obj.count = 0;
 			obj.type -= 4;
 			obj.where.wallPad = robotWallPad(obj.type);
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 13118ce62c4..37ce711ac91 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -353,7 +353,7 @@ void ColonyEngine::updateViewportLayout() {
 		// _headsUpRect = floorRect (8,8)-(62,62) relative to moveWindow
 		// This is the minimap clipping area  must NOT overlap compass dish
 		_headsUpRect = makeSafeRect(moveLeft + 8, moveTop + 8,
-		                            moveLeft + 2 * CCENTER - 8, moveTop + 2 * CCENTER - 8);
+			moveLeft + 2 * CCENTER - 8, moveTop + 2 * CCENTER - 8);
 
 		// _compassRect = entire moveWindow (used for compass dish drawing)
 		_compassRect = makeSafeRect(moveLeft, moveTop, moveLeft + moveW, moveTop + moveH);
@@ -668,7 +668,7 @@ void ColonyEngine::drawDashboardMac() {
 void ColonyEngine::drawMiniMapMarker(int x, int y, int halfSize, uint32 color, bool isMac, const Common::Rect *clip) {
 	const Common::Rect &cr = clip ? *clip : _headsUpRect;
 	if (x < cr.left + 1 || x >= cr.right - 1 ||
-	    y < cr.top + 1 || y >= cr.bottom - 1)
+		y < cr.top + 1 || y >= cr.bottom - 1)
 		return;
 	if (isMac) {
 		_gfx->drawEllipse(x, y, halfSize, halfSize, color);
@@ -826,28 +826,28 @@ void ColonyEngine::markVisited() {
 	if (cy + 1 < 32) {
 		canN = !(_wall[cx][cy + 1] & 0x01);
 		if (canN && cx < 31 && cy < 31 &&
-		    (_mapData[cx][cy][kDirNorth][0] == kWallFeatureDoor || _mapData[cx][cy][kDirNorth][0] == kWallFeatureAirlock))
+			(_mapData[cx][cy][kDirNorth][0] == kWallFeatureDoor || _mapData[cx][cy][kDirNorth][0] == kWallFeatureAirlock))
 			canN = false;
 	}
 	// South: wall at south edge of cell (cx, cy)
 	if (cy - 1 >= 0) {
 		canS = !(_wall[cx][cy] & 0x01);
 		if (canS && cx < 31 && cy < 31 &&
-		    (_mapData[cx][cy][kDirSouth][0] == kWallFeatureDoor || _mapData[cx][cy][kDirSouth][0] == kWallFeatureAirlock))
+			(_mapData[cx][cy][kDirSouth][0] == kWallFeatureDoor || _mapData[cx][cy][kDirSouth][0] == kWallFeatureAirlock))
 			canS = false;
 	}
 	// East: wall at west edge of cell (cx+1, cy)
 	if (cx + 1 < 32) {
 		canE = !(_wall[cx + 1][cy] & 0x02);
 		if (canE && cx < 31 && cy < 31 &&
-		    (_mapData[cx][cy][kDirEast][0] == kWallFeatureDoor || _mapData[cx][cy][kDirEast][0] == kWallFeatureAirlock))
+			(_mapData[cx][cy][kDirEast][0] == kWallFeatureDoor || _mapData[cx][cy][kDirEast][0] == kWallFeatureAirlock))
 			canE = false;
 	}
 	// West: wall at west edge of cell (cx, cy)
 	if (cx - 1 >= 0) {
 		canW = !(_wall[cx][cy] & 0x02);
 		if (canW && cx < 31 && cy < 31 &&
-		    (_mapData[cx][cy][kDirWest][0] == kWallFeatureDoor || _mapData[cx][cy][kDirWest][0] == kWallFeatureAirlock))
+			(_mapData[cx][cy][kDirWest][0] == kWallFeatureDoor || _mapData[cx][cy][kDirWest][0] == kWallFeatureAirlock))
 			canW = false;
 	}
 
@@ -865,8 +865,8 @@ void ColonyEngine::markVisited() {
 
 static bool isPassableFeature(int feat) {
 	return feat == kWallFeatureDoor || feat == kWallFeatureAirlock ||
-	       feat == kWallFeatureUpStairs || feat == kWallFeatureDnStairs ||
-	       feat == kWallFeatureTunnel || feat == kWallFeatureElevator;
+		feat == kWallFeatureUpStairs || feat == kWallFeatureDnStairs ||
+		feat == kWallFeatureTunnel || feat == kWallFeatureElevator;
 }
 
 void ColonyEngine::automapCellCorner(int dx, int dy, int xloc, int yloc, int lExt, int tsin, int tcos, int ccx, int ccy, int &sx, int &sy) {


Commit: 2f391fa69b5f62160fe56bb041f62f827a043e1d
    https://github.com/scummvm/scummvm/commit/2f391fa69b5f62160fe56bb041f62f827a043e1d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:56+02:00

Commit Message:
COLONY: fix include order in all files, common/ first, then engine main, then engine-specific

Changed paths:
    engines/colony/animation.cpp
    engines/colony/battle.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/debugger.cpp
    engines/colony/detection.cpp
    engines/colony/gfx.cpp
    engines/colony/interaction.cpp
    engines/colony/intro.cpp
    engines/colony/map.cpp
    engines/colony/metaengine.cpp
    engines/colony/movement.cpp
    engines/colony/render.cpp
    engines/colony/render_features.cpp
    engines/colony/render_objects.cpp
    engines/colony/renderer_opengl.cpp
    engines/colony/savegame.cpp
    engines/colony/sound.cpp
    engines/colony/sound.h
    engines/colony/think.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index a6e3f241fb4..ed6faf1873a 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -25,14 +25,14 @@
  *
  */
 
-#include "colony/colony.h"
-#include "colony/renderer.h"
-#include "common/system.h"
-#include "common/events.h"
 #include "common/debug.h"
+#include "common/events.h"
 #include "common/file.h"
+#include "common/system.h"
 #include "graphics/cursorman.h"
-#include <math.h>
+
+#include "colony/colony.h"
+#include "colony/renderer.h"
 
 namespace Colony {
 
diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index d63867520c8..4195e7cfd22 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -30,12 +30,12 @@
 // Original used software 2D projection + wireframe; this uses the existing
 // OpenGL renderer with filled polygons and depth-tested 3D.
 
+#include "common/debug.h"
+#include "common/system.h"
+
 #include "colony/colony.h"
 #include "colony/renderer.h"
 #include "colony/sound.h"
-#include "common/debug.h"
-#include "common/system.h"
-#include <math.h>
 
 namespace Colony {
 
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 52f88d052f3..3a2b31c75d2 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -25,23 +25,23 @@
  *
  */
 
-#include "colony/colony.h"
-#include "colony/debugger.h"
-#include "colony/renderer.h"
 #include "common/config-manager.h"
-#include "common/file.h"
-#include "common/system.h"
-#include "common/util.h"
 #include "common/debug.h"
 #include "common/debug-channels.h"
 #include "common/events.h"
+#include "common/file.h"
 #include "common/keyboard.h"
+#include "common/system.h"
+#include "common/util.h"
 #include "engines/util.h"
 #include "graphics/cursorman.h"
 #include "graphics/maccursor.h"
 #include "graphics/palette.h"
 #include "graphics/paletteman.h"
-#include <math.h>
+
+#include "colony/colony.h"
+#include "colony/debugger.h"
+#include "colony/renderer.h"
 
 namespace Colony {
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index a7ae60c546a..0c09d517649 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -28,19 +28,20 @@
 #ifndef COLONY_COLONY_H
 #define COLONY_COLONY_H
 
-#include "engines/engine.h"
-#include "engines/advancedDetector.h"
 #include "common/array.h"
 #include "common/random.h"
 #include "common/rect.h"
+#include "common/rendermode.h"
 #include "common/stream.h"
+#include "engines/advancedDetector.h"
+#include "engines/engine.h"
+#include "graphics/cursor.h"
 #include "graphics/framelimiter.h"
-#include "common/rendermode.h"
+#include "graphics/macgui/macmenu.h"
+#include "graphics/macgui/macwindowmanager.h"
+
 #include "colony/renderer.h"
 #include "colony/sound.h"
-#include "graphics/cursor.h"
-#include "graphics/macgui/macwindowmanager.h"
-#include "graphics/macgui/macmenu.h"
 
 
 namespace Colony {
diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
index 4102f737b2e..e70a39ad2ed 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/debugger.cpp
@@ -25,8 +25,8 @@
  *
  */
 
-#include "colony/debugger.h"
 #include "colony/colony.h"
+#include "colony/debugger.h"
 
 namespace Colony {
 
diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
index 8ab38d96028..b40ebf00ce3 100644
--- a/engines/colony/detection.cpp
+++ b/engines/colony/detection.cpp
@@ -25,8 +25,8 @@
  *
  */
 
-#include "colony/detection.h"
 #include "colony/colony.h"
+#include "colony/detection.h"
 
 namespace Colony {
 
diff --git a/engines/colony/gfx.cpp b/engines/colony/gfx.cpp
index 6bd2886d2e7..52627e3da9c 100644
--- a/engines/colony/gfx.cpp
+++ b/engines/colony/gfx.cpp
@@ -25,9 +25,10 @@
  *
  */
 
-#include "common/system.h"
 #include "common/config-manager.h"
+#include "common/system.h"
 #include "engines/util.h"
+
 #include "colony/renderer.h"
 
 namespace Colony {
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index eb0a4740bf2..ca543bca7e0 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -25,11 +25,12 @@
  *
  */
 
+#include "common/debug.h"
+#include "common/system.h"
+
 #include "colony/colony.h"
 #include "colony/renderer.h"
 #include "colony/sound.h"
-#include "common/system.h"
-#include "common/debug.h"
 
 namespace Colony {
 
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 105b6b6ea7e..f41418b518c 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -25,21 +25,21 @@
  *
  */
 
-#include "colony/colony.h"
-#include "colony/renderer.h"
-#include "common/system.h"
-#include "common/events.h"
 #include "common/debug.h"
+#include "common/events.h"
+#include "common/system.h"
 #include "common/translation.h"
+#include "graphics/cursorman.h"
 #include "graphics/font.h"
 #include "graphics/fonts/dosfont.h"
 #include "graphics/fonts/macfont.h"
-#include "graphics/cursorman.h"
 #include "graphics/macgui/macdialog.h"
 #include "graphics/macgui/mactext.h"
 #include "gui/message.h"
 #include "image/pict.h"
-#include <math.h>
+
+#include "colony/colony.h"
+#include "colony/renderer.h"
 
 namespace Colony {
 
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index be6629f85ac..2f4e59b1fee 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -25,9 +25,10 @@
  *
  */
 
-#include "colony/colony.h"
-#include "common/file.h"
 #include "common/debug.h"
+#include "common/file.h"
+
+#include "colony/colony.h"
 
 namespace Colony {
 
diff --git a/engines/colony/metaengine.cpp b/engines/colony/metaengine.cpp
index f3b055eb290..3ce7dac15c1 100644
--- a/engines/colony/metaengine.cpp
+++ b/engines/colony/metaengine.cpp
@@ -25,17 +25,18 @@
  *
  */
 
-#include "colony/colony.h"
-#include "colony/detection.h"
-#include "common/system.h"
-#include "common/translation.h"
-#include "common/config-manager.h"
 #include "backends/keymapper/action.h"
 #include "backends/keymapper/keymap.h"
 #include "backends/keymapper/standard-actions.h"
+#include "common/config-manager.h"
+#include "common/system.h"
+#include "common/translation.h"
 #include "graphics/scaler.h"
 #include "graphics/thumbnail.h"
 
+#include "colony/colony.h"
+#include "colony/detection.h"
+
 namespace Colony {
 
 static const ADExtraGuiOptionsMap optionsList[] = {
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index c7601fb2e31..8d64f384da4 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -25,11 +25,11 @@
  *
  */
 
+#include "common/debug.h"
+#include "common/system.h"
+
 #include "colony/colony.h"
 #include "colony/renderer.h"
-#include "common/system.h"
-#include "common/debug.h"
-#include <math.h>
 
 namespace Colony {
 
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index b124bda0531..2063b291844 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -25,14 +25,14 @@
  *
  */
 
+#include "common/algorithm.h"
+#include "common/debug.h"
+#include "common/system.h"
+#include "common/util.h"
+
 #include "colony/colony.h"
 #include "colony/render_internal.h"
 #include "colony/renderer.h"
-#include "common/system.h"
-#include "common/util.h"
-#include "common/algorithm.h"
-#include "common/debug.h"
-#include <math.h>
 
 namespace Colony {
 
diff --git a/engines/colony/render_features.cpp b/engines/colony/render_features.cpp
index 0e3ee6a0284..e8ef8531f8a 100644
--- a/engines/colony/render_features.cpp
+++ b/engines/colony/render_features.cpp
@@ -25,10 +25,10 @@
  *
  */
 
+#include "common/system.h"
+
 #include "colony/colony.h"
 #include "colony/render_internal.h"
-#include "common/system.h"
-#include <math.h>
 
 namespace Colony {
 
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index c9011968932..1e94495f681 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -25,9 +25,10 @@
  *
  */
 
+#include "common/system.h"
+
 #include "colony/colony.h"
 #include "colony/render_internal.h"
-#include "common/system.h"
 
 namespace Colony {
 
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index d0aa0de6511..aaa9c497075 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -25,11 +25,11 @@
  *
  */
 
-#include "common/system.h"
 #include "common/config-manager.h"
-#include "graphics/surface.h"
+#include "common/system.h"
 #include "graphics/font.h"
-#include <math.h>
+#include "graphics/surface.h"
+
 #include "colony/renderer.h"
 
 #ifdef USE_OPENGL_GAME
diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
index ddc6b08914c..f482065b348 100644
--- a/engines/colony/savegame.cpp
+++ b/engines/colony/savegame.cpp
@@ -21,9 +21,10 @@
  *
  */
 
-#include "colony/colony.h"
 #include "common/translation.h"
 
+#include "colony/colony.h"
+
 namespace Colony {
 
 namespace {
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index e9449cd0a94..b2ea6f277fe 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -25,11 +25,12 @@
  *
  */
 
-#include "colony/sound.h"
-#include "colony/colony.h"
-#include "common/stream.h"
 #include "audio/audiostream.h"
 #include "audio/decoders/raw.h"
+#include "common/stream.h"
+
+#include "colony/colony.h"
+#include "colony/sound.h"
 
 namespace Colony {
 
diff --git a/engines/colony/sound.h b/engines/colony/sound.h
index 30980c18961..15519767235 100644
--- a/engines/colony/sound.h
+++ b/engines/colony/sound.h
@@ -28,10 +28,10 @@
 #ifndef COLONY_SOUND_H
 #define COLONY_SOUND_H
 
-#include "audio/softsynth/pcspk.h"
 #include "audio/mixer.h"
-#include "common/ptr.h"
+#include "audio/softsynth/pcspk.h"
 #include "common/macresman.h"
+#include "common/ptr.h"
 
 namespace Colony {
 
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index 04f7a8685d3..8aef1689165 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -25,9 +25,10 @@
  *
  */
 
-#include "colony/colony.h"
 #include "common/debug.h"
 
+#include "colony/colony.h"
+
 namespace Colony {
 
 enum {
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 37ce711ac91..1a79b21e639 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -25,20 +25,20 @@
  *
  */
 
-#include "colony/colony.h"
-#include "colony/renderer.h"
-#include "common/system.h"
-#include "common/util.h"
 #include "common/debug.h"
-#include "common/file.h"
 #include "common/events.h"
-#include "graphics/palette.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/util.h"
 #include "graphics/fontman.h"
 #include "graphics/fonts/dosfont.h"
 #include "graphics/macgui/macfontmanager.h"
 #include "graphics/macgui/macwindowborder.h"
+#include "graphics/palette.h"
 #include "image/pict.h"
-#include <math.h>
+
+#include "colony/colony.h"
+#include "colony/renderer.h"
 
 namespace Colony {
 


Commit: caa05c95d6733a206f44efe32d1128fce33b751a
    https://github.com/scummvm/scummvm/commit/caa05c95d6733a206f44efe32d1128fce33b751a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:56+02:00

Commit Message:
COLONY: noted anonymous namespace in savegame.cpp

Changed paths:
    engines/colony/savegame.cpp


diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
index f482065b348..3de0c4f5f59 100644
--- a/engines/colony/savegame.cpp
+++ b/engines/colony/savegame.cpp
@@ -273,7 +273,7 @@ bool findInvalidActiveObjectSlot(const Common::Array<Thing> &objects, uint32 &in
 	return false;
 }
 
-} // namespace
+} // anonymous namespace
 
 bool ColonyEngine::hasFeature(EngineFeature f) const {
 	return f == kSupportsReturnToLauncher ||


Commit: 8f8436672e885a9f4e95a545df75c7717b18ad16
    https://github.com/scummvm/scummvm/commit/8f8436672e885a9f4e95a545df75c7717b18ad16
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:56+02:00

Commit Message:
COLONY: noted anonymous namespace in colony.cpp

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 3a2b31c75d2..191c3d13a39 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -122,7 +122,7 @@ static Graphics::Cursor *cloneAndScaleCursor(const Graphics::Cursor &src, int sc
 		src.getPaletteStartIndex(), src.getPaletteCount(), scale);
 }
 
-} // namespace
+} // anonymous namespace
 
 ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd), _randomSource("colony") {
 	_level = 0;


Commit: c14e2016fa573c0cfd9cc451b74841663973b8fe
    https://github.com/scummvm/scummvm/commit/c14e2016fa573c0cfd9cc451b74841663973b8fe
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:57+02:00

Commit Message:
COLONY: ifndef COLONY_H

Changed paths:
    engines/colony/colony.h


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 0c09d517649..86956e6ae38 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -25,8 +25,8 @@
  *
  */
 
-#ifndef COLONY_COLONY_H
-#define COLONY_COLONY_H
+#ifndef COLONY_H
+#define COLONY_H
 
 #include "common/array.h"
 #include "common/random.h"
@@ -776,4 +776,4 @@ private:
 
 } // End of namespace Colony
 
-#endif // COLONY_COLONY_H
+#endif // COLONY_H


Commit: 7364024ba09e1faa843389635d6b914c29ad2050
    https://github.com/scummvm/scummvm/commit/7364024ba09e1faa843389635d6b914c29ad2050
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:57+02:00

Commit Message:
COLONY: initialize missing class variables in colony

Changed paths:
    engines/colony/colony.h


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 86956e6ae38..66b388f81a9 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -453,14 +453,14 @@ private:
 	int _robotNum;
 	int _dynamicObjectBase = 0;
 
-	Renderer *_gfx;
-	Sound *_sound;
-	Graphics::FrameLimiter *_frameLimiter;
+	Renderer *_gfx = nullptr;
+	Sound *_sound = nullptr;
+	Graphics::FrameLimiter *_frameLimiter = nullptr;
 	Common::RenderMode _renderMode;
 	Graphics::Surface *_savedScreen = nullptr;
 
 
-	int _tsin, _tcos;
+	int _tsin = 0, _tcos = 0;
 	int _sint[256];
 	int _cost[256];
 	int _centerX, _centerY;
@@ -509,8 +509,8 @@ private:
 	uint32 _lastHotfootTime = 0;  // Time-gate for HOTFOOT damage (~8fps)
 	uint32 _lastAnimUpdate = 0;
 	uint32 _lastWarningChimeTime = 0;
-	int _action0, _action1;
-	int _creature;
+	int _action0 = 0, _action1 = 0;
+	int _creature = 0;
 	bool _allGrow = false;
 	bool _suppressCollisionSound = false;
 
@@ -525,7 +525,7 @@ private:
 	int _mountains[256];          // mountain height profile
 	int _battledx = 0;            // mountain parallax divisor (Width/59)
 	int _battleRound = 0;         // AI round-robin counter
-	Locate *_battlePwh[100];      // visible object pointers (for hit detection)
+	Locate *_battlePwh[100] = {};  // visible object pointers (for hit detection)
 	int _battleMaxP = 0;          // count of visible objects
 	Locate _pyramids[4][4][15];   // pyramid obstacles: 4x4 quadrants, 15 each
 
@@ -543,21 +543,21 @@ private:
 	int _lastLoggedCursorMode = -1;
 
 	// Mac menu bar (MacWindowManager overlay)
-	Graphics::MacWindowManager *_wm;
-	Graphics::MacMenu *_macMenu;
-	Graphics::ManagedSurface *_menuSurface;
-	int _menuBarHeight;
+	Graphics::MacWindowManager *_wm = nullptr;
+	Graphics::MacMenu *_macMenu = nullptr;
+	Graphics::ManagedSurface *_menuSurface = nullptr;
+	int _menuBarHeight = 0;
 	void initMacMenus();
 	void loadMacCursorResources();
 	void handleMenuAction(int action);
 	static void menuCommandsCallback(int action, Common::String &text, void *data);
 
-	int _frntxWall, _frntyWall;
-	int _sidexWall, _sideyWall;
-	int _frntx, _frnty;
-	int _sidex, _sidey;
-	int _front, _side;
-	int _direction;
+	int _frntxWall = 0, _frntyWall = 0;
+	int _sidexWall = 0, _sideyWall = 0;
+	int _frntx = 0, _frnty = 0;
+	int _sidex = 0, _sidey = 0;
+	int _front = 0, _side = 0;
+	int _direction = 0;
 
 	Common::Rect _clip;
 	Common::Rect _screenR;
@@ -567,10 +567,10 @@ private:
 	Common::Rect _powerRect;     // DOS: powerRect; Mac: infoWindow
 
 	// DOS dashboard layout (from original MetaWINDOW pix_per_Qinch values)
-	int _pQx;           // pixels per quarter-inch X (24 for EGA 640x350)
-	int _pQy;           // pixels per quarter-inch Y (18 for EGA 640x350)
-	int _powerWidth;     // width of each of the 3 power bar columns
-	int _powerHeight;    // pixel height per power bar unit (max 5)
+	int _pQx = 0;           // pixels per quarter-inch X (24 for EGA 640x350)
+	int _pQy = 0;           // pixels per quarter-inch Y (18 for EGA 640x350)
+	int _powerWidth = 0;     // width of each of the 3 power bar columns
+	int _powerHeight = 0;    // pixel height per power bar unit (max 5)
 
 	// Cached decoded PICT surfaces for dashboard panels (Mac color mode)
 	Graphics::Surface *_pictPower = nullptr;      // PICT -32755 (normal) or -32760 (trouble)
@@ -582,7 +582,7 @@ private:
 
 	uint8 wallAt(int x, int y) const;
 	const uint8 *mapFeatureAt(int x, int y, int direction) const;
-	bool _visibleCell[32][32];
+	bool _visibleCell[32][32] = {};
 	void computeVisibleCells();
 	void drawStaticObjects();
 
@@ -688,23 +688,23 @@ private:
 	// Animation system
 	Common::Array<Sprite *> _cSprites;
 	Common::Array<ComplexSprite *> _lSprites;
-	Image *_backgroundMask;
-	Image *_backgroundFG;
+	Image *_backgroundMask = nullptr;
+	Image *_backgroundFG = nullptr;
 	Common::Rect _backgroundClip;
 	Common::Rect _backgroundLocate;
 	bool _backgroundActive;
-	Common::MacResManager *_resMan;
-	Common::MacResManager *_colorResMan;
-	byte _topBG[8];
-	byte _bottomBG[8];
+	Common::MacResManager *_resMan = nullptr;
+	Common::MacResManager *_colorResMan = nullptr;
+	byte _topBG[8] = {};
+	byte _bottomBG[8] = {};
 	int16 _divideBG;
 	Common::String _animationName;
 	Common::Array<int16> _animBMColors;
 	bool _animationRunning;
 	int _animationResult;
 	bool _doorOpen;
-	int _liftObject;  // sprite index for the carried object in lift animation
-	bool _liftUp;     // current lift state: true=raised, false=lowered
+	int _liftObject = 0;  // sprite index for the carried object in lift animation
+	bool _liftUp = false;     // current lift state: true=raised, false=lowered
 	int _elevatorFloor;
 	int _airlockX = -1;
 	int _airlockY = -1;


Commit: 6e65a1e6e58d2bcc1c193134534025f8e1fe3817
    https://github.com/scummvm/scummvm/commit/6e65a1e6e58d2bcc1c193134534025f8e1fe3817
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:57+02:00

Commit Message:
COLONY: initialize missing class variables in secondary classes

Changed paths:
    engines/colony/colony.h
    engines/colony/debugger.h
    engines/colony/renderer_opengl.cpp
    engines/colony/sound.h


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 66b388f81a9..b1dc5824b4b 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -279,35 +279,35 @@ static const int kBaseObject = 20;
 static const int kMeNum = 101;
 
 struct Locate {
-	uint8 ang;
-	uint8 look;
-	int8  lookY;
-	int lookx;
-	int delta;
-	int xloc;
-	int yloc;
-	int xindex;
-	int yindex;
-	int xmx, xmn;
-	int zmx, zmn;
-	int32 power[3];
-	int type;
-	int dx, dy;
-	int dist;
-	int wallPad; // 3D bounding radius for wall clamping (0 = use default kWallPad)
+	uint8 ang = 0;
+	uint8 look = 0;
+	int8  lookY = 0;
+	int lookx = 0;
+	int delta = 0;
+	int xloc = 0;
+	int yloc = 0;
+	int xindex = 0;
+	int yindex = 0;
+	int xmx = 0, xmn = 0;
+	int zmx = 0, zmn = 0;
+	int32 power[3] = {};
+	int type = 0;
+	int dx = 0, dy = 0;
+	int dist = 0;
+	int wallPad = 0; // 3D bounding radius for wall clamping (0 = use default kWallPad)
 };
 
 struct Thing {
-	int type;
-	int visible;
-	int alive;
+	int type = 0;
+	int visible = 0;
+	int alive = 0;
 	Common::Rect clip;
-	int count;
+	int count = 0;
 	Locate where;
-	int opcode;
-	int counter;
-	int time;
-	int grow;
+	int opcode = 0;
+	int counter = 0;
+	int time = 0;
+	int grow = 0;
 	// void (*make)(); // To be implemented as virtual functions or member function pointers
 	// void (*think)();
 };
diff --git a/engines/colony/debugger.h b/engines/colony/debugger.h
index 7a7b14bbd63..d7aa9c7b5a4 100644
--- a/engines/colony/debugger.h
+++ b/engines/colony/debugger.h
@@ -39,7 +39,7 @@ public:
 	Debugger(ColonyEngine *vm);
 
 private:
-	ColonyEngine *_vm;
+	ColonyEngine *_vm = nullptr;
 	bool cmdTeleport(int argc, const char **argv);
 	bool cmdPos(int argc, const char **argv);
 	bool cmdInfo(int argc, const char **argv);
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index aaa9c497075..e8a4d56b60c 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -100,17 +100,17 @@ public:
 
 private:
 	void useColor(uint32 color);
-	GLuint _overlayTexId;
-
-	OSystem *_system;
-	int _width;
-	int _height;
-	byte _palette[256 * 3];
-	bool _wireframe;
-	int64_t _wireframeFillColor; // -1 = no fill (outline only)
-	const byte *_stippleData; // GL_POLYGON_STIPPLE pattern (128 bytes), null = disabled
-	uint32 _stippleFgColor;
-	uint32 _stippleBgColor;
+	GLuint _overlayTexId = 0;
+
+	OSystem *_system = nullptr;
+	int _width = 0;
+	int _height = 0;
+	byte _palette[256 * 3] = {};
+	bool _wireframe = true;
+	int64_t _wireframeFillColor = 0; // -1 = no fill (outline only)
+	const byte *_stippleData = nullptr; // GL_POLYGON_STIPPLE pattern (128 bytes), null = disabled
+	uint32 _stippleFgColor = 0;
+	uint32 _stippleBgColor = 0;
 	Common::Rect _screenViewport;
 };
 
diff --git a/engines/colony/sound.h b/engines/colony/sound.h
index 15519767235..93ddcb601ea 100644
--- a/engines/colony/sound.h
+++ b/engines/colony/sound.h
@@ -83,10 +83,10 @@ public:
 	};
 
 private:
-	ColonyEngine *_vm;
-	Audio::PCSpeaker *_speaker;
-	Common::MacResManager *_resMan;
-	Common::MacResManager *_appResMan;
+	ColonyEngine *_vm = nullptr;
+	Audio::PCSpeaker *_speaker = nullptr;
+	Common::MacResManager *_resMan = nullptr;
+	Common::MacResManager *_appResMan = nullptr;
 	Audio::SoundHandle _handle;
 
 	void playPCSpeaker(int soundID);


Commit: 31b6fc2efd52e2e5f7d8a6fea5c729dfdae81b48
    https://github.com/scummvm/scummvm/commit/31b6fc2efd52e2e5f7d8a6fea5c729dfdae81b48
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:58+02:00

Commit Message:
COLONY: removed most of the static keyword usage

Changed paths:
    engines/colony/animation.cpp
    engines/colony/battle.cpp
    engines/colony/colony.cpp
    engines/colony/debugger.cpp
    engines/colony/map.cpp
    engines/colony/movement.cpp
    engines/colony/render.cpp
    engines/colony/render_features.cpp
    engines/colony/render_objects.cpp
    engines/colony/think.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index ed6faf1873a..3f563f5492f 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -36,7 +36,7 @@
 
 namespace Colony {
 
-static void responsiveAnimationDelay(OSystem *system, uint32 delayMs) {
+void responsiveAnimationDelay(OSystem *system, uint32 delayMs) {
 	if (!system || delayMs == 0)
 		return;
 
@@ -70,7 +70,7 @@ static void responsiveAnimationDelay(OSystem *system, uint32 delayMs) {
 	}
 }
 
-static bool isBackdoorCode111111(const uint8 display[6]) {
+bool isBackdoorCode111111(const uint8 display[6]) {
 	for (int i = 0; i < 6; i++) {
 		if (display[i] != 3)
 			return false;
@@ -208,7 +208,7 @@ static const AnimColorEntry kAnimColors[] = {
 };
 
 // Convert Mac Toolbox BackColor constant to ARGB.
-static uint32 macSysColorToARGB(int sysColor) {
+uint32 macSysColorToARGB(int sysColor) {
 	switch (sysColor) {
 	case kMacWhite:   return 0xFFFFFFFF;
 	case kMacBlack:   return 0xFF000000;
@@ -222,12 +222,12 @@ static uint32 macSysColorToARGB(int sysColor) {
 	}
 }
 
-static uint32 packMacColorBG(const uint16 rgb[3]) {
+uint32 packMacColorBG(const uint16 rgb[3]) {
 	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
 		((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
 }
 
-static int getAnimationStateCount(const Common::Array<ComplexSprite *> &sprites, int num) {
+int getAnimationStateCount(const Common::Array<ComplexSprite *> &sprites, int num) {
 	num--;
 	if (num >= 0 && num < (int)sprites.size()) {
 		int count = (int)sprites[num]->objects.size();
diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 4195e7cfd22..5d6e8df0cd8 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -56,7 +56,7 @@ static const int kBLtGray = 1;
 static const int kBBlack  = 4;
 
 // Map battle surface color index to a packed ARGB color.
-static uint32 battleColor(int colorIdx) {
+uint32 battleColor(int colorIdx) {
 	switch (colorIdx) {
 	case 0:  return 0xFFFFFFFF; // WHITE
 	case 1:  return 0xFFC0C0C0; // LTGRAY
@@ -73,7 +73,7 @@ static uint32 battleColor(int colorIdx) {
 	}
 }
 
-static int battleHorizonY(const Common::Rect &screenR, int lookY) {
+int battleHorizonY(const Common::Rect &screenR, int lookY) {
 	const float halfHeight = screenR.height() * 0.5f;
 	const float centerY = screenR.top + halfHeight;
 	const float clampedLookY = CLIP<float>((float)lookY, -63.5f, 63.5f);
@@ -83,7 +83,7 @@ static int battleHorizonY(const Common::Rect &screenR, int lookY) {
 	return (int)roundf(centerY - focalY * tanf(pitchRad));
 }
 
-static int battlePowerLevel(int32 power) {
+int battlePowerLevel(int32 power) {
 	int level = 0;
 	while (power > 0) {
 		power >>= 1;
@@ -92,18 +92,18 @@ static int battlePowerLevel(int32 power) {
 	return level;
 }
 
-static int battleNormalizeCoord(int coord) {
+int battleNormalizeCoord(int coord) {
 	return (int16)coord;
 }
 
-static int wrapBattleCoord(int coord) {
+int wrapBattleCoord(int coord) {
 	coord = battleNormalizeCoord(coord);
 	if (coord < 0)
 		coord += 0x8000;
 	return coord;
 }
 
-static bool battleProjectPoint(const Common::Rect &screenR, uint8 look, int8 lookY, const int *sint,
+bool battleProjectPoint(const Common::Rect &screenR, uint8 look, int8 lookY, const int *sint,
 	const int *cost, int camX, int camY,
 	float worldX, float worldY, float worldZ,
 	int &screenX, int &screenY) {
@@ -135,14 +135,14 @@ static bool battleProjectPoint(const Common::Rect &screenR, uint8 look, int8 loo
 	return true;
 }
 
-static void battleResetBounds(const Common::Rect &screenR, Locate &loc) {
+void battleResetBounds(const Common::Rect &screenR, Locate &loc) {
 	loc.xmn = screenR.right;
 	loc.xmx = screenR.left;
 	loc.zmn = screenR.bottom;
 	loc.zmx = screenR.top;
 }
 
-static bool battleAccumulateBounds(const Common::Rect &screenR, const ColonyEngine::PrismPartDef &def,
+bool battleAccumulateBounds(const Common::Rect &screenR, const ColonyEngine::PrismPartDef &def,
 	Locate &loc, int worldX, int worldY, uint8 ang, int zShift,
 	uint8 look, int8 lookY, const int *sint, const int *cost,
 	int camX, int camY) {
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 191c3d13a39..c1d8c7c2640 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -87,14 +87,14 @@ private:
 	uint16 _paletteCount;
 };
 
-static int getMacCursorScaleFactor(OSystem *system) {
+int getMacCursorScaleFactor(OSystem *system) {
 	if (!system)
 		return 1;
 
 	return MAX(1, (int)floorf(system->getHiDPIScreenFactor() + 0.5f));
 }
 
-static Graphics::Cursor *createScaledCursor(const byte *srcSurface, const byte *srcMask,
+Graphics::Cursor *createScaledCursor(const byte *srcSurface, const byte *srcMask,
 		uint16 srcWidth, uint16 srcHeight, uint16 hotspotX, uint16 hotspotY, byte keyColor,
 		const byte *palette, byte paletteStart, uint16 paletteCount, int scale) {
 	scale = MAX(1, scale);
@@ -116,7 +116,7 @@ static Graphics::Cursor *createScaledCursor(const byte *srcSurface, const byte *
 	return cursor;
 }
 
-static Graphics::Cursor *cloneAndScaleCursor(const Graphics::Cursor &src, int scale) {
+Graphics::Cursor *cloneAndScaleCursor(const Graphics::Cursor &src, int scale) {
 	return createScaledCursor(src.getSurface(), src.getMask(), src.getWidth(), src.getHeight(),
 		src.getHotspotX(), src.getHotspotY(), src.getKeyColor(), src.getPalette(),
 		src.getPaletteStartIndex(), src.getPaletteCount(), scale);
diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
index e70a39ad2ed..32f3d817ccb 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/debugger.cpp
@@ -30,14 +30,14 @@
 
 namespace Colony {
 
-static int wrapBattleDebugCoord(int coord) {
+int wrapBattleDebugCoord(int coord) {
 	coord = (int16)coord;
 	if (coord < 0)
 		coord += 0x8000;
 	return coord;
 }
 
-static const char *robotTypeName(int type) {
+const char *robotTypeName(int type) {
 	switch (type) {
 	case kRobEye: return "Eye";
 	case kRobPyramid: return "Pyramid";
@@ -63,7 +63,7 @@ static const char *robotTypeName(int type) {
 	}
 }
 
-static const char *featureTypeName(int type) {
+const char *featureTypeName(int type) {
 	switch (type) {
 	case kWallFeatureUpStairs: return "UpStairs";
 	case kWallFeatureDnStairs: return "DnStairs";
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 2f4e59b1fee..7b701c245c7 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -61,11 +61,11 @@ static const int kReservedPlayerSlotIndex = kMeNum - 1;
 static const int kStaticObjectStartIndex = kMeNum;
 static const int kMaxObjectSlots = 255;
 
-static void clearThing(Thing &thing) {
+void clearThing(Thing &thing) {
 	thing = Thing();
 }
 
-static void ensureObjectLayout(Common::Array<Thing> &objects) {
+void ensureObjectLayout(Common::Array<Thing> &objects) {
 	const int oldSize = (int)objects.size();
 	if (oldSize >= kMeNum)
 		return;
@@ -75,7 +75,7 @@ static void ensureObjectLayout(Common::Array<Thing> &objects) {
 		clearThing(objects[i]);
 }
 
-static void resetObjectLayout(Common::Array<Thing> &objects) {
+void resetObjectLayout(Common::Array<Thing> &objects) {
 	objects.clear();
 	objects.resize(kMeNum);
 	for (int i = 0; i < kMeNum; i++)
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 8d64f384da4..3ca7d3e31d6 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -62,12 +62,12 @@ static const int kTunnelST[] = {
 
 static const int kTunnelStraight[60] = {0};
 
-static uint32 packTunnelMacColor(const uint16 rgb[3]) {
+uint32 packTunnelMacColor(const uint16 rgb[3]) {
 	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
 		((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
 }
 
-static void fillTunnelPattern(Renderer *gfx, const Common::Rect &rect, uint32 fg, uint32 bg, int pattern) {
+void fillTunnelPattern(Renderer *gfx, const Common::Rect &rect, uint32 fg, uint32 bg, int pattern) {
 	if (rect.isEmpty())
 		return;
 
@@ -115,7 +115,7 @@ static void fillTunnelPattern(Renderer *gfx, const Common::Rect &rect, uint32 fg
 	}
 }
 
-static void projectTunnelPoint(const Common::Rect &rect, int pnt[2], int rox, int roy) {
+void projectTunnelPoint(const Common::Rect &rect, int pnt[2], int rox, int roy) {
 	if (roy <= 0)
 		roy = 1;
 
@@ -139,7 +139,7 @@ enum {
 	kTunnelClipBottom = 8
 };
 
-static int tunnelClipCode(const Common::Rect &rect, int x, int y) {
+int tunnelClipCode(const Common::Rect &rect, int x, int y) {
 	int code = 0;
 
 	if (x < rect.left)
@@ -155,7 +155,7 @@ static int tunnelClipCode(const Common::Rect &rect, int x, int y) {
 	return code;
 }
 
-static void drawTunnelLine(Renderer *gfx, const Common::Rect &rect, int x1, int y1, int x2, int y2, uint32 color) {
+void drawTunnelLine(Renderer *gfx, const Common::Rect &rect, int x1, int y1, int x2, int y2, uint32 color) {
 	if (rect.isEmpty())
 		return;
 
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 2063b291844..e67bd057900 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -144,7 +144,7 @@ static const DOSColorEntry g_dosColors[79] = {
 
 // Look up the DOS lsColor entry for a given ObjColor index.
 // Returns a fallback entry for out-of-range indices.
-static const DOSColorEntry &lookupDOSColor(int colorIdx, int level) {
+const DOSColorEntry &lookupDOSColor(int colorIdx, int level) {
 	// DOS pcycle for animated reactor/suit: WHITE,LTGRAY,GRAY,DKGRAY,BLACK (bounce)
 	static const DOSColorEntry kHCore1 = {0, 0, 15, 15, 15, 1}; // WHITE
 	static const DOSColorEntry kHCore2 = {1, 8,  8, 15,  8, 3}; // LTGRAY
@@ -183,12 +183,12 @@ static const DOSColorEntry &lookupDOSColor(int colorIdx, int level) {
 // Map ObjColor → Mac B&W dither pattern (from ROBOCOLR.C MONOCHROME field).
 // The monochrome field in the DOS table matches MacPattern enum values directly:
 // WHITE=0, LTGRAY=1, GRAY=2, DKGRAY=3, BLACK=4, CLEAR=5.
-static int lookupMacPattern(int colorIdx, int level) {
+int lookupMacPattern(int colorIdx, int level) {
 	return lookupDOSColor(colorIdx, level).monochrome;
 }
 
 // Map ObjColor constant → Mac Color256 index (cColor[] from colordef.h).
-static int mapObjColorToMacColor(int colorIdx, int level) {
+int mapObjColorToMacColor(int colorIdx, int level) {
 	switch (colorIdx) {
 	case kColorBath:      return 97;  // c_tub
 	case kColorWater:     return 102; // c_water
@@ -267,7 +267,7 @@ static int mapObjColorToMacColor(int colorIdx, int level) {
 	}
 }
 
-static void projectCorridorPointClamped(const Common::Rect &screenR, int look, int lookY,
+void projectCorridorPointClamped(const Common::Rect &screenR, int look, int lookY,
 	const int *sint, const int *cost, int camX, int camY,
 	float worldX, float worldY, float worldZ,
 	int &screenX, int &screenY) {
@@ -297,7 +297,7 @@ static void projectCorridorPointClamped(const Common::Rect &screenR, int look, i
 	screenY = (int)roundf(centerY - (eyeY * focal / depth));
 }
 
-static bool isSurfaceVisible(const int *surface, int pointCount, const int *screenX, const int *screenY) {
+bool isSurfaceVisible(const int *surface, int pointCount, const int *screenX, const int *screenY) {
 	if (pointCount < 3)
 		return false;
 
@@ -408,7 +408,7 @@ const uint8 *ColonyEngine::mapFeatureAt(int x, int y, int direction) const {
 	return _mapData[x][y][direction];
 }
 
-static bool projectCorridorPoint(const Common::Rect &screenR, uint8 look, int8 lookY,
+bool projectCorridorPoint(const Common::Rect &screenR, uint8 look, int8 lookY,
 	const int *sint, const int *cost, int camX, int camY,
 	float worldX, float worldY, float worldZ,
 	int &screenX, int &screenY) {
diff --git a/engines/colony/render_features.cpp b/engines/colony/render_features.cpp
index e8ef8531f8a..e23491cd234 100644
--- a/engines/colony/render_features.cpp
+++ b/engines/colony/render_features.cpp
@@ -75,7 +75,7 @@ void ColonyEngine::getWallFace3D(int cellX, int cellY, int direction, float corn
 // Interpolate a point on the wall face.
 // u: 0=left, 1=right (horizontal fraction)
 // v: 0=bottom, 1=top (vertical fraction)
-static void wallPoint(const float corners[4][3], float u, float v, float out[3]) {
+void wallPoint(const float corners[4][3], float u, float v, float out[3]) {
 	float botX = corners[0][0] + (corners[1][0] - corners[0][0]) * u;
 	float botY = corners[0][1] + (corners[1][1] - corners[0][1]) * u;
 	float botZ = corners[0][2] + (corners[1][2] - corners[0][2]) * u;
@@ -109,11 +109,11 @@ void ColonyEngine::wallPolygon(const float corners[4][3], const float *u, const
 	_gfx->draw3DPolygon(px, py, pz, count, color);
 }
 
-static bool nearlyEqual(float a, float b, float eps = 0.0001f) {
+bool nearlyEqual(float a, float b, float eps = 0.0001f) {
 	return fabsf(a - b) <= eps;
 }
 
-static void addSortedUniqueFloat(float *values, int &count, float value, float eps = 0.0001f) {
+void addSortedUniqueFloat(float *values, int &count, float value, float eps = 0.0001f) {
 	for (int i = 0; i < count; ++i) {
 		if (nearlyEqual(values[i], value, eps))
 			return;
@@ -129,7 +129,7 @@ static void addSortedUniqueFloat(float *values, int &count, float value, float e
 	values[count++] = value;
 }
 
-static bool segmentIntersection2D(float ax, float ay, float bx, float by,
+bool segmentIntersection2D(float ax, float ay, float bx, float by,
 		float cx, float cy, float dx, float dy, float &ix, float &iy) {
 	const float den = (ax - bx) * (cy - dy) - (ay - by) * (cx - dx);
 	if (fabsf(den) < 0.0001f)
@@ -145,7 +145,7 @@ static bool segmentIntersection2D(float ax, float ay, float bx, float by,
 	return true;
 }
 
-static float edgeXAtY(float x0, float y0, float x1, float y1, float y) {
+float edgeXAtY(float x0, float y0, float x1, float y1, float y) {
 	if (nearlyEqual(y0, y1))
 		return x0;
 
@@ -159,7 +159,7 @@ struct WallCharSpan {
 	float xBottom;
 };
 
-static void sortWallCharSpans(WallCharSpan *spans, int count) {
+void sortWallCharSpans(WallCharSpan *spans, int count) {
 	for (int i = 1; i < count; ++i) {
 		WallCharSpan span = spans[i];
 		int j = i - 1;
@@ -450,7 +450,7 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 	}
 }
 
-static float stairStepHeight(const float *vf, const float *vc, int d, int s) {
+float stairStepHeight(const float *vf, const float *vc, int d, int s) {
 	return vf[d] + (s + 1) / 8.0f * (vc[d] - vf[d]);
 }
 
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 1e94495f681..3337de5cd01 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -32,11 +32,11 @@
 
 namespace Colony {
 
-static uint32 packEyeOverlayMacColor(const uint16 rgb[3]) {
+uint32 packEyeOverlayMacColor(const uint16 rgb[3]) {
 	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
 }
 
-static int mapEyeOverlayColorToMacColor(int colorIdx, int level) {
+int mapEyeOverlayColorToMacColor(int colorIdx, int level) {
 	switch (colorIdx) {
 	case kColorPupil:        return 36; // c_pupil
 	case kColorEyeball:      return 34; // c_eyeball
@@ -48,7 +48,7 @@ static int mapEyeOverlayColorToMacColor(int colorIdx, int level) {
 	}
 }
 
-static uint8 mapEyeOverlayColorToDOSFill(int colorIdx, int level) {
+uint8 mapEyeOverlayColorToDOSFill(int colorIdx, int level) {
 	switch (colorIdx) {
 	case kColorBlack:
 	case kColorPupil:
@@ -69,7 +69,7 @@ static uint8 mapEyeOverlayColorToDOSFill(int colorIdx, int level) {
 	}
 }
 
-static int mapEyeOverlayColorToMacPattern(int colorIdx) {
+int mapEyeOverlayColorToMacPattern(int colorIdx) {
 	switch (colorIdx) {
 	case kColorBlack:
 	case kColorPupil:
@@ -85,7 +85,7 @@ static int mapEyeOverlayColorToMacPattern(int colorIdx) {
 	}
 }
 
-static bool projectCorridorPointRaw(const Common::Rect &screenR, uint8 look, int8 lookY,
+bool projectCorridorPointRaw(const Common::Rect &screenR, uint8 look, int8 lookY,
 	const int *sint, const int *cost, int camX, int camY,
 	float worldX, float worldY, float worldZ,
 	int &screenX, int &screenY) {
@@ -117,7 +117,7 @@ static bool projectCorridorPointRaw(const Common::Rect &screenR, uint8 look, int
 	return true;
 }
 
-static bool isProjectedSurfaceVisible(const int *surface, int pointCount, const int *screenX, const int *screenY) {
+bool isProjectedSurfaceVisible(const int *surface, int pointCount, const int *screenX, const int *screenY) {
 	if (pointCount < 3)
 		return false;
 
@@ -175,7 +175,7 @@ static bool isProjectedSurfaceVisible(const int *surface, int pointCount, const
 	return false;
 }
 
-static bool isProjectedPrismSurfaceVisible(const Common::Rect &screenR, const Colony::Thing &thing,
+bool isProjectedPrismSurfaceVisible(const Common::Rect &screenR, const Colony::Thing &thing,
 		const Colony::ColonyEngine::PrismPartDef &def, bool useLook, int surfaceIndex,
 		uint8 cameraLook, int8 cameraLookY, int cameraX, int cameraY,
 		const int *sint, const int *cost) {
@@ -1153,12 +1153,12 @@ static const int kSnoopHeadSurf[3][8] = {
 static const Colony::ColonyEngine::PrismPartDef kSnoopAbdomenDef = {4, kSnoopAbdomenPts, 2, kSnoopAbdomenSurf};
 static const Colony::ColonyEngine::PrismPartDef kSnoopHeadDef = {4, kSnoopHeadPts, 3, kSnoopHeadSurf};
 
-static int wrapAngle256(int angle) {
+int wrapAngle256(int angle) {
 	angle %= 256;
 	return (angle < 0) ? (angle + 256) : angle;
 }
 
-static void rotatePoint(int angle, const int src[3], int dst[3], const int *cost, const int *sint) {
+void rotatePoint(int angle, const int src[3], int dst[3], const int *cost, const int *sint) {
 	const long tcos = cost[angle];
 	const long tsin = sint[angle];
 	dst[0] = (int)(((long)src[0] * tcos - (long)src[1] * tsin) >> 7);
@@ -1166,7 +1166,7 @@ static void rotatePoint(int angle, const int src[3], int dst[3], const int *cost
 	dst[2] = src[2];
 }
 
-static void resetObjectBounds(const Common::Rect &screenR, Locate &loc) {
+void resetObjectBounds(const Common::Rect &screenR, Locate &loc) {
 	loc.xmn = screenR.right;
 	loc.xmx = screenR.left;
 	loc.zmn = screenR.bottom;
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index 8aef1689165..bd3d1ce829f 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -39,15 +39,15 @@ enum {
 	kOpcodeSnoop = 20
 };
 
-static bool isBaseRobotType(int type) {
+bool isBaseRobotType(int type) {
 	return type >= kRobEye && type <= kRobUPyramid;
 }
 
-static bool isEggType(int type) {
+bool isEggType(int type) {
 	return type > kRobUPyramid && type < kRobQueen;
 }
 
-static int trailTargetAngle(uint8 code) {
+int trailTargetAngle(uint8 code) {
 	switch (code) {
 	case 2:
 		return 0;
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 1a79b21e639..e97b89e57a7 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -43,16 +43,16 @@
 namespace Colony {
 
 // Pack RGB into 32-bit ARGB with 0xFF000000 marker for direct RGB rendering.
-static uint32 packRGB(byte r, byte g, byte b) {
+uint32 packRGB(byte r, byte g, byte b) {
 	return 0xFF000000 | ((uint32)r << 16) | ((uint32)g << 8) | b;
 }
 
 // Pack Mac 16-bit RGB into 32-bit ARGB.
-static uint32 packMacColorUI(const uint16 rgb[3]) {
+uint32 packMacColorUI(const uint16 rgb[3]) {
 	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
 }
 
-static bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
+bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
 		int screenWidth, int screenHeight, int centerX, int centerY,
 		const Common::Array<Common::String> &lines, Graphics::TextAlign align, bool macColor) {
 	if (!gfx || lines.empty())
@@ -863,7 +863,7 @@ void ColonyEngine::markVisited() {
 	if (canS && canW && cx - 1 >= 0 && cy - 1 >= 0) _visited[lv][cx - 1][cy - 1] = true;
 }
 
-static bool isPassableFeature(int feat) {
+bool isPassableFeature(int feat) {
 	return feat == kWallFeatureDoor || feat == kWallFeatureAirlock ||
 		feat == kWallFeatureUpStairs || feat == kWallFeatureDnStairs ||
 		feat == kWallFeatureTunnel || feat == kWallFeatureElevator;


Commit: 372b3ddbc041abfa68ae0541ee9ac3a81595ff77
    https://github.com/scummvm/scummvm/commit/372b3ddbc041abfa68ae0541ee9ac3a81595ff77
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:58+02:00

Commit Message:
COLONY: reduced static keyword usage

Changed paths:
    engines/colony/animation.cpp
    engines/colony/battle.cpp
    engines/colony/colony.cpp
    engines/colony/debugger.cpp
    engines/colony/detection.cpp
    engines/colony/intro.cpp
    engines/colony/map.cpp
    engines/colony/metaengine.cpp
    engines/colony/movement.cpp
    engines/colony/render.cpp
    engines/colony/render_features.cpp
    engines/colony/render_objects.cpp
    engines/colony/savegame.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 3f563f5492f..56773d8887c 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -104,7 +104,7 @@ enum {
 // BMColor arrays from ganimate.c  per-animation color maps.
 // Index 0 = background top, 1 = background image, 2+ = per-sprite fill.
 // Positive = cColor[] index, negative = -MacSystemColor, 0 = level-based.
-static const int16 kBMC_Desk[] = {
+const int16 kBMC_Desk[] = {
 	0, mc_desktop,
 	-kMacRed, -kMacCyan, -kMacCyan, -kMacCyan, -kMacCyan,
 	-kMacWhite, -kMacWhite, -kMacMagenta, -kMacYellow, mc_desk,
@@ -112,13 +112,13 @@ static const int16 kBMC_Desk[] = {
 	-kMacWhite, mc_screen, -kMacMagenta, -kMacCyan, -kMacCyan,
 	-kMacBlue, -kMacWhite, -kMacRed, -kMacWhite, -kMacYellow
 };
-static const int16 kBMC_Vanity[] = {
+const int16 kBMC_Vanity[] = {
 	0, mc_vanity,
 	mc_mirror, -kMacRed, -kMacCyan, -kMacWhite, -kMacYellow,
 	-kMacGreen, -kMacBlue, -kMacRed, -kMacMagenta, -kMacRed,
 	mc_vanity, -kMacWhite, -kMacYellow, mc_mirror
 };
-static const int16 kBMC_Reactor[] = {
+const int16 kBMC_Reactor[] = {
 	0, mc_console,
 	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
 	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
@@ -127,7 +127,7 @@ static const int16 kBMC_Reactor[] = {
 	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacCyan,
 	-kMacMagenta, -kMacWhite
 };
-static const int16 kBMC_Security[] = {
+const int16 kBMC_Security[] = {
 	0, mc_console,
 	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
 	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
@@ -136,42 +136,42 @@ static const int16 kBMC_Security[] = {
 	-kMacRed, -kMacRed, -kMacRed, -kMacRed, -kMacWhite,
 	-kMacRed, -kMacCyan, -kMacCyan, -kMacCyan, -kMacCyan
 };
-static const int16 kBMC_Teleport[] = {
+const int16 kBMC_Teleport[] = {
 	0, mc_teleport, 0, mc_teledoor
 };
-static const int16 kBMC_Creatures[] = {
+const int16 kBMC_Creatures[] = {
 	-kMacWhite, 0, -kMacWhite, -kMacCyan, mc_proj,
 	-kMacBlue, -kMacMagenta, -kMacMagenta
 };
-static const int16 kBMC_Controls[] = {
+const int16 kBMC_Controls[] = {
 	0, mc_console,
 	-kMacRed, -kMacYellow, -kMacYellow, -kMacBlue, -kMacYellow, -kMacGreen, mc_screen
 };
-static const int16 kBMC_Lift[] = {
+const int16 kBMC_Lift[] = {
 	0, mc_flglass,
 	mc_teleport, mc_box1, mc_cryo, mc_ccore, 0,
 	-kMacRed, -kMacRed, -kMacCyan, -kMacCyan
 };
-static const int16 kBMC_Powersuit[] = {
+const int16 kBMC_Powersuit[] = {
 	0, mc_powerbase,
 	-kMacMagenta, -kMacMagenta, -kMacYellow, -kMacYellow, mc_powerbase, -kMacWhite
 };
-static const int16 kBMC_Forklift[] = {
+const int16 kBMC_Forklift[] = {
 	0, mc_forklift, mc_forklift, mc_forklift
 };
-static const int16 kBMC_Door[] = {
+const int16 kBMC_Door[] = {
 	0, mc_bulkhead, 0, mc_door, -kMacYellow
 };
-static const int16 kBMC_Bulkhead[] = {
+const int16 kBMC_Bulkhead[] = {
 	0, mc_bulkhead, 0, mc_bulkhead, -kMacYellow
 };
-static const int16 kBMC_Airlock[] = {
+const int16 kBMC_Airlock[] = {
 	0, mc_bulkhead, mc_bulkhead, -kMacRed, mc_airlock
 };
-static const int16 kBMC_Elevator[] = {
+const int16 kBMC_Elevator[] = {
 	0, mc_bulkhead, 0, mc_elevator, mc_elevator, -kMacYellow
 };
-static const int16 kBMC_Elevator2[] = {
+const int16 kBMC_Elevator2[] = {
 	0, mc_bulkhead, 0, -kMacMagenta, mc_elevator, mc_elevator,
 	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow
 };
@@ -182,7 +182,7 @@ struct AnimColorEntry {
 	int count;
 };
 
-static const AnimColorEntry kAnimColors[] = {
+const AnimColorEntry kAnimColors[] = {
 	{ "desk",        kBMC_Desk,       ARRAYSIZE(kBMC_Desk) },
 	{ "vanity",      kBMC_Vanity,     ARRAYSIZE(kBMC_Vanity) },
 	{ "reactor",     kBMC_Reactor,    ARRAYSIZE(kBMC_Reactor) },
@@ -257,7 +257,7 @@ bool ColonyEngine::loadAnimation(const Common::String &name) {
 
 	// DOS uses short names (suit.pic, elev.pic, etc.); Mac uses full names
 	// (spacesuit, elevator, etc.) without extensions, in a CData folder.
-	static const struct { const char *dosName; const char *macName; } nameMap[] = {
+	const struct { const char *dosName; const char *macName; } nameMap[] = {
 		{ "suit",   "spacesuit" },
 		{ "elev",   "elevator" },
 		{ "slides", "slideshow" },
diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 5d6e8df0cd8..03965ba8a5f 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -42,18 +42,18 @@ namespace Colony {
 // =====================================================================
 // Constants
 // =====================================================================
-static const int kBattleSize = 150;   // BSIZE: collision/spawn radius
-static const int kMaxQuad = 15;       // pyramids per quadrant
-static const int kTankMax = 24;       // turret pincer animation range
-static const int kFloor = 160;        // ground z-offset
-static const float kBattleFovY = 75.0f;
+const int kBattleSize = 150;   // BSIZE: collision/spawn radius
+const int kMaxQuad = 15;       // pyramids per quadrant
+const int kTankMax = 24;       // turret pincer animation range
+const int kFloor = 160;        // ground z-offset
+const float kBattleFovY = 75.0f;
 
 // =====================================================================
 // Battle color constants (original Mac QuickDraw pattern indices)
 // Values 0-5 are QuickDraw patterns; >=6 are ObjColor enum values.
 // =====================================================================
-static const int kBLtGray = 1;
-static const int kBBlack  = 4;
+const int kBLtGray = 1;
+const int kBBlack  = 4;
 
 // Map battle surface color index to a packed ARGB color.
 uint32 battleColor(int colorIdx) {
@@ -180,25 +180,25 @@ bool battleAccumulateBounds(const Common::Rect &screenR, const ColonyEngine::Pri
 // Original: base at z=0, peak at z=200. Floor subtracted → base -160, peak 40.
 // We store raw vertices (pre-Floor); draw3DBattlePrism applies zShift=-160.
 // =====================================================================
-static const int kRockPts[5][3] = {
+const int kRockPts[5][3] = {
 	{-75,  75, 0},
 	{ 75,  75, 0},
 	{ 75, -75, 0},
 	{-75, -75, 0},
 	{  0,   0, 200}
 };
-static const int kRockSurf[4][8] = {
+const int kRockSurf[4][8] = {
 	{kBLtGray, 3, 1, 0, 4, 0, 0, 0},
 	{kBLtGray, 3, 2, 1, 4, 0, 0, 0},
 	{kBLtGray, 3, 3, 2, 4, 0, 0, 0},
 	{kBLtGray, 3, 0, 3, 4, 0, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kRockDef = {5, kRockPts, 4, kRockSurf};
+const ColonyEngine::PrismPartDef kRockDef = {5, kRockPts, 4, kRockSurf};
 
 // =====================================================================
 // 3D Model Data - Entrance (airlock structure)
 // =====================================================================
-static const int kEntPts[8][3] = {
+const int kEntPts[8][3] = {
 	{-300,  300,   0},
 	{ 300,  300,   0},
 	{ 300, -300,   0},
@@ -208,50 +208,50 @@ static const int kEntPts[8][3] = {
 	{ 250, -250, 600},
 	{-250, -250, 600}
 };
-static const int kEntSurf[4][8] = {
+const int kEntSurf[4][8] = {
 	{kBLtGray, 4, 1, 0, 4, 5, 0, 0},
 	{kBLtGray, 4, 0, 3, 7, 4, 0, 0},
 	{kBLtGray, 4, 3, 2, 6, 7, 0, 0},
 	{kBLtGray, 4, 2, 1, 5, 6, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kEntDef = {8, kEntPts, 4, kEntSurf};
+const ColonyEngine::PrismPartDef kEntDef = {8, kEntPts, 4, kEntSurf};
 
-static const int kEntDoorPts[4][3] = {
+const int kEntDoorPts[4][3] = {
 	{-60,  300,   0},
 	{ 60,  300,   0},
 	{ 50,  275, 200},
 	{-50,  275, 200}
 };
-static const int kEntDoorSurf[1][8] = {
+const int kEntDoorSurf[1][8] = {
 	{kBLtGray, 4, 0, 3, 2, 1, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kEntDoorDef = {4, kEntDoorPts, 1, kEntDoorSurf};
+const ColonyEngine::PrismPartDef kEntDoorDef = {4, kEntDoorPts, 1, kEntDoorSurf};
 
 // =====================================================================
 // 3D Model Data - Shuttle (spaceship)
 // =====================================================================
 // Body (fuselage)
-static const int kSBodyPts[12][3] = {
+const int kSBodyPts[12][3] = {
 	{ 500,  250,   0}, { 500,  350, 200}, { 500,  150, 400},
 	{ 500, -150, 400}, { 500, -350, 200}, { 500, -250,   0},
 	{-500,  250,   0}, {-500,  350, 200}, {-500,  150, 400},
 	{-500, -150, 400}, {-500, -350, 200}, {-500, -250,   0}
 };
-static const int kSBodySurf[4][8] = {
+const int kSBodySurf[4][8] = {
 	{kBLtGray, 4, 0, 6, 7, 1, 0, 0},
 	{kBLtGray, 4, 1, 7, 8, 2, 0, 0},
 	{kBLtGray, 4, 3, 9, 10, 4, 0, 0},
 	{kBLtGray, 4, 4, 10, 11, 5, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kSBodyDef = {12, kSBodyPts, 4, kSBodySurf};
+const ColonyEngine::PrismPartDef kSBodyDef = {12, kSBodyPts, 4, kSBodySurf};
 
 // Front (nose cone)
-static const int kSFrontPts[7][3] = {
+const int kSFrontPts[7][3] = {
 	{ 500,  250,   0}, { 500,  350, 200}, { 500,  150, 400},
 	{ 500, -150, 400}, { 500, -350, 200}, { 500, -250,   0},
 	{ 900,    0,  50}
 };
-static const int kSFrontSurf[6][8] = {
+const int kSFrontSurf[6][8] = {
 	{kBLtGray, 3, 0, 1, 6, 0, 0, 0},
 	{kBLtGray, 3, 1, 2, 6, 0, 0, 0},
 	{kBLtGray, 3, 2, 3, 6, 0, 0, 0},
@@ -259,77 +259,77 @@ static const int kSFrontSurf[6][8] = {
 	{kBLtGray, 3, 4, 5, 6, 0, 0, 0},
 	{kBLtGray, 3, 5, 0, 6, 0, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kSFrontDef = {7, kSFrontPts, 6, kSFrontSurf};
+const ColonyEngine::PrismPartDef kSFrontDef = {7, kSFrontPts, 6, kSFrontSurf};
 
 // Back (engine section)
-static const int kSBackPts[7][3] = {
+const int kSBackPts[7][3] = {
 	{-500,  250,   0}, {-500,  350, 200}, {-500,  150, 400},
 	{-500, -150, 400}, {-500, -350, 200}, {-500, -250,   0},
 	{-900,    0, 400}
 };
-static const int kSBackSurf[5][8] = {
+const int kSBackSurf[5][8] = {
 	{kBLtGray, 3, 0, 6, 1, 0, 0, 0},
 	{kBLtGray, 3, 1, 6, 2, 0, 0, 0},
 	{kBLtGray, 3, 3, 6, 4, 0, 0, 0},
 	{kBLtGray, 3, 4, 6, 5, 0, 0, 0},
 	{kBLtGray, 3, 5, 6, 0, 0, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kSBackDef = {7, kSBackPts, 5, kSBackSurf};
+const ColonyEngine::PrismPartDef kSBackDef = {7, kSBackPts, 5, kSBackSurf};
 
 // Top fin
-static const int kFTopPts[4][3] = {
+const int kFTopPts[4][3] = {
 	{ -500, 0, 400}, {-900, 0, 400}, {-1000, 0, 800}, {-700, 0, 800}
 };
-static const int kFTopSurf[1][8] = {
+const int kFTopSurf[1][8] = {
 	{kBLtGray, 4, 0, 1, 2, 3, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kFTopDef = {4, kFTopPts, 1, kFTopSurf};
+const ColonyEngine::PrismPartDef kFTopDef = {4, kFTopPts, 1, kFTopSurf};
 
 // Left fin
-static const int kFLeftPts[4][3] = {
+const int kFLeftPts[4][3] = {
 	{-100, -350, 200}, {-700, -350, 200}, {-900, -750, 200}, {-500, -750, 200}
 };
-static const int kFLeftSurf[1][8] = {
+const int kFLeftSurf[1][8] = {
 	{kBLtGray, 4, 0, 1, 2, 3, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kFLeftDef = {4, kFLeftPts, 1, kFLeftSurf};
+const ColonyEngine::PrismPartDef kFLeftDef = {4, kFLeftPts, 1, kFLeftSurf};
 
 // Right fin
-static const int kFRightPts[4][3] = {
+const int kFRightPts[4][3] = {
 	{-100, 350, 200}, {-700, 350, 200}, {-900, 750, 200}, {-500, 750, 200}
 };
-static const int kFRightSurf[1][8] = {
+const int kFRightSurf[1][8] = {
 	{kBLtGray, 4, 0, 1, 2, 3, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kFRightDef = {4, kFRightPts, 1, kFRightSurf};
+const ColonyEngine::PrismPartDef kFRightDef = {4, kFRightPts, 1, kFRightSurf};
 
 // Shuttle door
-static const int kSDoorPts[4][3] = {
+const int kSDoorPts[4][3] = {
 	{-50, 262, 25}, {50, 262, 25}, {60, 325, 150}, {-60, 325, 150}
 };
-static const int kSDoorSurf[1][8] = {
+const int kSDoorSurf[1][8] = {
 	{kBLtGray, 4, 0, 3, 2, 1, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kSDoorDef = {4, kSDoorPts, 1, kSDoorSurf};
+const ColonyEngine::PrismPartDef kSDoorDef = {4, kSDoorPts, 1, kSDoorSurf};
 
 // =====================================================================
 // 3D Model Data - Projectile
 // =====================================================================
-static const int kProjPts[5][3] = {
+const int kProjPts[5][3] = {
 	{ 80,   0,  80},
 	{-80,  20,  80},
 	{-80,   0, 100},
 	{-80, -20,  80},
 	{-80,   0,  60}
 };
-static const int kProjSurf[5][8] = {
+const int kProjSurf[5][8] = {
 	{kBBlack,  4, 1, 4, 3, 2, 0, 0},
 	{kBLtGray, 3, 0, 1, 2, 0, 0, 0},
 	{kBLtGray, 3, 0, 2, 3, 0, 0, 0},
 	{kBLtGray, 3, 0, 3, 4, 0, 0, 0},
 	{kBLtGray, 3, 0, 4, 1, 0, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kProjDef = {5, kProjPts, 5, kProjSurf};
+const ColonyEngine::PrismPartDef kProjDef = {5, kProjPts, 5, kProjSurf};
 
 // =====================================================================
 // 3D Model Data - Drone (enemy tank)
@@ -337,55 +337,55 @@ static const ColonyEngine::PrismPartDef kProjDef = {5, kProjPts, 5, kProjSurf};
 // Drawn with zShift=0 (their z values are in absolute world coords).
 // =====================================================================
 // Abdomen (body)
-static const int kBDroneAbdPts[6][3] = {
+const int kBDroneAbdPts[6][3] = {
 	{0, 0, 170}, {120, 0, 130}, {0, 100, 130},
 	{-130, 0, 130}, {0, -100, 130}, {0, 0, 100}
 };
-static const int kBDroneAbdSurf[8][8] = {
+const int kBDroneAbdSurf[8][8] = {
 	{kColorDrone, 3, 0, 1, 2, 0, 0, 0}, {kColorDrone, 3, 0, 2, 3, 0, 0, 0},
 	{kColorDrone, 3, 0, 3, 4, 0, 0, 0}, {kColorDrone, 3, 0, 4, 1, 0, 0, 0},
 	{kColorDrone, 3, 5, 2, 1, 0, 0, 0}, {kColorDrone, 3, 5, 3, 2, 0, 0, 0},
 	{kColorDrone, 3, 5, 4, 3, 0, 0, 0}, {kColorDrone, 3, 5, 1, 4, 0, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kBDroneAbdDef = {6, kBDroneAbdPts, 8, kBDroneAbdSurf};
+const ColonyEngine::PrismPartDef kBDroneAbdDef = {6, kBDroneAbdPts, 8, kBDroneAbdSurf};
 
 // Left pincer base points (for rotation source)
-static const int kLLPincerPts[4][3] = {
+const int kLLPincerPts[4][3] = {
 	{0, 0, 130}, {50, -2, 130}, {35, -20, 140}, {35, -20, 120}
 };
 // Right pincer base points (for rotation source)
-static const int kRRPincerPts[4][3] = {
+const int kRRPincerPts[4][3] = {
 	{0, 0, 130}, {50, 2, 130}, {35, 20, 140}, {35, 20, 120}
 };
 
 // Left pincer surfaces
-static const int kBLPincerSurf[4][8] = {
+const int kBLPincerSurf[4][8] = {
 	{kColorClaw1, 3, 0, 2, 1, 0, 0, 0}, {kColorClaw1, 3, 0, 1, 3, 0, 0, 0},
 	{kColorClaw2, 3, 0, 3, 2, 0, 0, 0}, {kColorClaw2, 3, 1, 2, 3, 0, 0, 0}
 };
 // Right pincer surfaces
-static const int kBRPincerSurf[4][8] = {
+const int kBRPincerSurf[4][8] = {
 	{kColorClaw1, 3, 0, 1, 2, 0, 0, 0}, {kColorClaw1, 3, 0, 3, 1, 0, 0, 0},
 	{kColorClaw2, 3, 0, 2, 3, 0, 0, 0}, {kColorClaw2, 3, 1, 3, 2, 0, 0, 0}
 };
 
 // Left eye
-static const int kBLEyePts[3][3] = {
+const int kBLEyePts[3][3] = {
 	{60, 0, 150}, {60, 50, 130}, {60, 25, 150}
 };
-static const int kBLEyeSurf[2][8] = {
+const int kBLEyeSurf[2][8] = {
 	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kBLEyeDef = {3, kBLEyePts, 2, kBLEyeSurf};
+const ColonyEngine::PrismPartDef kBLEyeDef = {3, kBLEyePts, 2, kBLEyeSurf};
 
 // Right eye
-static const int kBREyePts[3][3] = {
+const int kBREyePts[3][3] = {
 	{60, 0, 150}, {60, -50, 130}, {60, -25, 150}
 };
-static const int kBREyeSurf[2][8] = {
+const int kBREyeSurf[2][8] = {
 	{kColorEyes, 3, 0, 1, 2, 0, 0, 0}, {kColorEyes, 3, 0, 2, 1, 0, 0, 0}
 };
-static const ColonyEngine::PrismPartDef kBREyeDef = {3, kBREyePts, 2, kBREyeSurf};
+const ColonyEngine::PrismPartDef kBREyeDef = {3, kBREyePts, 2, kBREyeSurf};
 
 // =====================================================================
 // draw3DBattlePrism: Render a PrismPartDef at a world position.
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c1d8c7c2640..e6c4d07e39e 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -591,7 +591,7 @@ void ColonyEngine::initMacMenus() {
 	//   index 0 = Apple, 1 = File, 2 = Edit, 3 = Options
 	// NOTE: menunum=0 is the loop terminator, so Apple submenu items
 	// must be added manually after addStaticMenus() (see WAGE pattern).
-	static const Graphics::MacMenuData menuItems[] = {
+	const Graphics::MacMenuData menuItems[] = {
 		{-1, "File",            0, 0, true},
 		{-1, "Edit",            0, 0, true},
 		{-1, "Options",         0, 0, true},
@@ -766,7 +766,7 @@ Common::Error ColonyEngine::run() {
 
 	// Setup a palette with standard 16 colors followed by grayscale
 	byte pal[256 * 3];
-	static const byte egaColors[16][3] = {
+	const byte egaColors[16][3] = {
 		{0, 0, 0}, {0, 0, 170}, {0, 170, 0}, {0, 170, 170},
 		{170, 0, 0}, {170, 0, 170}, {170, 85, 0}, {170, 170, 170},
 		{85, 85, 85}, {85, 85, 255}, {85, 255, 85}, {85, 255, 255},
diff --git a/engines/colony/debugger.cpp b/engines/colony/debugger.cpp
index 32f3d817ccb..5f9cf592397 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/debugger.cpp
@@ -447,7 +447,7 @@ bool Debugger::cmdSpawn(int argc, const char **argv) {
 		return true;
 	}
 
-	static const struct { const char *name; int type; } spawnTypes[] = {
+	const struct { const char *name; int type; } spawnTypes[] = {
 		{"eye",       kRobEye},
 		{"pyramid",   kRobPyramid},
 		{"cube",      kRobCube},
diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
index b40ebf00ce3..a15226a5a47 100644
--- a/engines/colony/detection.cpp
+++ b/engines/colony/detection.cpp
@@ -30,7 +30,7 @@
 
 namespace Colony {
 
-static const DebugChannelDef debugFlagList[] = {
+const DebugChannelDef debugFlagList[] = {
 	{ kColonyDebugMove, "move", "Movement and collision" },
 	{ kColonyDebugRender, "render", "3D rendering and graphics" },
 	{ kColonyDebugAnimation, "animation", "Animation and sprites" },
@@ -41,7 +41,7 @@ static const DebugChannelDef debugFlagList[] = {
 	DEBUG_CHANNEL_END
 };
 
-static const PlainGameDescriptor colonyGames[] = {
+const PlainGameDescriptor colonyGames[] = {
 	{"colony", "The Colony"},
 	{0, 0}
 };
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index f41418b518c..73d13979e96 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -758,9 +758,9 @@ bool ColonyEngine::makeBlackHole() {
 // Phase 3: planet approaches the camera (zoom from dist 800 to 32)
 // All rendering in XOR mode so dots toggle on/off.
 bool ColonyEngine::makePlanet() {
-	static const int PDELTA = 16;
+	const int PDELTA = 16;
 	// Original rtable has 11585 entries; planet uses indices up to 800.
-	static const int RTABLE_SIZE = 801;
+	const int RTABLE_SIZE = 801;
 
 	int rtable[RTABLE_SIZE];
 	rtable[0] = 32000;
@@ -773,7 +773,7 @@ bool ColonyEngine::makePlanet() {
 	const int costheta = _cost[210];
 
 	// Phase 1a: draw background stars
-	static const int STAR_COUNT = 192; // (800-32)/16 * 4 = ~192
+	const int STAR_COUNT = 192; // (800-32)/16 * 4 = ~192
 	int xstars[STAR_COUNT], ystars[STAR_COUNT];
 	int starcnt = 0;
 
@@ -795,7 +795,7 @@ bool ColonyEngine::makePlanet() {
 	// Phase 1b: draw initial planet wireframe at distance 800
 	// Sphere: j=latitude (0..255 step PDELTA), k=longitude (0..127 step PDELTA)
 	// Tilted by sintheta/costheta around X axis (viewing angle)
-	static const int MAX_POINTS = (256 / PDELTA) * (128 / PDELTA); // 16*8 = 128
+	const int MAX_POINTS = (256 / PDELTA) * (128 / PDELTA); // 16*8 = 128
 	int xsave[MAX_POINTS], ysave[MAX_POINTS];
 	bool zsave[MAX_POINTS];
 	int start = 0, dstart = 1;
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 7b701c245c7..67c9ef7f4e8 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -32,7 +32,7 @@
 
 namespace Colony {
 
-static const int kRobotTypeOrder[] = {
+const int kRobotTypeOrder[] = {
 	0,
 	kRobQueen,
 	kRobSnoop,
@@ -56,10 +56,10 @@ static const int kRobotTypeOrder[] = {
 	kRobSCube
 };
 
-static const int kDynamicObjectLimit = kMeNum - 1;
-static const int kReservedPlayerSlotIndex = kMeNum - 1;
-static const int kStaticObjectStartIndex = kMeNum;
-static const int kMaxObjectSlots = 255;
+const int kDynamicObjectLimit = kMeNum - 1;
+const int kReservedPlayerSlotIndex = kMeNum - 1;
+const int kStaticObjectStartIndex = kMeNum;
+const int kMaxObjectSlots = 255;
 
 void clearThing(Thing &thing) {
 	thing = Thing();
diff --git a/engines/colony/metaengine.cpp b/engines/colony/metaengine.cpp
index 3ce7dac15c1..25b2cecdde1 100644
--- a/engines/colony/metaengine.cpp
+++ b/engines/colony/metaengine.cpp
@@ -39,7 +39,7 @@
 
 namespace Colony {
 
-static const ADExtraGuiOptionsMap optionsList[] = {
+const ADExtraGuiOptionsMap optionsList[] = {
 	{
 		GAMEOPTION_WIDESCREEN,
 		{
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 3ca7d3e31d6..4a3245beb59 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -33,7 +33,7 @@
 
 namespace Colony {
 
-static const int kTunnelXT[] = {
+const int kTunnelXT[] = {
 	4, 8, 8, 15, 16, 16, 16, 17, 20, 22,
 	22, 22, 25, 25, 28, 25, 25, 23, 20, 18,
 	18, 16, 14, 14, 13, 12, 10, 9, 7, 3,
@@ -42,7 +42,7 @@ static const int kTunnelXT[] = {
 	0, 0, 0, 0, 0, 0
 };
 
-static const int kTunnelYT[] = {
+const int kTunnelYT[] = {
 	2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
 	7, 8, 9, 10, 11, 12, 11, 9, 7, 6,
 	5, 4, 3, 2, 1, 1, 0, 0, -1, -2,
@@ -51,7 +51,7 @@ static const int kTunnelYT[] = {
 	-7, -4, -2, 0, 0, 0, 0, 0, 0, 0
 };
 
-static const int kTunnelST[] = {
+const int kTunnelST[] = {
 	2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
 	2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
 	2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -60,7 +60,7 @@ static const int kTunnelST[] = {
 	2, 2, 2, 2, 2, 2, 2, 2, 2, 2
 };
 
-static const int kTunnelStraight[60] = {0};
+const int kTunnelStraight[60] = {0};
 
 uint32 packTunnelMacColor(const uint16 rgb[3]) {
 	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
@@ -236,7 +236,7 @@ bool ColonyEngine::hasInteractiveWallFeature(int cx, int cy, int dir) const {
 }
 
 void ColonyEngine::clampToWalls(Locate *p) {
-	static const int kPlayerWallPad = 40;
+	const int kPlayerWallPad = 40;
 	const bool isPlayer = (p == &_me);
 	const int pad = isPlayer ? kPlayerWallPad : p->wallPad;
 	int cx = p->xindex;
@@ -266,7 +266,7 @@ void ColonyEngine::clampToDiagonalWalls(Locate *p) {
 	// CWall/FWall objects are diagonal corner fills not registered in _robotArray.
 	// Enforce geometric collision: the CWall inner face is the line lx+ly=kThreshold
 	// in the object's local coordinate space.  The player must stay on the room side.
-	static const int kThreshold = 120; // inner face ~112 + padding
+	const int kThreshold = 120; // inner face ~112 + padding
 	for (uint i = 0; i < _objects.size(); i++) {
 		const Thing &obj = _objects[i];
 		if (!obj.alive)
@@ -306,7 +306,7 @@ void ColonyEngine::clampToDiagonalWalls(Locate *p) {
 			p->xindex = p->xloc >> 8;
 			p->yindex = p->yloc >> 8;
 		} else { // kObjFWall — flat wall along the diagonal
-			static const int kFWallThreshold = 20;
+			const int kFWallThreshold = 20;
 			if (diag >= kFWallThreshold)
 				continue;
 
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index e67bd057900..5094ab71442 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -36,7 +36,7 @@
 
 namespace Colony {
 
-static const int g_indexTable[4][10] = {
+const int g_indexTable[4][10] = {
 	{0, 0,  0, 0,  0,  1,  1,  0,  1, 2},
 	{1, 0,  0, 0, -1,  0,  0,  1,  2, 1},
 	{0, 1,  1, 0,  0, -1, -1,  0,  1, 2},
@@ -59,7 +59,7 @@ struct DOSColorEntry {
 	uint8 pattern;
 };
 
-static const DOSColorEntry g_dosColors[79] = {
+const DOSColorEntry g_dosColors[79] = {
 	//                     MONO BK  FILL LFIL LINE PAT
 	/* 0  cCLEAR      */ { 5,  0,  0,  0,  0, 1},
 	/* 1  cBLACK      */ { 4,  0,  0,  0,  0, 1},
@@ -715,7 +715,7 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 	float upZ = 1.0f;
 
 	// Create 12-sided polygon
-	static const int N = 12;
+	const int N = 12;
 	float px[N], py[N], pz[N];
 	for (int i = 0; i < N; i++) {
 		float a = (float)i * 2.0f * (float)M_PI / (float)N;
diff --git a/engines/colony/render_features.cpp b/engines/colony/render_features.cpp
index e23491cd234..8534a898db8 100644
--- a/engines/colony/render_features.cpp
+++ b/engines/colony/render_features.cpp
@@ -171,7 +171,7 @@ void sortWallCharSpans(WallCharSpan *spans, int count) {
 	}
 }
 
-static const char *const kWallCharData[] = {
+const char *const kWallCharData[] = {
 	"\00",
 	"\02\10\02\00\03\00\03\01\02\01\10\02\02\03\02\03\06\02\06",
 	"\02\10\01\04\02\04\02\05\01\05\10\03\04\04\04\04\05\03\05",
@@ -510,8 +510,8 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		bool shipLevel = (_level == 1 || _level == 5 || _level == 6);
 
 		if (shipLevel) {
-			static const float uSs[8] = { 0.375f, 0.250f, 0.250f, 0.375f, 0.625f, 0.750f, 0.750f, 0.625f };
-			static const float vSs[8] = { 0.125f, 0.250f, 0.750f, 0.875f, 0.875f, 0.750f, 0.250f, 0.125f };
+			const float uSs[8] = { 0.375f, 0.250f, 0.250f, 0.375f, 0.625f, 0.750f, 0.750f, 0.625f };
+			const float vSs[8] = { 0.125f, 0.250f, 0.750f, 0.875f, 0.875f, 0.750f, 0.250f, 0.125f };
 			const uint32 shipDoorColor = macColors ? (uint32)0xFF000000 : (macMode ? 0 : (_wireframe ? 8u : 0u));
 
 			if (macMode) {
@@ -553,8 +553,8 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 				wallLine(corners, 0.625f, 0.25f, 0.375f, 0.25f, shipDoorColor);
 			}
 		} else {
-			static const float xl = 0.25f, xr = 0.75f;
-			static const float yb = 0.0f, yt = 0.875f;
+			const float xl = 0.25f, xr = 0.75f;
+			const float yb = 0.0f, yt = 0.875f;
 
 			if (macMode) {
 				float ud[4] = {xl, xr, xr, xl};
@@ -828,8 +828,8 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	}
 	case kWallFeatureTunnel: {
 		// Tunnel: hexagonal opening from Grid (0,0 0,5 1,6 5,6 6,5 6,0)
-		static const float uT[6] = { 0.0f,    0.0f,    1/6.0f,  5/6.0f,  1.0f,    1.0f };
-		static const float vT[6] = { 0.0f,    0.750f,  0.875f,  0.875f,  0.750f,  0.0f };
+		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 (macColors) {
 				macFillPoly(uT, vT, 6, 24); // c_tunnel
@@ -846,12 +846,12 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 	case kWallFeatureAirlock: {
 		// Direct port of drawALOpen/drawALClosed from WALLFTRS.C / wallftrs.c.
 		// These are the exact split7x7 positions on the wall face.
-		static const float u[8] = {0.125f, 0.25f, 0.5f, 0.75f, 0.875f, 0.75f, 0.5f, 0.25f};
-		static const float v[8] = {0.5f, 0.75f, 0.875f, 0.75f, 0.5f, 0.25f, 0.125f, 0.25f};
-		static const float spokeU[8] = {0.375f, 0.5f, 0.625f, 0.625f, 0.625f, 0.5f, 0.375f, 0.375f};
-		static const float spokeV[8] = {0.625f, 0.625f, 0.625f, 0.5f, 0.375f, 0.375f, 0.375f, 0.5f};
-		static const float centerU = 0.5f;
-		static const float centerV = 0.5f;
+		const float u[8] = {0.125f, 0.25f, 0.5f, 0.75f, 0.875f, 0.75f, 0.5f, 0.25f};
+		const float v[8] = {0.5f, 0.75f, 0.875f, 0.75f, 0.5f, 0.25f, 0.125f, 0.25f};
+		const float spokeU[8] = {0.375f, 0.5f, 0.625f, 0.625f, 0.625f, 0.5f, 0.375f, 0.375f};
+		const float spokeV[8] = {0.625f, 0.625f, 0.625f, 0.5f, 0.375f, 0.375f, 0.375f, 0.5f};
+		const float centerU = 0.5f;
+		const float centerV = 0.5f;
 
 		if (map[1] == 0) {
 			// Original drawALOpen: solid black opening on both DOS and Mac.
@@ -916,7 +916,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 					}
 				}
 			} else {
-				static const byte *stripPatterns[5] = {
+				const byte *stripPatterns[5] = {
 					nullptr, kStippleLtGray, kStippleGray, kStippleDkGray, nullptr
 				};
 				for (int i = 0; i < 4; i++) {
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 3337de5cd01..4af2da3bee6 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -216,296 +216,296 @@ bool isProjectedPrismSurfaceVisible(const Common::Rect &screenR, const Colony::T
 	return isProjectedSurfaceVisible(surface, pointCount, projectedX, projectedY);
 }
 
-static const int kScreenPts[8][3] = {
+const int kScreenPts[8][3] = {
 	{-16, 64, 0}, {16, 64, 0}, {16, -64, 0}, {-16, -64, 0},
 	{-16, 64, 288}, {16, 64, 288}, {16, -64, 288}, {-16, -64, 288}
 };
-static const int kScreenSurf[4][8] = {
+const int kScreenSurf[4][8] = {
 	{kColorBlack, 4, 0, 3, 7, 4, 0, 0}, {kColorBlack, 4, 3, 2, 6, 7, 0, 0},
 	{kColorBlack, 4, 1, 0, 4, 5, 0, 0}, {kColorBlack, 4, 2, 1, 5, 6, 0, 0}
 };
-static const int kTableTopPts[4][3] = {
+const int kTableTopPts[4][3] = {
 	{-128, 128, 100}, {128, 128, 100}, {128, -128, 100}, {-128, -128, 100}
 };
-static const int kTableTopSurf[1][8] = {{kColorTable, 4, 3, 2, 1, 0, 0, 0}};
-static const int kTableBasePts[8][3] = {
+const int kTableTopSurf[1][8] = {{kColorTable, 4, 3, 2, 1, 0, 0, 0}};
+const int kTableBasePts[8][3] = {
 	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
 	{-5, 5, 100}, {5, 5, 100}, {5, -5, 100}, {-5, -5, 100}
 };
-static const int kTableBaseSurf[4][8] = {
+const int kTableBaseSurf[4][8] = {
 	{kColorTableBase, 4, 0, 3, 7, 4, 0, 0}, {kColorTableBase, 4, 3, 2, 6, 7, 0, 0},
 	{kColorTableBase, 4, 1, 0, 4, 5, 0, 0}, {kColorTableBase, 4, 2, 1, 5, 6, 0, 0}
 };
-static const int kBedPostPts[4][3] = {
+const int kBedPostPts[4][3] = {
 	{-82, 128, 100}, {82, 128, 100}, {82, 128, 0}, {-82, 128, 0}
 };
-static const int kBedPostSurf[1][8] = {{kColorBed, 4, 3, 2, 1, 0, 0, 0}};
-static const int kBedBlanketPts[8][3] = {
+const int kBedPostSurf[1][8] = {{kColorBed, 4, 3, 2, 1, 0, 0, 0}};
+const int kBedBlanketPts[8][3] = {
 	{-80, 70, 0}, {80, 70, 0}, {80, -175, 0}, {-80, -175, 0},
 	{-80, 70, 60}, {80, 70, 60}, {80, -175, 60}, {-80, -175, 60}
 };
-static const int kBlanketSurf[4][8] = {
+const int kBlanketSurf[4][8] = {
 	{kColorBlanket, 4, 0, 3, 7, 4, 0, 0}, {kColorBlanket, 4, 3, 2, 6, 7, 0, 0},
 	{kColorBlanket, 4, 2, 1, 5, 6, 0, 0}, {kColorBlanket, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kBedSheetPts[8][3] = {
+const int kBedSheetPts[8][3] = {
 	{-80, 128, 30}, {80, 128, 30}, {80, 70, 30}, {-80, 70, 30},
 	{-80, 128, 60}, {80, 128, 60}, {80, 70, 60}, {-80, 70, 60}
 };
-static const int kSheetSurf[3][8] = {
+const int kSheetSurf[3][8] = {
 	{kColorSheet, 4, 0, 3, 7, 4, 0, 0}, {kColorSheet, 4, 2, 1, 5, 6, 0, 0},
 	{kColorSheet, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kBBedBlanketPts[8][3] = {
+const int kBBedBlanketPts[8][3] = {
 	{-128, 70, 0}, {128, 70, 0}, {128, -175, 0}, {-128, -175, 0},
 	{-128, 70, 60}, {128, 70, 60}, {128, -175, 60}, {-128, -175, 60}
 };
-static const int kBBedSheetPts[8][3] = {
+const int kBBedSheetPts[8][3] = {
 	{-128, 128, 30}, {128, 128, 30}, {128, 70, 30}, {-128, 70, 30},
 	{-128, 128, 60}, {128, 128, 60}, {128, 70, 60}, {-128, 70, 60}
 };
-static const int kBBedPostPts[4][3] = {
+const int kBBedPostPts[4][3] = {
 	{-130, 128, 100}, {130, 128, 100}, {130, 128, 0}, {-130, 128, 0}
 };
-static const int kDeskTopPts[4][3] = {
+const int kDeskTopPts[4][3] = {
 	{-150, 110, 100}, {150, 110, 100}, {150, -110, 100}, {-150, -110, 100}
 };
-static const int kDeskTopSurf[1][8] = {{kColorDeskTop, 4, 3, 2, 1, 0, 0, 0}};
-static const int kDeskLeftPts[8][3] = {
+const int kDeskTopSurf[1][8] = {{kColorDeskTop, 4, 3, 2, 1, 0, 0, 0}};
+const int kDeskLeftPts[8][3] = {
 	{-135, 95, 0}, {-55, 95, 0}, {-55, -95, 0}, {-135, -95, 0},
 	{-135, 95, 100}, {-55, 95, 100}, {-55, -95, 100}, {-135, -95, 100}
 };
-static const int kDeskRightPts[8][3] = {
+const int kDeskRightPts[8][3] = {
 	{55, 95, 0}, {135, 95, 0}, {135, -95, 0}, {55, -95, 0},
 	{55, 95, 100}, {135, 95, 100}, {135, -95, 100}, {55, -95, 100}
 };
-static const int kDeskCabSurf[4][8] = {
+const int kDeskCabSurf[4][8] = {
 	{kColorDesk, 4, 0, 3, 7, 4, 0, 0}, {kColorDesk, 4, 3, 2, 6, 7, 0, 0},
 	{kColorDesk, 4, 1, 0, 4, 5, 0, 0}, {kColorDesk, 4, 2, 1, 5, 6, 0, 0}
 };
-static const int kSeatPts[4][3] = {
+const int kSeatPts[4][3] = {
 	{-40, 210, 60}, {40, 210, 60}, {40, 115, 60}, {-40, 115, 60}
 };
-static const int kSeatSurf[1][8] = {{kColorDeskChair, 4, 3, 2, 1, 0, 0, 0}};
-static const int kArmLeftPts[4][3] = {
+const int kSeatSurf[1][8] = {{kColorDeskChair, 4, 3, 2, 1, 0, 0, 0}};
+const int kArmLeftPts[4][3] = {
 	{-40, 210, 90}, {-40, 210, 0}, {-40, 115, 0}, {-40, 115, 90}
 };
-static const int kArmRightPts[4][3] = {
+const int kArmRightPts[4][3] = {
 	{40, 210, 90}, {40, 210, 0}, {40, 115, 0}, {40, 115, 90}
 };
-static const int kArmSurf[2][8] = {
+const int kArmSurf[2][8] = {
 	{kColorClear, 4, 3, 2, 1, 0, 0, 0}, {kColorClear, 4, 0, 1, 2, 3, 0, 0}
 };
-static const int kBackPts[4][3] = {
+const int kBackPts[4][3] = {
 	{-40, 210, 130}, {40, 210, 130}, {40, 210, 70}, {-40, 210, 70}
 };
-static const int kBackSurf[2][8] = {
+const int kBackSurf[2][8] = {
 	{kColorDeskChair, 4, 3, 2, 1, 0, 0, 0}, {kColorDeskChair, 4, 0, 1, 2, 3, 0, 0}
 };
-static const int kComputerPts[8][3] = {
+const int kComputerPts[8][3] = {
 	{70, 25, 100}, {120, 25, 100}, {120, -25, 100}, {70, -25, 100},
 	{70, 25, 120}, {120, 25, 120}, {120, -25, 120}, {70, -25, 120}
 };
-static const int kMonitorPts[8][3] = {
+const int kMonitorPts[8][3] = {
 	{75, 20, 120}, {115, 20, 120}, {115, -20, 120}, {75, -20, 120},
 	{75, 20, 155}, {115, 20, 155}, {115, -20, 145}, {75, -20, 145}
 };
-static const int kComputerSurf[5][8] = {
+const int kComputerSurf[5][8] = {
 	{kColorMac, 4, 7, 6, 5, 4, 0, 0}, {kColorMac, 4, 0, 3, 7, 4, 0, 0},
 	{kColorMac, 4, 3, 2, 6, 7, 0, 0}, {kColorMac, 4, 1, 0, 4, 5, 0, 0},
 	{kColorMac, 4, 2, 1, 5, 6, 0, 0}
 };
-static const int kDeskScreenPts[4][3] = {
+const int kDeskScreenPts[4][3] = {
 	{80, 20, 125}, {110, 20, 125}, {110, 20, 150}, {80, 20, 150}
 };
-static const int kDeskScreenSurf[1][8] = {{kColorMacScreen, 4, 3, 2, 1, 0, 0, 0}};
-static const int kCSeatPts[4][3] = {
+const int kDeskScreenSurf[1][8] = {{kColorMacScreen, 4, 3, 2, 1, 0, 0, 0}};
+const int kCSeatPts[4][3] = {
 	{-40, 40, 60}, {40, 40, 60}, {40, -40, 60}, {-40, -40, 60}
 };
-static const int kCSeatSurf[2][8] = {
+const int kCSeatSurf[2][8] = {
 	{kColorChair, 4, 3, 2, 1, 0, 0, 0}, {kColorChair, 4, 0, 1, 2, 3, 0, 0}
 };
-static const int kCArmLeftPts[4][3] = {
+const int kCArmLeftPts[4][3] = {
 	{-50, 40, 90}, {-40, 40, 60}, {-40, -40, 60}, {-50, -40, 90}
 };
-static const int kCArmLeftSurf[2][8] = {
+const int kCArmLeftSurf[2][8] = {
 	{kColorChair, 4, 3, 2, 1, 0, 0, 0}, {kColorChair, 4, 0, 1, 2, 3, 0, 0}
 };
-static const int kCArmRightPts[4][3] = {
+const int kCArmRightPts[4][3] = {
 	{50, 40, 90}, {40, 40, 60}, {40, -40, 60}, {50, -40, 90}
 };
-static const int kCArmRightSurf[2][8] = {
+const int kCArmRightSurf[2][8] = {
 	{kColorChair, 4, 3, 2, 1, 0, 0, 0}, {kColorChair, 4, 0, 1, 2, 3, 0, 0}
 };
-static const int kCBackPts[4][3] = {
+const int kCBackPts[4][3] = {
 	{-20, 60, 150}, {20, 60, 150}, {40, 40, 60}, {-40, 40, 60}
 };
-static const int kCBackSurf[2][8] = {
+const int kCBackSurf[2][8] = {
 	{kColorChair, 4, 3, 2, 1, 0, 0, 0}, {kColorChair, 4, 0, 1, 2, 3, 0, 0}
 };
-static const int kCBasePts[8][3] = {
+const int kCBasePts[8][3] = {
 	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
 	{-5, 5, 60}, {5, 5, 60}, {5, -5, 60}, {-5, -5, 60}
 };
-static const int kCBaseSurf[4][8] = {
+const int kCBaseSurf[4][8] = {
 	{kColorChairBase, 4, 0, 3, 7, 4, 0, 0}, {kColorChairBase, 4, 3, 2, 6, 7, 0, 0},
 	{kColorChairBase, 4, 1, 0, 4, 5, 0, 0}, {kColorChairBase, 4, 2, 1, 5, 6, 0, 0}
 };
-static const int kConsolePts[8][3] = {
+const int kConsolePts[8][3] = {
 	{-5, 5, 0}, {5, 5, 0}, {5, -5, 0}, {-5, -5, 0},
 	{-100, 70, 100}, {-35, 70, 140}, {-35, -70, 140}, {-100, -70, 100}
 };
-static const int kConsoleSurf[5][8] = {
+const int kConsoleSurf[5][8] = {
 	{kColorConsole, 4, 4, 0, 3, 7, 0, 0}, {kColorConsole, 4, 7, 3, 2, 6, 0, 0},
 	{kColorConsole, 4, 5, 1, 0, 4, 0, 0}, {kColorConsole, 4, 6, 2, 1, 5, 0, 0},
 	{kColorBlack, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kCouchSurf[5][8] = {
+const int kCouchSurf[5][8] = {
 	{kColorCouch, 4, 0, 3, 7, 4, 0, 0}, {kColorCouch, 4, 3, 2, 6, 7, 0, 0},
 	{kColorCouch, 4, 1, 0, 4, 5, 0, 0}, {kColorCouch, 4, 2, 1, 5, 6, 0, 0},
 	{kColorCouch, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kACouchPts[8][3] = {
+const int kACouchPts[8][3] = {
 	{-50, 150, 0}, {50, 150, 0}, {50, -150, 0}, {-50, -150, 0},
 	{-50, 150, 50}, {50, 150, 50}, {50, -150, 50}, {-50, -150, 50}
 };
-static const int kBCouchPts[8][3] = {
+const int kBCouchPts[8][3] = {
 	{-80, 150, 0}, {-45, 150, 0}, {-45, -150, 0}, {-80, -150, 0},
 	{-80, 150, 120}, {-55, 150, 120}, {-55, -150, 120}, {-80, -150, 120}
 };
-static const int kCCouchPts[8][3] = {
+const int kCCouchPts[8][3] = {
 	{-70, 170, 0}, {50, 170, 0}, {50, 150, 0}, {-70, 150, 0},
 	{-70, 170, 80}, {50, 170, 80}, {50, 150, 80}, {-70, 150, 80}
 };
-static const int kDCouchPts[8][3] = {
+const int kDCouchPts[8][3] = {
 	{-70, -150, 0}, {50, -150, 0}, {50, -170, 0}, {-70, -170, 0},
 	{-70, -150, 80}, {50, -150, 80}, {50, -170, 80}, {-70, -170, 80}
 };
-static const int kAChairPts[8][3] = {
+const int kAChairPts[8][3] = {
 	{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
 	{-50, 50, 50}, {50, 50, 50}, {50, -50, 50}, {-50, -50, 50}
 };
-static const int kBChairPts[8][3] = {
+const int kBChairPts[8][3] = {
 	{-80, 50, 0}, {-45, 50, 0}, {-45, -50, 0}, {-80, -50, 0},
 	{-80, 50, 120}, {-55, 50, 120}, {-55, -50, 120}, {-80, -50, 120}
 };
-static const int kCChairPts2[8][3] = {
+const int kCChairPts2[8][3] = {
 	{-70, 70, 0}, {50, 70, 0}, {50, 50, 0}, {-70, 50, 0},
 	{-70, 70, 80}, {50, 70, 80}, {50, 50, 80}, {-70, 50, 80}
 };
-static const int kDChairPts[8][3] = {
+const int kDChairPts[8][3] = {
 	{-70, -50, 0}, {50, -50, 0}, {50, -70, 0}, {-70, -70, 0},
 	{-70, -50, 80}, {50, -50, 80}, {50, -70, 80}, {-70, -70, 80}
 };
-static const int kTVBodyPts[8][3] = {
+const int kTVBodyPts[8][3] = {
 	{-30, 60, 0}, {30, 60, 0}, {30, -60, 0}, {-30, -60, 0},
 	{-30, 60, 120}, {30, 60, 120}, {30, -60, 120}, {-30, -60, 120}
 };
-static const int kTVBodySurf[5][8] = {
+const int kTVBodySurf[5][8] = {
 	{kColorTV, 4, 0, 3, 7, 4, 0, 0}, {kColorTV, 4, 3, 2, 6, 7, 0, 0},
 	{kColorTV, 4, 1, 0, 4, 5, 0, 0}, {kColorTV, 4, 2, 1, 5, 6, 0, 0},
 	{kColorTV, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kTVScreenPts[4][3] = {
+const int kTVScreenPts[4][3] = {
 	{30, 50, 10}, {30, -50, 10}, {30, 50, 110}, {30, -50, 110}
 };
-static const int kTVScreenSurf[1][8] = {{kColorTVScreen, 4, 1, 0, 2, 3, 0, 0}};
-static const int kDrawerPts[8][3] = {
+const int kTVScreenSurf[1][8] = {{kColorTVScreen, 4, 1, 0, 2, 3, 0, 0}};
+const int kDrawerPts[8][3] = {
 	{-80, 70, 0}, {0, 70, 0}, {0, -70, 0}, {-80, -70, 0},
 	{-80, 70, 100}, {0, 70, 100}, {0, -70, 100}, {-80, -70, 100}
 };
-static const int kDrawerSurf[5][8] = {
+const int kDrawerSurf[5][8] = {
 	{kColorDrawer, 4, 0, 3, 7, 4, 0, 0}, {kColorDrawer, 4, 3, 2, 6, 7, 0, 0},
 	{kColorDrawer, 4, 1, 0, 4, 5, 0, 0}, {kColorDrawer, 4, 2, 1, 5, 6, 0, 0},
 	{kColorDrawer, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kMirrorPts[4][3] = {
+const int kMirrorPts[4][3] = {
 	{-80, 65, 100}, {-80, -65, 100}, {-80, 65, 210}, {-80, -65, 210}
 };
-static const int kMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
+const int kMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
 
 // Bathtub geometry
-static const int kTubPts[8][3] = {
+const int kTubPts[8][3] = {
 	{-128, 128,  0}, {   0, 128,  0}, {   0,-128,  0}, {-128,-128,  0},
 	{-128, 128, 70}, {   0, 128, 70}, {   0,-128, 70}, {-128,-128, 70}
 };
-static const int kTubSurf[5][8] = {
+const int kTubSurf[5][8] = {
 	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
 	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
 	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kDTubPts[6][3] = {
+const int kDTubPts[6][3] = {
 	{-16, 112, 70}, {-8, 0, 70}, {-16, -112, 70}, {-112, -112, 70}, {-120, 0, 70}, {-112, 112, 70}
 };
-static const int kDTubSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
+const int kDTubSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
 
 // Toilet geometry
-static const int kAToiletPts[8][3] = {
+const int kAToiletPts[8][3] = {
 	{-128, 45, 30}, {-100, 45, 30}, {-100, -45, 30}, {-128, -45, 30},
 	{-128, 45, 100}, {-100, 45, 100}, {-100, -45, 100}, {-128, -45, 100}
 };
-static const int kAToiletSurf[5][8] = {
+const int kAToiletSurf[5][8] = {
 	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
 	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
 	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kBToiletPts[12][3] = {
+const int kBToiletPts[12][3] = {
 	{-100, 20, 50}, {-60, 40, 50}, {-20, 20, 50}, {-20, -20, 50}, {-60, -40, 50}, {-100, -20, 50},
 	{-80, 10,  0}, {-60, 20,  0}, {-40, 10,  0}, {-40, -10,  0}, {-60, -20,  0}, {-80, -10,  0}
 };
-static const int kBToiletSurf[7][8] = {
+const int kBToiletSurf[7][8] = {
 	{kColorBath, 4, 0, 1, 7, 6, 0, 0}, {kColorBath, 4, 1, 2, 8, 7, 0, 0}, {kColorBath, 4, 2, 3, 9, 8, 0, 0},
 	{kColorBath, 4, 3, 4, 10, 9, 0, 0}, {kColorBath, 4, 4, 5, 11, 10, 0, 0}, {kColorBath, 4, 5, 0, 6, 11, 0, 0},
 	{kColorBath, 6, 5, 4, 3, 2, 1, 0}
 };
-static const int kCToiletPts[6][3] = {
+const int kCToiletPts[6][3] = {
 	{-95, 15, 50}, {-60, 35, 50}, {-25, 15, 50}, {-25, -15, 50}, {-60, -35, 50}, {-95, -15, 50}
 };
-static const int kCToiletSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
-static const int kDToiletPts[6][3] = {
+const int kCToiletSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
+const int kDToiletPts[6][3] = {
 	{-100, 20, 50}, {-100, 40, 90}, {-100, 20, 130}, {-100, -20, 130}, {-100, -40, 90}, {-100, -20, 50}
 };
-static const int kDToiletSurf[1][8] = {{kColorLtGreen, 6, 5, 4, 3, 2, 1, 0}};
-static const int kEToiletPts[4][3] = {
+const int kDToiletSurf[1][8] = {{kColorLtGreen, 6, 5, 4, 3, 2, 1, 0}};
+const int kEToiletPts[4][3] = {
 	{-128,-128, 20}, {-128,-128, 200}, { 128,-128, 200}, { 128,-128, 20}
 };
-static const int kEToiletSurf[1][8] = {{kColorDkGray, 4, 0, 1, 2, 3, 0, 0}};
+const int kEToiletSurf[1][8] = {{kColorDkGray, 4, 0, 1, 2, 3, 0, 0}};
 
 // Sink geometry
-static const int kSinkPts[8][3] = {
+const int kSinkPts[8][3] = {
 	{-128, 50, 70}, {-50, 50, 90}, {-50,-50, 90}, {-128,-50, 70},
 	{-128, 50, 110}, {-50, 50, 110}, {-50,-50, 110}, {-128,-50, 110}
 };
-static const int kSinkSurf[5][8] = {
+const int kSinkSurf[5][8] = {
 	{kColorBath, 4, 0, 3, 7, 4, 0, 0}, {kColorBath, 4, 3, 2, 6, 7, 0, 0},
 	{kColorBath, 4, 1, 0, 4, 5, 0, 0}, {kColorBath, 4, 2, 1, 5, 6, 0, 0},
 	{kColorBath, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kDSinkPts[6][3] = {
+const int kDSinkPts[6][3] = {
 	{-55, 0, 110}, {-60, -45, 110}, {-118, -45, 110}, {-123, 0, 110}, {-118, 45, 110}, {-60, 45, 110}
 };
-static const int kDSinkSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
-static const int kSinkMirrorPts[4][3] = {
+const int kDSinkSurf[1][8] = {{kColorWater, 6, 5, 4, 3, 2, 1, 0}};
+const int kSinkMirrorPts[4][3] = {
 	{-128, 65, 130}, {-128, -65, 130}, {-128, 65, 250}, {-128, -65, 250}
 };
-static const int kSinkMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
+const int kSinkMirrorSurf[1][8] = {{kColorSilver, 4, 1, 0, 2, 3, 0, 0}};
 
-static const Colony::ColonyEngine::PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
-static const Colony::ColonyEngine::PrismPartDef kTableParts[2] = {
+const Colony::ColonyEngine::PrismPartDef kScreenPart = {8, kScreenPts, 4, kScreenSurf};
+const Colony::ColonyEngine::PrismPartDef kTableParts[2] = {
 	{4, kTableTopPts, 1, kTableTopSurf},
 	{8, kTableBasePts, 4, kTableBaseSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kBedParts[3] = {
+const Colony::ColonyEngine::PrismPartDef kBedParts[3] = {
 	{4, kBedPostPts, 1, kBedPostSurf},
 	{8, kBedBlanketPts, 4, kBlanketSurf},
 	{8, kBedSheetPts, 3, kSheetSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kBBedParts[3] = {
+const Colony::ColonyEngine::PrismPartDef kBBedParts[3] = {
 	{4, kBBedPostPts, 1, kBedPostSurf},
 	{8, kBBedBlanketPts, 4, kBlanketSurf},
 	{8, kBBedSheetPts, 3, kSheetSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kDeskParts[10] = {
+const Colony::ColonyEngine::PrismPartDef kDeskParts[10] = {
 	{4, kDeskTopPts, 1, kDeskTopSurf},
 	{8, kDeskLeftPts, 4, kDeskCabSurf},
 	{8, kDeskRightPts, 4, kDeskCabSurf},
@@ -517,50 +517,50 @@ static const Colony::ColonyEngine::PrismPartDef kDeskParts[10] = {
 	{8, kMonitorPts, 5, kComputerSurf},
 	{4, kDeskScreenPts, 1, kDeskScreenSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kCChairParts[5] = {
+const Colony::ColonyEngine::PrismPartDef kCChairParts[5] = {
 	{4, kCSeatPts, 2, kCSeatSurf},
 	{4, kCArmLeftPts, 2, kCArmLeftSurf},
 	{4, kCArmRightPts, 2, kCArmRightSurf},
 	{4, kCBackPts, 1, kCBackSurf},
 	{8, kCBasePts, 4, kCBaseSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kConsolePart = {8, kConsolePts, 5, kConsoleSurf};
-static const Colony::ColonyEngine::PrismPartDef kCouchParts[4] = {
+const Colony::ColonyEngine::PrismPartDef kConsolePart = {8, kConsolePts, 5, kConsoleSurf};
+const Colony::ColonyEngine::PrismPartDef kCouchParts[4] = {
 	{8, kACouchPts, 5, kCouchSurf},
 	{8, kBCouchPts, 5, kCouchSurf},
 	{8, kCCouchPts, 5, kCouchSurf},
 	{8, kDCouchPts, 5, kCouchSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kChairParts[4] = {
+const Colony::ColonyEngine::PrismPartDef kChairParts[4] = {
 	{8, kAChairPts, 5, kCouchSurf},
 	{8, kBChairPts, 5, kCouchSurf},
 	{8, kCChairPts2, 5, kCouchSurf},
 	{8, kDChairPts, 5, kCouchSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kTVParts[2] = {
+const Colony::ColonyEngine::PrismPartDef kTVParts[2] = {
 	{8, kTVBodyPts, 5, kTVBodySurf},
 	{4, kTVScreenPts, 1, kTVScreenSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kDrawerParts[2] = {
+const Colony::ColonyEngine::PrismPartDef kDrawerParts[2] = {
 	{8, kDrawerPts, 5, kDrawerSurf},
 	{4, kMirrorPts, 1, kMirrorSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kTubParts[2] = {
+const Colony::ColonyEngine::PrismPartDef kTubParts[2] = {
 	{8, kTubPts, 5, kTubSurf},
 	{6, kDTubPts, 1, kDTubSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kSinkParts[3] = {
+const Colony::ColonyEngine::PrismPartDef kSinkParts[3] = {
 	{8, kSinkPts, 5, kSinkSurf},
 	{6, kDSinkPts, 1, kDSinkSurf},
 	{4, kSinkMirrorPts, 1, kSinkMirrorSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kToiletParts[4] = {
+const Colony::ColonyEngine::PrismPartDef kToiletParts[4] = {
 	{8, kAToiletPts, 5, kAToiletSurf},
 	{12, kBToiletPts, 7, kBToiletSurf},
 	{6, kCToiletPts, 1, kCToiletSurf},
 	{6, kDToiletPts, 1, kDToiletSurf}
 };
-static const Colony::ColonyEngine::PrismPartDef kPToiletParts[5] = {
+const Colony::ColonyEngine::PrismPartDef kPToiletParts[5] = {
 	{8, kAToiletPts, 5, kAToiletSurf},
 	{12, kBToiletPts, 7, kBToiletSurf},
 	{6, kCToiletPts, 1, kCToiletSurf},
@@ -568,44 +568,44 @@ static const Colony::ColonyEngine::PrismPartDef kPToiletParts[5] = {
 	{4, kEToiletPts, 1, kEToiletSurf}
 };
 
-static const int kFWallPts[4][3] = {
+const int kFWallPts[4][3] = {
 	{-128, 128, 0}, {128, -128, 0}, {-128, 128, 320}, {128, -128, 320}
 };
-static const int kFWallSurf[1][8] = {{kColorWall, 4, 2, 3, 1, 0, 0, 0}};
-static const Colony::ColonyEngine::PrismPartDef kFWallPart = {4, kFWallPts, 1, kFWallSurf};
+const int kFWallSurf[1][8] = {{kColorWall, 4, 2, 3, 1, 0, 0, 0}};
+const Colony::ColonyEngine::PrismPartDef kFWallPart = {4, kFWallPts, 1, kFWallSurf};
 
-static const int kCWallPts[8][3] = {
+const int kCWallPts[8][3] = {
 	{-128, 128, 0}, {0, 112, 0}, {112, 0, 0}, {128, -128, 0},
 	{-128, 128, 320},  {0, 112, 320},  {112, 0, 320},  {128, -128, 320}
 };
-static const int kCWallSurf[3][8] = {
+const int kCWallSurf[3][8] = {
 	{kColorWall, 4, 1, 0, 4, 5, 0, 0}, {kColorWall, 4, 2, 1, 5, 6, 0, 0}, {kColorWall, 4, 3, 2, 6, 7, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kCWallPart = {8, kCWallPts, 3, kCWallSurf};
+const Colony::ColonyEngine::PrismPartDef kCWallPart = {8, kCWallPts, 3, kCWallSurf};
 
-static const int kPlantPotPts[12][3] = {
+const int kPlantPotPts[12][3] = {
 	{10, 17, 40}, {20, 0, 40}, {10, -17, 40}, {-10, -17, 40}, {-20, 0, 40}, {-10, 17, 40},
 	{8, 12, 0}, {15, 0, 0}, {8, -12, 0}, {-8, -12, 0}, {-15, 0, 0}, {-8, 12, 0}
 };
-static const int kPlantPotSurf[6][8] = {
+const int kPlantPotSurf[6][8] = {
 	{kColorPot, 4, 0, 1, 7, 6, 0, 0}, {kColorPot, 4, 1, 2, 8, 7, 0, 0}, {kColorPot, 4, 2, 3, 9, 8, 0, 0},
 	{kColorPot, 4, 3, 4, 10, 9, 0, 0}, {kColorPot, 4, 4, 5, 11, 10, 0, 0}, {kColorPot, 4, 5, 0, 6, 11, 0, 0}
 };
-static const int kPlantTopPotPts[6][3] = {
+const int kPlantTopPotPts[6][3] = {
 	{10, 17, 40}, {20, 0, 40}, {10, -17, 40}, {-10, -17, 40}, {-20, 0, 40}, {-10, 17, 40}
 };
-static const int kPlantTopPotSurf[1][8] = {{kColorDkGray, 6, 5, 4, 3, 2, 1, 0}};
+const int kPlantTopPotSurf[1][8] = {{kColorDkGray, 6, 5, 4, 3, 2, 1, 0}};
 
-static const int kPlantLeaf0Pts[3][3] = {{0, 0, 0}, {20, 20, 150}, {70, 70, 100}};
-static const int kPlantLeaf1Pts[3][3] = {{0, 0, 0}, {-20, 30, 100}, {-60, 50, 50}};
-static const int kPlantLeaf2Pts[3][3] = {{0, 0, 0}, {-20, -10, 70}, {-90, -70, 50}};
-static const int kPlantLeaf3Pts[3][3] = {{0, 0, 0}, {20, -10, 50}, {90, -70, 70}};
-static const int kPlantLeaf4Pts[3][3] = {{0, 0, 0}, {20, -30, 190}, {40, -60, 150}};
-static const int kPlantLeaf5Pts[3][3] = {{0, 0, 0}, {20, -10, 130}, {50, -80, 200}};
+const int kPlantLeaf0Pts[3][3] = {{0, 0, 0}, {20, 20, 150}, {70, 70, 100}};
+const int kPlantLeaf1Pts[3][3] = {{0, 0, 0}, {-20, 30, 100}, {-60, 50, 50}};
+const int kPlantLeaf2Pts[3][3] = {{0, 0, 0}, {-20, -10, 70}, {-90, -70, 50}};
+const int kPlantLeaf3Pts[3][3] = {{0, 0, 0}, {20, -10, 50}, {90, -70, 70}};
+const int kPlantLeaf4Pts[3][3] = {{0, 0, 0}, {20, -30, 190}, {40, -60, 150}};
+const int kPlantLeaf5Pts[3][3] = {{0, 0, 0}, {20, -10, 130}, {50, -80, 200}};
 
-static const int kPlantLeafSurf[2][8] = {{kColorClear, 3, 0, 1, 2, 0, 0, 0}, {kColorClear, 3, 2, 1, 0, 0, 0, 0}};
+const int kPlantLeafSurf[2][8] = {{kColorClear, 3, 0, 1, 2, 0, 0, 0}, {kColorClear, 3, 2, 1, 0, 0, 0, 0}};
 
-static const Colony::ColonyEngine::PrismPartDef kPlantParts[8] = {
+const Colony::ColonyEngine::PrismPartDef kPlantParts[8] = {
 	{12, kPlantPotPts, 6, kPlantPotSurf},
 	{6, kPlantTopPotPts, 1, kPlantTopPotSurf},
 	{3, kPlantLeaf0Pts, 2, kPlantLeafSurf},
@@ -616,106 +616,106 @@ static const Colony::ColonyEngine::PrismPartDef kPlantParts[8] = {
 	{3, kPlantLeaf5Pts, 2, kPlantLeafSurf}
 };
 
-static const int kBox1Pts[8][3] = {
+const int kBox1Pts[8][3] = {
 	{-50, 50, 0}, {50, 50, 0}, {50, -50, 0}, {-50, -50, 0},
 	{-50, 50, 100}, {50, 50, 100}, {50, -50, 100}, {-50, -50, 100}
 };
-static const int kBox1Surf[5][8] = {
+const int kBox1Surf[5][8] = {
 	{kColorBox, 4, 0, 3, 7, 4, 0, 0}, {kColorBox, 4, 3, 2, 6, 7, 0, 0},
 	{kColorBox, 4, 1, 0, 4, 5, 0, 0}, {kColorBox, 4, 2, 1, 5, 6, 0, 0}, {kColorBox, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kBox2Pts[8][3] = {
+const int kBox2Pts[8][3] = {
 	{-50, 50, 100}, {50, 50, 100}, {50, -50, 100}, {-50, -50, 100},
 	{-50, 50, 200}, {50, 50, 200}, {50, -50, 200}, {-50, -50, 200}
 };
 
-static const int kReactorCorePts[12][3] = {
+const int kReactorCorePts[12][3] = {
 	{-40, 20, 288}, {0, 40, 288}, {40, 20, 288}, {40, -20, 288}, {0, -40, 288}, {-40, -20, 288},
 	{-40, 20, 32}, {0, 40, 32}, {40, 20, 32}, {40, -20, 32}, {0, -40, 32}, {-40, -20, 32}
 };
-static const int kReactorCoreSurf[7][8] = {
+const int kReactorCoreSurf[7][8] = {
 	{kColorCCore, 4, 0, 1, 7, 6, 0, 0}, {kColorCCore, 4, 1, 2, 8, 7, 0, 0}, {kColorCCore, 4, 2, 3, 9, 8, 0, 0},
 	{kColorCCore, 4, 3, 4, 10, 9, 0, 0}, {kColorCCore, 4, 4, 5, 11, 10, 0, 0}, {kColorCCore, 4, 5, 0, 6, 11, 0, 0},
 	{kColorCCore, 6, 5, 4, 3, 2, 1, 0}
 };
-static const int kReactorBasePts[8][3] = {
+const int kReactorBasePts[8][3] = {
 	{-128, 128, 0}, {128, 128, 0}, {128, -128, 0}, {-128, -128, 0},
 	{-128, 128, 32}, {128, 128, 32}, {128, -128, 32}, {-128, -128, 32}
 };
-static const int kReactorBaseSurf[6][8] = {
+const int kReactorBaseSurf[6][8] = {
 	{kColorReactor, 4, 0, 3, 7, 4, 0, 0}, {kColorReactor, 4, 3, 2, 6, 7, 0, 0}, {kColorReactor, 4, 1, 0, 4, 5, 0, 0},
 	{kColorReactor, 4, 2, 1, 5, 6, 0, 0}, {kColorReactor, 4, 7, 6, 5, 4, 0, 0}, {kColorReactor, 4, 0, 1, 2, 3, 0, 0}
 };
-static const int kReactorTopPts[8][3] = {
+const int kReactorTopPts[8][3] = {
 	{-128, 128, 288}, {128, 128, 288}, {128, -128, 288}, {-128, -128, 288},
 	{-128, 128, 320}, {128, 128, 320}, {128, -128, 320}, {-128, -128, 320}
 };
 
-static const Colony::ColonyEngine::PrismPartDef kBox1Part = {8, kBox1Pts, 5, kBox1Surf};
-static const Colony::ColonyEngine::PrismPartDef kBox2Parts[2] = {
+const Colony::ColonyEngine::PrismPartDef kBox1Part = {8, kBox1Pts, 5, kBox1Surf};
+const Colony::ColonyEngine::PrismPartDef kBox2Parts[2] = {
 	{8, kBox2Pts, 4, kBox1Surf}, // Body (stacked on box1)
 	{8, kBox1Pts, 5, kBox1Surf}  // Lid (same geometry as box1 base)
 };
 // Bench: simple box (1 part). DOS INITOBJ.C InitBench.
-static const int kBenchPts[8][3] = {
+const int kBenchPts[8][3] = {
 	{-60, 128, 0}, {60, 128, 0}, {60, -128, 0}, {-60, -128, 0},
 	{-60, 128, 120}, {60, 128, 120}, {60, -128, 120}, {-60, -128, 120}
 };
-static const int kBenchSurf[5][8] = {
+const int kBenchSurf[5][8] = {
 	{kColorBench, 4, 0, 3, 7, 4, 0, 0}, {kColorBench, 4, 3, 2, 6, 7, 0, 0},
 	{kColorBench, 4, 1, 0, 4, 5, 0, 0}, {kColorBench, 4, 2, 1, 5, 6, 0, 0},
 	{kColorBlack, 4, 7, 6, 5, 4, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kBenchPart = {8, kBenchPts, 5, kBenchSurf};
+const Colony::ColonyEngine::PrismPartDef kBenchPart = {8, kBenchPts, 5, kBenchSurf};
 
 // CBench: L-shaped corner bench (2 parts). DOS INITOBJ.C InitCBench.
 // Part 0: slanted front face (front-left Y=60, front-right Y=-60)
-static const int kCBenchPts[8][3] = {
+const int kCBenchPts[8][3] = {
 	{-60, 60, 0}, {60, -60, 0}, {60, -128, 0}, {-60, -128, 0},
 	{-60, 60, 120}, {60, -60, 120}, {60, -128, 120}, {-60, -128, 120}
 };
 // Part 1: wider perpendicular section
-static const int kDBenchPts[8][3] = {
+const int kDBenchPts[8][3] = {
 	{-60, 60, 0}, {128, 60, 0}, {128, -60, 0}, {60, -60, 0},
 	{-60, 60, 120}, {128, 60, 120}, {128, -60, 120}, {60, -60, 120}
 };
-static const Colony::ColonyEngine::PrismPartDef kCBenchParts[2] = {
+const Colony::ColonyEngine::PrismPartDef kCBenchParts[2] = {
 	{8, kCBenchPts, 5, kBenchSurf},
 	{8, kDBenchPts, 5, kBenchSurf}
 };
 
 // Power Suit: triangular prism body + small rectangular pedestal + flat table + hexagonal power source
 // DOS INITOBJ.C: 5 prism parts. Floor=160, so 2*Floor=320.
-static const int kPowerTopPts[3][3] = {{-150, 120, 320}, {150, 120, 320}, {0, -150, 320}};
-static const int kPowerTopSurf[1][8] = {{kColorPower, 3, 0, 1, 2, 0, 0, 0}};
+const int kPowerTopPts[3][3] = {{-150, 120, 320}, {150, 120, 320}, {0, -150, 320}};
+const int kPowerTopSurf[1][8] = {{kColorPower, 3, 0, 1, 2, 0, 0, 0}};
 
-static const int kPowerBottomPts[3][3] = {{-150, 120, 0}, {150, 120, 0}, {0, -150, 0}};
-static const int kPowerBottomSurf[1][8] = {{kColorPower, 3, 2, 1, 0, 0, 0, 0}};
+const int kPowerBottomPts[3][3] = {{-150, 120, 0}, {150, 120, 0}, {0, -150, 0}};
+const int kPowerBottomSurf[1][8] = {{kColorPower, 3, 2, 1, 0, 0, 0, 0}};
 
-static const int kPowerBasePts[8][3] = {
+const int kPowerBasePts[8][3] = {
 	{-5, 100, 0}, {5, 100, 0}, {5, 90, 0}, {-5, 90, 0},
 	{-5, 100, 100}, {5, 100, 100}, {5, 90, 100}, {-5, 90, 100}
 };
-static const int kPowerBaseSurf[4][8] = {
+const int kPowerBaseSurf[4][8] = {
 	{kColorPBase, 4, 0, 3, 7, 4, 0, 0}, {kColorPBase, 4, 3, 2, 6, 7, 0, 0},
 	{kColorPBase, 4, 1, 0, 4, 5, 0, 0}, {kColorPBase, 4, 2, 1, 5, 6, 0, 0}
 };
 
-static const int kPowerTablePts[4][3] = {{-50, 135, 100}, {50, 135, 100}, {50, 55, 100}, {-50, 55, 100}};
-static const int kPowerTableSurf[1][8] = {{kColorBlack, 4, 3, 2, 1, 0, 0, 0}};
+const int kPowerTablePts[4][3] = {{-50, 135, 100}, {50, 135, 100}, {50, 55, 100}, {-50, 55, 100}};
+const int kPowerTableSurf[1][8] = {{kColorBlack, 4, 3, 2, 1, 0, 0, 0}};
 
-static const int kPowerSourcePts[12][3] = {
+const int kPowerSourcePts[12][3] = {
 	{-75, 0, 290}, {-35, 60, 290}, {35, 60, 290}, {75, 0, 290}, {35, -60, 290}, {-35, -60, 290},
 	{-75, 0, 320}, {-35, 60, 320}, {35, 60, 320}, {75, 0, 320}, {35, -60, 320}, {-35, -60, 320}
 };
-static const int kPowerSourceSurf[7][8] = {
+const int kPowerSourceSurf[7][8] = {
 	{kColorRainbow1, 6, 0, 1, 2, 3, 4, 5},
 	{kColorPSource, 4, 0, 6, 7, 1, 0, 0}, {kColorPSource, 4, 1, 7, 8, 2, 0, 0},
 	{kColorPSource, 4, 2, 8, 9, 3, 0, 0}, {kColorPSource, 4, 3, 9, 10, 4, 0, 0},
 	{kColorPSource, 4, 4, 10, 11, 5, 0, 0}, {kColorPSource, 4, 5, 11, 6, 0, 0, 0}
 };
 
-static const Colony::ColonyEngine::PrismPartDef kPowerSuitParts[5] = {
+const Colony::ColonyEngine::PrismPartDef kPowerSuitParts[5] = {
 	{3, kPowerTopPts, 1, kPowerTopSurf},
 	{3, kPowerBottomPts, 1, kPowerBottomSurf},
 	{8, kPowerBasePts, 4, kPowerBaseSurf},
@@ -724,14 +724,14 @@ static const Colony::ColonyEngine::PrismPartDef kPowerSuitParts[5] = {
 };
 
 // Cryo tube: top (coffin-shaped lid) + base
-static const int kCryoTopPts[16][3] = {
+const int kCryoTopPts[16][3] = {
 	{-130,  50,  80}, { 130,  50,  80}, { 130, -50,  80}, {-130, -50,  80},
 	{-130,  50, 140}, { 130,  50, 140}, { 130, -50, 140}, {-130, -50, 140},
 	{   0,  50, 140}, {   0, -50, 140},
 	{-140,  70, 110}, { 140,  70, 110}, { 140, -70, 110}, {-140, -70, 110},
 	{   0,  70, 110}, {   0, -70, 110}
 };
-static const int kCryoTopSurf[12][8] = {
+const int kCryoTopSurf[12][8] = {
 	{kColorCryo,      4, 7, 9, 8, 4, 0, 0},
 	{kColorCryoGlass, 4, 9, 6, 5, 8, 0, 0},
 	{kColorCryo,      4, 0, 10, 11, 1, 0, 0},
@@ -745,29 +745,29 @@ static const int kCryoTopSurf[12][8] = {
 	{kColorSilver,    4, 6, 12, 11, 5, 0, 0},
 	{kColorSilver,    4, 9, 15, 12, 6, 0, 0}
 };
-static const int kCryoBasePts[8][3] = {
+const int kCryoBasePts[8][3] = {
 	{-130,  50,  0}, { 130,  50,  0}, { 130, -50,  0}, {-130, -50,  0},
 	{-130,  50, 80}, { 130,  50, 80}, { 130, -50, 80}, {-130, -50, 80}
 };
-static const int kCryoBaseSurf[5][8] = {
+const int kCryoBaseSurf[5][8] = {
 	{kColorCryoBase, 4, 0, 3, 7, 4, 0, 0}, {kColorCryoBase, 4, 3, 2, 6, 7, 0, 0},
 	{kColorCryoBase, 4, 1, 0, 4, 5, 0, 0}, {kColorCryoBase, 4, 2, 1, 5, 6, 0, 0},
 	{kColorCryo,     4, 7, 6, 5, 4, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kCryoParts[2] = {
+const Colony::ColonyEngine::PrismPartDef kCryoParts[2] = {
 	{16, kCryoTopPts, 12, kCryoTopSurf},
 	{8, kCryoBasePts, 5, kCryoBaseSurf}
 };
 
 // Forklift: cab + treads + upper-left arm + lower-left fork + upper-right arm + lower-right fork
-static const int kFLCabPts[14][3] = {
+const int kFLCabPts[14][3] = {
 	{-35, 60, 40}, {35, 60, 40}, {35, -60, 40}, {-35, -60, 40},
 	{-15, 60, 260}, {15, 60, 260}, {15, -60, 260}, {-15, -60, 260},
 	{25, 60, 140}, {25, -60, 140},
 	{70, 35, 120}, {70, -35, 120},
 	{-70, 40, 80}, {-70, -40, 80}
 };
-static const int kFLCabSurf[12][8] = {
+const int kFLCabSurf[12][8] = {
 	{kColorForklift, 4, 0, 3, 13, 12, 0, 0}, {kColorForklift, 4, 12, 13, 7, 4, 0, 0},
 	{kColorForklift, 3, 0, 12, 4, 0, 0, 0},  {kColorForklift, 3, 3, 7, 13, 0, 0, 0},
 	{kColorForklift, 4, 3, 2, 6, 7, 0, 0},   {kColorForklift, 4, 1, 0, 4, 5, 0, 0},
@@ -776,12 +776,12 @@ static const int kFLCabSurf[12][8] = {
 	{kColorSilver,   3, 8, 5, 10, 0, 0, 0},  {kColorSilver, 3, 11, 6, 9, 0, 0, 0},
 	{kColorSilver,   4, 10, 5, 6, 11, 0, 0}
 };
-static const int kFLTreadPts[12][3] = {
+const int kFLTreadPts[12][3] = {
 	{-60, 60, 20}, {60, 60, 20}, {60, -60, 20}, {-60, -60, 20},
 	{-35, 60, 40}, {35, 60, 40}, {35, -60, 40}, {-35, -60, 40},
 	{-35, 60, 0}, {35, 60, 0}, {35, -60, 0}, {-35, -60, 0}
 };
-static const int kFLTreadSurf[6][8] = {
+const int kFLTreadSurf[6][8] = {
 	{kColorTread1, 4, 0, 3, 7, 4, 0, 0},
 	{kColorTread2, 6, 3, 11, 10, 2, 6, 7},
 	{kColorTread2, 6, 0, 4, 5, 1, 9, 8},
@@ -789,32 +789,32 @@ static const int kFLTreadSurf[6][8] = {
 	{kColorTread1, 4, 0, 8, 11, 3, 0, 0},
 	{kColorTread1, 4, 10, 9, 1, 2, 0, 0}
 };
-static const int kFLULPts[8][3] = {
+const int kFLULPts[8][3] = {
 	{-15, 70, 120}, {15, 70, 120}, {15, 60, 120}, {-15, 60, 120},
 	{-25, 70, 230}, {25, 70, 230}, {25, 60, 230}, {-25, 60, 230}
 };
-static const int kFLArmSurf[4][8] = {
+const int kFLArmSurf[4][8] = {
 	{kColorForklift, 4, 0, 3, 7, 4, 0, 0}, {kColorForklift, 4, 3, 2, 6, 7, 0, 0},
 	{kColorForklift, 4, 1, 0, 4, 5, 0, 0}, {kColorForklift, 4, 2, 1, 5, 6, 0, 0}
 };
-static const int kFLLLPts[8][3] = {
+const int kFLLLPts[8][3] = {
 	{-15, 80, 120}, {100, 80, 125}, {100, 70, 125}, {-15, 70, 120},
 	{-15, 80, 150}, {100, 80, 140}, {100, 70, 140}, {-15, 70, 150}
 };
-static const int kFLForkSurf[6][8] = {
+const int kFLForkSurf[6][8] = {
 	{kColorForklift, 4, 0, 3, 7, 4, 0, 0}, {kColorForklift, 4, 3, 2, 6, 7, 0, 0},
 	{kColorForklift, 4, 1, 0, 4, 5, 0, 0}, {kColorBlack,    4, 2, 1, 5, 6, 0, 0},
 	{kColorForklift, 4, 7, 6, 5, 4, 0, 0}, {kColorForklift, 4, 0, 1, 2, 3, 0, 0}
 };
-static const int kFLURPts[8][3] = {
+const int kFLURPts[8][3] = {
 	{-15, -60, 120}, {15, -60, 120}, {15, -70, 120}, {-15, -70, 120},
 	{-25, -60, 230}, {25, -60, 230}, {25, -70, 230}, {-25, -70, 230}
 };
-static const int kFLLRPts[8][3] = {
+const int kFLLRPts[8][3] = {
 	{-15, -70, 120}, {100, -70, 125}, {100, -80, 125}, {-15, -80, 120},
 	{-15, -70, 150}, {100, -70, 140}, {100, -80, 140}, {-15, -80, 150}
 };
-static const Colony::ColonyEngine::PrismPartDef kForkliftParts[6] = {
+const Colony::ColonyEngine::PrismPartDef kForkliftParts[6] = {
 	{14, kFLCabPts, 12, kFLCabSurf},
 	{12, kFLTreadPts, 6, kFLTreadSurf},
 	{8, kFLULPts, 4, kFLArmSurf},
@@ -824,7 +824,7 @@ static const Colony::ColonyEngine::PrismPartDef kForkliftParts[6] = {
 };
 
 // Teleport: octagonal booth with flared middle
-static const int kTelePts[24][3] = {
+const int kTelePts[24][3] = {
 	// Ring 0: outer flared ring at z=140
 	{   0, 175, 140}, { 125, 125, 140}, { 175,   0, 140}, { 125,-125, 140},
 	{   0,-175, 140}, {-125,-125, 140}, {-175,   0, 140}, {-125, 125, 140},
@@ -835,7 +835,7 @@ static const int kTelePts[24][3] = {
 	{  0,  80, 280}, { 65,  65, 280}, { 80,   0, 280}, { 65, -65, 280},
 	{  0, -80, 280}, {-65, -65, 280}, {-80,   0, 280}, {-65,  65, 280}
 };
-static const int kTeleSurf[16][8] = {
+const int kTeleSurf[16][8] = {
 	// Bottom 8 panels (outer mid to inner bottom)
 	{kColorTeleDoor, 4, 0, 1, 9, 8, 0, 0},
 	{kColorTele,     4, 1, 2, 10, 9, 0, 0}, {kColorTele, 4, 2, 3, 11, 10, 0, 0},
@@ -849,33 +849,33 @@ static const int kTeleSurf[16][8] = {
 	{kColorTele,     4, 6, 5, 21, 22, 0, 0}, {kColorTele, 4, 7, 6, 22, 23, 0, 0},
 	{kColorTele,     4, 0, 7, 23, 16, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kTelePart = {24, kTelePts, 16, kTeleSurf};
+const Colony::ColonyEngine::PrismPartDef kTelePart = {24, kTelePts, 16, kTeleSurf};
 
 // Projector: body + stand + lens (sits on table)
-static const int kProjectorPts[8][3] = {
+const int kProjectorPts[8][3] = {
 	{-30, 30, 140}, {30, 30, 140}, {30, -30, 140}, {-30, -30, 140},
 	{-20, 30, 160}, {30, 30, 160}, {30, -30, 160}, {-20, -30, 160}
 };
-static const int kProjectorSurf[5][8] = {
+const int kProjectorSurf[5][8] = {
 	{kColorProjector, 4, 0, 3, 7, 4, 0, 0}, {kColorProjector, 4, 3, 2, 6, 7, 0, 0},
 	{kColorProjector, 4, 1, 0, 4, 5, 0, 0}, {kColorProjector, 4, 2, 1, 5, 6, 0, 0},
 	{kColorProjector, 4, 7, 6, 5, 4, 0, 0}
 };
-static const int kPStandPts[4][3] = {
+const int kPStandPts[4][3] = {
 	{-25, 50, 100}, {0, 10, 140}, {0, -10, 140}, {-25, -50, 100}
 };
-static const int kPStandSurf[1][8] = {{kColorPStand, 4, 0, 1, 2, 3, 0, 0}};
-static const int kPLensPts[12][3] = {
+const int kPStandSurf[1][8] = {{kColorPStand, 4, 0, 1, 2, 3, 0, 0}};
+const int kPLensPts[12][3] = {
 	{30,  8, 154}, {30,  0, 158}, {30, -8, 154}, {30, -8, 146}, {30,  0, 142}, {30,  8, 146},
 	{55, 10, 155}, {55,  0, 160}, {55,-10, 155}, {55,-10, 145}, {55,  0, 140}, {55, 10, 145}
 };
-static const int kPLensSurf[7][8] = {
+const int kPLensSurf[7][8] = {
 	{kColorPLens, 4, 0, 1, 7, 6, 0, 0}, {kColorPLens, 4, 1, 2, 8, 7, 0, 0},
 	{kColorPLens, 4, 2, 3, 9, 8, 0, 0}, {kColorPLens, 4, 3, 4, 10, 9, 0, 0},
 	{kColorPLens, 4, 4, 5, 11, 10, 0, 0}, {kColorPLens, 4, 5, 0, 6, 11, 0, 0},
 	{kColorBlack, 6, 6, 7, 8, 9, 10, 11}
 };
-static const Colony::ColonyEngine::PrismPartDef kProjectorParts[3] = {
+const Colony::ColonyEngine::PrismPartDef kProjectorParts[3] = {
 	{8, kProjectorPts, 5, kProjectorSurf},
 	{4, kPStandPts, 1, kPStandSurf},
 	{12, kPLensPts, 7, kPLensSurf}
@@ -887,175 +887,175 @@ static const Colony::ColonyEngine::PrismPartDef kProjectorParts[3] = {
 // ============================================================================
 
 // --- Pyramid (type 2) ---
-static const int kPyramidPts[5][3] = {
+const int kPyramidPts[5][3] = {
 	{-75, 75, 30}, {75, 75, 30}, {75, -75, 30}, {-75, -75, 30}, {0, 0, 200}
 };
-static const int kPyramidSurf[4][8] = {
+const int kPyramidSurf[4][8] = {
 	{kColorPyramid, 3, 1, 0, 4, 1, 0, 0}, {kColorPyramid, 3, 2, 1, 4, 2, 0, 0},
 	{kColorPyramid, 3, 3, 2, 4, 3, 0, 0}, {kColorPyramid, 3, 0, 3, 4, 0, 0, 0}
 };
-static const int kPShadowPts[4][3] = {
+const int kPShadowPts[4][3] = {
 	{-75, 75, 0}, {75, 75, 0}, {75, -75, 0}, {-75, -75, 0}
 };
-static const int kPShadowSurf[1][8] = {{kColorBlack, 4, 3, 2, 1, 0, 3, 0}};
+const int kPShadowSurf[1][8] = {{kColorBlack, 4, 3, 2, 1, 0, 3, 0}};
 // Pyramid eye (ball on top)
-static const int kPIrisPts[4][3] = {
+const int kPIrisPts[4][3] = {
 	{15, 0, 185}, {15, 15, 200}, {15, 0, 215}, {15, -15, 200}
 };
-static const int kPIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
-static const int kPPupilPts[4][3] = {
+const int kPIrisSurf[1][8] = {{kColorIris, 4, 0, 1, 2, 3, 0, 0}};
+const int kPPupilPts[4][3] = {
 	{16, 0, 194}, {16, 6, 200}, {16, 0, 206}, {16, -6, 200}
 };
-static const int kPPupilSurf[1][8] = {{kColorPupil, 4, 0, 1, 2, 3, 0, 0}};
+const int kPPupilSurf[1][8] = {{kColorPupil, 4, 0, 1, 2, 3, 0, 0}};
 
-static const Colony::ColonyEngine::PrismPartDef kPyramidBodyDef = {5, kPyramidPts, 4, kPyramidSurf};
-static const Colony::ColonyEngine::PrismPartDef kPShadowDef = {4, kPShadowPts, 1, kPShadowSurf};
-static const Colony::ColonyEngine::PrismPartDef kPIrisDef = {4, kPIrisPts, 1, kPIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kPPupilDef = {4, kPPupilPts, 1, kPPupilSurf};
+const Colony::ColonyEngine::PrismPartDef kPyramidBodyDef = {5, kPyramidPts, 4, kPyramidSurf};
+const Colony::ColonyEngine::PrismPartDef kPShadowDef = {4, kPShadowPts, 1, kPShadowSurf};
+const Colony::ColonyEngine::PrismPartDef kPIrisDef = {4, kPIrisPts, 1, kPIrisSurf};
+const Colony::ColonyEngine::PrismPartDef kPPupilDef = {4, kPPupilPts, 1, kPPupilSurf};
 
 // --- Cube (type 3) --- (octahedron)
-static const int kCubePts[6][3] = {
+const int kCubePts[6][3] = {
 	{0, 0, 200}, {100, 0, 100}, {0, 100, 100}, {-100, 0, 100}, {0, -100, 100}, {0, 0, 0}
 };
-static const int kCubeSurf[8][8] = {
+const int kCubeSurf[8][8] = {
 	{kColorCube, 3, 0, 1, 2, 0, 0, 0}, {kColorCube, 3, 0, 2, 3, 0, 0, 0},
 	{kColorCube, 3, 0, 3, 4, 0, 0, 0}, {kColorCube, 3, 0, 4, 1, 0, 0, 0},
 	{kColorCube, 3, 5, 2, 1, 5, 0, 0}, {kColorCube, 3, 5, 3, 2, 5, 0, 0},
 	{kColorCube, 3, 5, 4, 3, 5, 0, 0}, {kColorCube, 3, 5, 1, 4, 5, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kCubeBodyDef = {6, kCubePts, 8, kCubeSurf};
+const Colony::ColonyEngine::PrismPartDef kCubeBodyDef = {6, kCubePts, 8, kCubeSurf};
 
 // --- UPyramid (type 4) --- (inverted pyramid)
-static const int kUPyramidPts[5][3] = {
+const int kUPyramidPts[5][3] = {
 	{-75, 75, 190}, {75, 75, 190}, {75, -75, 190}, {-75, -75, 190}, {0, 0, 30}
 };
-static const int kUPyramidSurf[5][8] = {
+const int kUPyramidSurf[5][8] = {
 	{kColorUPyramid, 3, 0, 1, 4, 0, 0, 0}, {kColorUPyramid, 3, 1, 2, 4, 1, 0, 0},
 	{kColorUPyramid, 3, 2, 3, 4, 2, 0, 0}, {kColorUPyramid, 3, 3, 0, 4, 3, 0, 0},
 	{kColorUPyramid, 4, 3, 2, 1, 0, 3, 0}
 };
-static const int kUPShadowPts[4][3] = {
+const int kUPShadowPts[4][3] = {
 	{-25, 25, 0}, {25, 25, 0}, {25, -25, 0}, {-25, -25, 0}
 };
-static const int kUPShadowSurf[1][8] = {{kColorBlack, 4, 3, 2, 1, 0, 3, 0}};
+const int kUPShadowSurf[1][8] = {{kColorBlack, 4, 3, 2, 1, 0, 3, 0}};
 
-static const Colony::ColonyEngine::PrismPartDef kUPyramidBodyDef = {5, kUPyramidPts, 5, kUPyramidSurf};
-static const Colony::ColonyEngine::PrismPartDef kUPShadowDef = {4, kUPShadowPts, 1, kUPShadowSurf};
+const Colony::ColonyEngine::PrismPartDef kUPyramidBodyDef = {5, kUPyramidPts, 5, kUPyramidSurf};
+const Colony::ColonyEngine::PrismPartDef kUPShadowDef = {4, kUPShadowPts, 1, kUPShadowSurf};
 
 // --- Eye (type 1) ---
 // Ball is rendered by draw3DSphere(), not as a prism
-static const int kEyeIrisPts[4][3] = {
+const int kEyeIrisPts[4][3] = {
 	{60, 0, 140}, {60, 60, 200}, {60, 0, 260}, {60, -60, 200}
 };
-static const int kEyeIrisSurf[1][8] = {{kColorEyeIris, 4, 0, 1, 2, 3, 0, 0}};
-static const int kEyePupilPts[4][3] = {
+const int kEyeIrisSurf[1][8] = {{kColorEyeIris, 4, 0, 1, 2, 3, 0, 0}};
+const int kEyePupilPts[4][3] = {
 	{66, 0, 175}, {66, 25, 200}, {66, 0, 225}, {66, -25, 200}
 };
-static const int kEyePupilSurf[1][8] = {{kColorBlack, 4, 0, 1, 2, 3, 0, 0}};
+const int kEyePupilSurf[1][8] = {{kColorBlack, 4, 0, 1, 2, 3, 0, 0}};
 
-static const Colony::ColonyEngine::PrismPartDef kEyeIrisDef = {4, kEyeIrisPts, 1, kEyeIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kEyePupilDef = {4, kEyePupilPts, 1, kEyePupilSurf};
+const Colony::ColonyEngine::PrismPartDef kEyeIrisDef = {4, kEyeIrisPts, 1, kEyeIrisSurf};
+const Colony::ColonyEngine::PrismPartDef kEyePupilDef = {4, kEyePupilPts, 1, kEyePupilSurf};
 
 // --- Floating Pyramid (type 6) --- egg on ground
-static const int kFPyramidPts[5][3] = {
+const int kFPyramidPts[5][3] = {
 	{-75, 75, 0}, {75, 75, 0}, {75, -75, 0}, {-75, -75, 0}, {0, 0, 170}
 };
-static const Colony::ColonyEngine::PrismPartDef kFPyramidBodyDef = {5, kFPyramidPts, 4, kPyramidSurf};
+const Colony::ColonyEngine::PrismPartDef kFPyramidBodyDef = {5, kFPyramidPts, 4, kPyramidSurf};
 
 // --- Small Pyramid (type 10) ---
-static const int kSPyramidPts[5][3] = {
+const int kSPyramidPts[5][3] = {
 	{-40, 40, 0}, {40, 40, 0}, {40, -40, 0}, {-40, -40, 0}, {0, 0, 100}
 };
-static const Colony::ColonyEngine::PrismPartDef kSPyramidBodyDef = {5, kSPyramidPts, 4, kPyramidSurf};
+const Colony::ColonyEngine::PrismPartDef kSPyramidBodyDef = {5, kSPyramidPts, 4, kPyramidSurf};
 
 // --- Mini Pyramid (type 14) ---
-static const int kMPyramidPts[5][3] = {
+const int kMPyramidPts[5][3] = {
 	{-20, 20, 0}, {20, 20, 0}, {20, -20, 0}, {-20, -20, 0}, {0, 0, 50}
 };
-static const Colony::ColonyEngine::PrismPartDef kMPyramidBodyDef = {5, kMPyramidPts, 4, kPyramidSurf};
+const Colony::ColonyEngine::PrismPartDef kMPyramidBodyDef = {5, kMPyramidPts, 4, kPyramidSurf};
 
 // --- Floating Cube (type 7) ---
-static const int kFCubePts[6][3] = {
+const int kFCubePts[6][3] = {
 	{0, 0, 150}, {75, 0, 75}, {0, 75, 75}, {-75, 0, 75}, {0, -75, 75}, {0, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kFCubeBodyDef = {6, kFCubePts, 8, kCubeSurf};
+const Colony::ColonyEngine::PrismPartDef kFCubeBodyDef = {6, kFCubePts, 8, kCubeSurf};
 
 // --- Small Cube (type 11) ---
-static const int kSCubePts[6][3] = {
+const int kSCubePts[6][3] = {
 	{0, 0, 100}, {50, 0, 50}, {0, 50, 50}, {-50, 0, 50}, {0, -50, 50}, {0, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kSCubeBodyDef = {6, kSCubePts, 8, kCubeSurf};
+const Colony::ColonyEngine::PrismPartDef kSCubeBodyDef = {6, kSCubePts, 8, kCubeSurf};
 
 // --- Mini Cube (type 15) ---
-static const int kMCubePts[6][3] = {
+const int kMCubePts[6][3] = {
 	{0, 0, 50}, {25, 0, 25}, {0, 25, 25}, {-25, 0, 25}, {0, -25, 25}, {0, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kMCubeBodyDef = {6, kMCubePts, 8, kCubeSurf};
+const Colony::ColonyEngine::PrismPartDef kMCubeBodyDef = {6, kMCubePts, 8, kCubeSurf};
 
 // --- Floating UPyramid (type 8) ---
-static const int kFUPyramidPts[5][3] = {
+const int kFUPyramidPts[5][3] = {
 	{-75, 75, 170}, {75, 75, 170}, {75, -75, 170}, {-75, -75, 170}, {0, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kFUPyramidBodyDef = {5, kFUPyramidPts, 5, kUPyramidSurf};
+const Colony::ColonyEngine::PrismPartDef kFUPyramidBodyDef = {5, kFUPyramidPts, 5, kUPyramidSurf};
 
 // --- Small UPyramid (type 12) ---
-static const int kSUPyramidPts[5][3] = {
+const int kSUPyramidPts[5][3] = {
 	{-40, 40, 100}, {40, 40, 100}, {40, -40, 100}, {-40, -40, 100}, {0, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kSUPyramidBodyDef = {5, kSUPyramidPts, 5, kUPyramidSurf};
+const Colony::ColonyEngine::PrismPartDef kSUPyramidBodyDef = {5, kSUPyramidPts, 5, kUPyramidSurf};
 
 // --- Mini UPyramid (type 16) ---
-static const int kMUPyramidPts[5][3] = {
+const int kMUPyramidPts[5][3] = {
 	{-20, 20, 50}, {20, 20, 50}, {20, -20, 50}, {-20, -20, 50}, {0, 0, 0}
 };
-static const Colony::ColonyEngine::PrismPartDef kMUPyramidBodyDef = {5, kMUPyramidPts, 5, kUPyramidSurf};
+const Colony::ColonyEngine::PrismPartDef kMUPyramidBodyDef = {5, kMUPyramidPts, 5, kUPyramidSurf};
 
 // --- Floating Eye (type 5) ---
-static const int kFEyeIrisPts[4][3] = {
+const int kFEyeIrisPts[4][3] = {
 	{60, 0, 40}, {60, 60, 100}, {60, 0, 160}, {60, -60, 100}
 };
-static const int kFEyePupilPts[4][3] = {
+const int kFEyePupilPts[4][3] = {
 	{66, 0, 75}, {66, 25, 100}, {66, 0, 125}, {66, -25, 100}
 };
-static const Colony::ColonyEngine::PrismPartDef kFEyeIrisDef = {4, kFEyeIrisPts, 1, kEyeIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kFEyePupilDef = {4, kFEyePupilPts, 1, kEyePupilSurf};
+const Colony::ColonyEngine::PrismPartDef kFEyeIrisDef = {4, kFEyeIrisPts, 1, kEyeIrisSurf};
+const Colony::ColonyEngine::PrismPartDef kFEyePupilDef = {4, kFEyePupilPts, 1, kEyePupilSurf};
 
 // --- Small Eye (type 9) ---
-static const int kSEyeIrisPts[4][3] = {
+const int kSEyeIrisPts[4][3] = {
 	{30, 0, 20}, {30, 30, 50}, {30, 0, 80}, {30, -30, 50}
 };
-static const int kSEyePupilPts[4][3] = {
+const int kSEyePupilPts[4][3] = {
 	{33, 0, 38}, {33, 13, 50}, {33, 0, 63}, {33, -13, 50}
 };
-static const Colony::ColonyEngine::PrismPartDef kSEyeIrisDef = {4, kSEyeIrisPts, 1, kEyeIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kSEyePupilDef = {4, kSEyePupilPts, 1, kEyePupilSurf};
+const Colony::ColonyEngine::PrismPartDef kSEyeIrisDef = {4, kSEyeIrisPts, 1, kEyeIrisSurf};
+const Colony::ColonyEngine::PrismPartDef kSEyePupilDef = {4, kSEyePupilPts, 1, kEyePupilSurf};
 
 // --- Mini Eye (type 13) ---
-static const int kMEyeIrisPts[4][3] = {
+const int kMEyeIrisPts[4][3] = {
 	{15, 0, 10}, {15, 15, 25}, {15, 0, 40}, {15, -15, 25}
 };
-static const int kMEyePupilPts[4][3] = {
+const int kMEyePupilPts[4][3] = {
 	{16, 0, 19}, {16, 6, 25}, {16, 0, 31}, {16, -6, 25}
 };
-static const Colony::ColonyEngine::PrismPartDef kMEyeIrisDef = {4, kMEyeIrisPts, 1, kEyeIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kMEyePupilDef = {4, kMEyePupilPts, 1, kEyePupilSurf};
+const Colony::ColonyEngine::PrismPartDef kMEyeIrisDef = {4, kMEyeIrisPts, 1, kEyeIrisSurf};
+const Colony::ColonyEngine::PrismPartDef kMEyePupilDef = {4, kMEyePupilPts, 1, kEyePupilSurf};
 
 // --- Queen (type 17) ---
 // Queen eye (ball rendered by draw3DSphere)
-static const int kQIrisPts[4][3] = {
+const int kQIrisPts[4][3] = {
 	{15, 0, 140}, {15, 15, 155}, {15, 0, 170}, {15, -15, 155}
 };
-static const int kQIrisSurf[1][8] = {{kColorQueenEye, 4, 0, 1, 2, 3, 0, 0}};
-static const int kQPupilPts[4][3] = {
+const int kQIrisSurf[1][8] = {{kColorQueenEye, 4, 0, 1, 2, 3, 0, 0}};
+const int kQPupilPts[4][3] = {
 	{16, 0, 148}, {16, 6, 155}, {16, 0, 161}, {16, -6, 155}
 };
-static const int kQPupilSurf[1][8] = {{kColorPupil, 4, 0, 1, 2, 3, 0, 0}};
+const int kQPupilSurf[1][8] = {{kColorPupil, 4, 0, 1, 2, 3, 0, 0}};
 // Queen abdomen
-static const int kQAbdomenPts[9][3] = {
+const int kQAbdomenPts[9][3] = {
 	{120, 0, 130}, {30, 0, 160}, {30, 0, 100}, {30, 50, 130}, {30, -50, 130},
 	{0, 0, 150}, {0, 0, 110}, {0, 25, 130}, {0, -25, 130}
 };
-static const int kQAbdomenSurf[9][8] = {
+const int kQAbdomenSurf[9][8] = {
 	{kColorQueenBody, 3, 0, 3, 1, 0, 0, 0}, {kColorQueenBody, 3, 0, 1, 4, 0, 0, 0},
 	{kColorQueenBody, 3, 0, 2, 3, 0, 0, 0}, {kColorQueenBody, 3, 0, 4, 2, 0, 0, 0},
 	{kColorQueenBody, 4, 1, 5, 8, 4, 1, 0}, {kColorQueenBody, 4, 1, 3, 7, 5, 1, 0},
@@ -1063,95 +1063,95 @@ static const int kQAbdomenSurf[9][8] = {
 	{kColorClear, 4, 5, 7, 6, 8, 5, 0}
 };
 // Queen thorax
-static const int kQThoraxPts[9][3] = {
+const int kQThoraxPts[9][3] = {
 	{-120, 0, 130}, {-50, 0, 170}, {-50, 0, 90}, {-50, 60, 130}, {-50, -60, 130},
 	{0, 0, 150}, {0, 0, 110}, {0, 25, 130}, {0, -25, 130}
 };
-static const int kQThoraxSurf[8][8] = {
+const int kQThoraxSurf[8][8] = {
 	{kColorQueenBody, 3, 0, 1, 3, 0, 0, 0}, {kColorQueenBody, 3, 0, 4, 1, 0, 0, 0},
 	{kColorQueenBody, 3, 0, 3, 2, 0, 0, 0}, {kColorQueenBody, 3, 0, 2, 4, 0, 0, 0},
 	{kColorQueenBody, 4, 1, 4, 8, 5, 1, 0}, {kColorQueenBody, 4, 1, 5, 7, 3, 1, 0},
 	{kColorQueenBody, 4, 2, 6, 8, 4, 2, 0}, {kColorQueenBody, 4, 2, 3, 7, 6, 2, 0}
 };
 // Queen wings
-static const int kQLWingPts[4][3] = {
+const int kQLWingPts[4][3] = {
 	{80, 0, 140}, {-40, 10, 200}, {-120, 60, 170}, {-40, 120, 140}
 };
-static const int kQLWingSurf[1][8] = {{kColorClear, 4, 0, 1, 2, 3, 0, 0}};
-static const int kQRWingPts[4][3] = {
+const int kQLWingSurf[1][8] = {{kColorClear, 4, 0, 1, 2, 3, 0, 0}};
+const int kQRWingPts[4][3] = {
 	{80, 0, 140}, {-40, -10, 200}, {-120, -60, 170}, {-40, -120, 140}
 };
-static const int kQRWingSurf[1][8] = {{kColorClear, 4, 0, 1, 2, 3, 0, 0}};
+const int kQRWingSurf[1][8] = {{kColorClear, 4, 0, 1, 2, 3, 0, 0}};
 
-static const Colony::ColonyEngine::PrismPartDef kQIrisDef = {4, kQIrisPts, 1, kQIrisSurf};
-static const Colony::ColonyEngine::PrismPartDef kQPupilDef = {4, kQPupilPts, 1, kQPupilSurf};
-static const Colony::ColonyEngine::PrismPartDef kQAbdomenDef = {9, kQAbdomenPts, 9, kQAbdomenSurf};
-static const Colony::ColonyEngine::PrismPartDef kQThoraxDef = {9, kQThoraxPts, 8, kQThoraxSurf};
-static const Colony::ColonyEngine::PrismPartDef kQLWingDef = {4, kQLWingPts, 1, kQLWingSurf};
-static const Colony::ColonyEngine::PrismPartDef kQRWingDef = {4, kQRWingPts, 1, kQRWingSurf};
+const Colony::ColonyEngine::PrismPartDef kQIrisDef = {4, kQIrisPts, 1, kQIrisSurf};
+const Colony::ColonyEngine::PrismPartDef kQPupilDef = {4, kQPupilPts, 1, kQPupilSurf};
+const Colony::ColonyEngine::PrismPartDef kQAbdomenDef = {9, kQAbdomenPts, 9, kQAbdomenSurf};
+const Colony::ColonyEngine::PrismPartDef kQThoraxDef = {9, kQThoraxPts, 8, kQThoraxSurf};
+const Colony::ColonyEngine::PrismPartDef kQLWingDef = {4, kQLWingPts, 1, kQLWingSurf};
+const Colony::ColonyEngine::PrismPartDef kQRWingDef = {4, kQRWingPts, 1, kQRWingSurf};
 
 // --- Drone / Soldier (types 18, 19) ---
-static const int kDAbdomenPts[6][3] = {
+const int kDAbdomenPts[6][3] = {
 	{0, 0, 170}, {120, 0, 130}, {0, 100, 130}, {-130, 0, 130}, {0, -100, 130}, {0, 0, 100}
 };
-static const int kDAbdomenSurf[8][8] = {
+const int kDAbdomenSurf[8][8] = {
 	{kColorDrone, 3, 0, 1, 2, 0, 0, 0}, {kColorDrone, 3, 0, 2, 3, 0, 0, 0},
 	{kColorDrone, 3, 0, 3, 4, 0, 0, 0}, {kColorDrone, 3, 0, 4, 1, 0, 0, 0},
 	{kColorDrone, 3, 5, 2, 1, 5, 0, 0}, {kColorDrone, 3, 5, 3, 2, 5, 0, 0},
 	{kColorDrone, 3, 5, 4, 3, 5, 0, 0}, {kColorDrone, 3, 5, 1, 4, 5, 0, 0}
 };
 // Drone static pincers (llPincer/rrPincer)
-static const int kDLLPincerPts[4][3] = {
+const int kDLLPincerPts[4][3] = {
 	{0, 0, 130}, {50, -2, 130}, {35, -20, 140}, {35, -20, 120}
 };
-static const int kDLPincerSurf[4][8] = {
+const int kDLPincerSurf[4][8] = {
 	{kColorClaw1, 3, 0, 2, 1, 0, 0, 0}, {kColorClaw1, 3, 0, 1, 3, 0, 0, 0},
 	{kColorClaw2, 3, 0, 3, 2, 0, 0, 0}, {kColorClaw2, 3, 1, 2, 3, 1, 0, 0}
 };
-static const int kDRRPincerPts[4][3] = {
+const int kDRRPincerPts[4][3] = {
 	{0, 0, 130}, {50, 2, 130}, {35, 20, 140}, {35, 20, 120}
 };
-static const int kDRPincerSurf[4][8] = {
+const int kDRPincerSurf[4][8] = {
 	{kColorClaw1, 3, 0, 1, 2, 0, 0, 0}, {kColorClaw1, 3, 0, 3, 1, 0, 0, 0},
 	{kColorClaw2, 3, 0, 2, 3, 0, 0, 0}, {kColorClaw2, 3, 1, 3, 2, 1, 0, 0}
 };
 // Drone eyes
-static const int kDLEyePts[3][3] = {
+const int kDLEyePts[3][3] = {
 	{60, 0, 150}, {60, 50, 130}, {60, 25, 150}
 };
-static const int kDLEyeSurf[2][8] = {
+const int kDLEyeSurf[2][8] = {
 	{kColorDroneEye, 3, 0, 1, 2, 0, 0, 0}, {kColorDroneEye, 3, 0, 2, 1, 0, 0, 0}
 };
-static const int kDREyePts[3][3] = {
+const int kDREyePts[3][3] = {
 	{60, 0, 150}, {60, -50, 130}, {60, -25, 150}
 };
-static const int kDREyeSurf[2][8] = {
+const int kDREyeSurf[2][8] = {
 	{kColorDroneEye, 3, 0, 1, 2, 0, 0, 0}, {kColorDroneEye, 3, 0, 2, 1, 0, 0, 0}
 };
 
-static const Colony::ColonyEngine::PrismPartDef kDAbdomenDef = {6, kDAbdomenPts, 8, kDAbdomenSurf};
-static const Colony::ColonyEngine::PrismPartDef kDLLPincerDef = {4, kDLLPincerPts, 4, kDLPincerSurf};
-static const Colony::ColonyEngine::PrismPartDef kDRRPincerDef = {4, kDRRPincerPts, 4, kDRPincerSurf};
-static const Colony::ColonyEngine::PrismPartDef kDLEyeDef = {3, kDLEyePts, 2, kDLEyeSurf};
-static const Colony::ColonyEngine::PrismPartDef kDREyeDef = {3, kDREyePts, 2, kDREyeSurf};
+const Colony::ColonyEngine::PrismPartDef kDAbdomenDef = {6, kDAbdomenPts, 8, kDAbdomenSurf};
+const Colony::ColonyEngine::PrismPartDef kDLLPincerDef = {4, kDLLPincerPts, 4, kDLPincerSurf};
+const Colony::ColonyEngine::PrismPartDef kDRRPincerDef = {4, kDRRPincerPts, 4, kDRPincerSurf};
+const Colony::ColonyEngine::PrismPartDef kDLEyeDef = {3, kDLEyePts, 2, kDLEyeSurf};
+const Colony::ColonyEngine::PrismPartDef kDREyeDef = {3, kDREyePts, 2, kDREyeSurf};
 
 // --- Snoop (type 20) ---
-static const int kSnoopAbdomenPts[4][3] = {
+const int kSnoopAbdomenPts[4][3] = {
 	{0, 100, 0}, {-180, 0, 0}, {0, -100, 0}, {0, 0, 70}
 };
-static const int kSnoopAbdomenSurf[2][8] = {
+const int kSnoopAbdomenSurf[2][8] = {
 	{kColorTopSnoop, 3, 0, 1, 3, 0, 0, 0}, {kColorTopSnoop, 3, 2, 3, 1, 2, 0, 0}
 };
-static const int kSnoopHeadPts[4][3] = {
+const int kSnoopHeadPts[4][3] = {
 	{0, 100, 0}, {150, 0, 0}, {0, -100, 0}, {0, 0, 70}
 };
-static const int kSnoopHeadSurf[3][8] = {
+const int kSnoopHeadSurf[3][8] = {
 	{kColorTopSnoop, 3, 0, 3, 1, 0, 0, 0}, {kColorTopSnoop, 3, 2, 1, 3, 2, 0, 0},
 	{kColorBottomSnoop, 3, 0, 1, 2, 0, 0, 0}
 };
 
-static const Colony::ColonyEngine::PrismPartDef kSnoopAbdomenDef = {4, kSnoopAbdomenPts, 2, kSnoopAbdomenSurf};
-static const Colony::ColonyEngine::PrismPartDef kSnoopHeadDef = {4, kSnoopHeadPts, 3, kSnoopHeadSurf};
+const Colony::ColonyEngine::PrismPartDef kSnoopAbdomenDef = {4, kSnoopAbdomenPts, 2, kSnoopAbdomenSurf};
+const Colony::ColonyEngine::PrismPartDef kSnoopHeadDef = {4, kSnoopHeadPts, 3, kSnoopHeadSurf};
 
 int wrapAngle256(int angle) {
 	angle %= 256;
@@ -1258,7 +1258,7 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 	const float axisVY = (transformedY[2] - transformedY[0]) * 0.5f;
 	const float axisVZ = (transformedZ[2] - transformedZ[0]) * 0.5f;
 
-	static const int kSegments = 20;
+	const int kSegments = 20;
 	float px[kSegments];
 	float py[kSegments];
 	float pz[kSegments];
@@ -1477,7 +1477,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		memcpy(modTopSurf, kReactorBaseSurf, sizeof(modTopSurf));
 
 		// Mac MakeReactor(): first 4 ring/top faces cycle through c_color0..c_color3.
-		static const int kRingColors[] = {kColorRainbow1, kColorRainbow2, kColorRainbow3, kColorRainbow4};
+		const int kRingColors[] = {kColorRainbow1, kColorRainbow2, kColorRainbow3, kColorRainbow4};
 		const int ringColor = kRingColors[_displayCount % 4];
 		for (int i = 0; i < 4; ++i) {
 			modRingSurf[i][0] = ringColor;
@@ -1485,7 +1485,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		}
 
 		// Only the 6 core side faces animate. The top face remains c_ccore.
-		static const int kCoreCycle[] = {kColorHCore1, kColorHCore2, kColorHCore3, kColorHCore4, kColorHCore3, kColorHCore2};
+		const int kCoreCycle[] = {kColorHCore1, kColorHCore2, kColorHCore3, kColorHCore4, kColorHCore3, kColorHCore2};
 		int coreColor;
 		if (_corePower[_coreIndex] > 1)
 			coreColor = kCoreCycle[_displayCount % 6];
@@ -1514,7 +1514,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	case kObjPowerSuit: {
 		// MakePowerSuit: part[4] (power source hexagon) surface[0] pulsates.
 		// Mac: pcycle[count%6]; DOS: pcycle[count%8]
-		static const int kSuitCycle[] = {kColorHCore1, kColorHCore2, kColorHCore3, kColorHCore4, kColorHCore3, kColorHCore2};
+		const int kSuitCycle[] = {kColorHCore1, kColorHCore2, kColorHCore3, kColorHCore4, kColorHCore3, kColorHCore2};
 		int sourceColor = kSuitCycle[_displayCount % 6];
 
 		for (int i = 0; i < 4; i++) {
diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
index 3de0c4f5f59..330f6fb1066 100644
--- a/engines/colony/savegame.cpp
+++ b/engines/colony/savegame.cpp
@@ -29,9 +29,9 @@ namespace Colony {
 
 namespace {
 
-static const uint32 kSaveVersion = 1;
-static const uint32 kMaxSaveObjects = 4096;
-static const uint32 kMaxSavePatches = 100;
+const uint32 kSaveVersion = 1;
+const uint32 kMaxSaveObjects = 4096;
+const uint32 kMaxSavePatches = 100;
 
 Common::Error makeReadError(const char *msg) {
 	return Common::Error(Common::kReadingFailed, msg);


Commit: 67678bd574e37eaa68637ee93d5a6c3a2aa85cc0
    https://github.com/scummvm/scummvm/commit/67678bd574e37eaa68637ee93d5a6c3a2aa85cc0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:58+02:00

Commit Message:
COLONY: replace long/long long by portable types

Changed paths:
    engines/colony/battle.cpp
    engines/colony/intro.cpp
    engines/colony/movement.cpp
    engines/colony/render.cpp
    engines/colony/render_objects.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 03965ba8a5f..6bf4802dcf6 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -147,8 +147,8 @@ bool battleAccumulateBounds(const Common::Rect &screenR, const ColonyEngine::Pri
 	uint8 look, int8 lookY, const int *sint, const int *cost,
 	int camX, int camY) {
 	const uint8 rotAng = ang + 32;
-	const long rotCos = cost[rotAng];
-	const long rotSin = sint[rotAng];
+	const int32 rotCos = cost[rotAng];
+	const int32 rotSin = sint[rotAng];
 	bool hasPoint = false;
 
 	for (int i = 0; i < def.pointCount; i++) {
@@ -156,8 +156,8 @@ bool battleAccumulateBounds(const Common::Rect &screenR, const ColonyEngine::Pri
 		const int oy = def.points[i][1];
 		const int oz = def.points[i][2];
 
-		const long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
-		const long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+		const int32 rx = ((int32)ox * rotCos - (int32)oy * rotSin) >> 7;
+		const int32 ry = ((int32)ox * rotSin + (int32)oy * rotCos) >> 7;
 
 		int sx = 0;
 		int sy = 0;
@@ -396,8 +396,8 @@ const ColonyEngine::PrismPartDef kBREyeDef = {3, kBREyePts, 2, kBREyeSurf};
 void ColonyEngine::draw3DBattlePrism(const PrismPartDef &def, int worldX, int worldY, uint8 ang, int zShift) {
 	// +32 compensates for sine table's 45-degree phase offset
 	const uint8 rotAng = ang + 32;
-	const long rotCos = _cost[rotAng];
-	const long rotSin = _sint[rotAng];
+	const int32 rotCos = _cost[rotAng];
+	const int32 rotSin = _sint[rotAng];
 
 	for (int i = 0; i < def.surfaceCount; i++) {
 		const int colorIdx = def.surfaces[i][0];
@@ -418,8 +418,8 @@ void ColonyEngine::draw3DBattlePrism(const PrismPartDef &def, int worldX, int wo
 			int oz = def.points[cur][2];
 
 			// Rotate around Z axis by object angle
-			long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
-			long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+			int32 rx = ((int32)ox * rotCos - (int32)oy * rotSin) >> 7;
+			int32 ry = ((int32)ox * rotSin + (int32)oy * rotCos) >> 7;
 
 			px[count] = (float)(rx + worldX);
 			py[count] = (float)(ry + worldY);
@@ -676,8 +676,8 @@ void ColonyEngine::battleDrawTanks() {
 
 	// --- 16 enemy drones ---
 	for (int i = 0; i < 16; i++) {
-		long relX = _bfight[i].xloc - _me.xloc;
-		long relY = _bfight[i].yloc - _me.yloc;
+		int32 relX = _bfight[i].xloc - _me.xloc;
+		int32 relY = _bfight[i].yloc - _me.yloc;
 		if (ABS(relX) + ABS(relY) >= 8000)
 			continue;
 
@@ -704,10 +704,10 @@ void ColonyEngine::battleDrawTanks() {
 		if (lLook < 0)
 			lLook += 256;
 		for (int j = 0; j < 4; j++) {
-			long tcos = _cost[(uint8)lLook];
-			long tsin = _sint[(uint8)lLook];
-			lPincerPts[j][0] = (int)(((long)kLLPincerPts[j][0] * tcos - (long)kLLPincerPts[j][1] * tsin) >> 7);
-			lPincerPts[j][1] = (int)(((long)kLLPincerPts[j][0] * tsin + (long)kLLPincerPts[j][1] * tcos) >> 7);
+			int32 tcos = _cost[(uint8)lLook];
+			int32 tsin = _sint[(uint8)lLook];
+			lPincerPts[j][0] = (int)(((int32)kLLPincerPts[j][0] * tcos - (int32)kLLPincerPts[j][1] * tsin) >> 7);
+			lPincerPts[j][1] = (int)(((int32)kLLPincerPts[j][0] * tsin + (int32)kLLPincerPts[j][1] * tcos) >> 7);
 			lPincerPts[j][2] = kLLPincerPts[j][2];
 			lPincerPts[j][0] += 120; // offset from abdomen center
 		}
@@ -722,10 +722,10 @@ void ColonyEngine::battleDrawTanks() {
 		if (rLook < 0)
 			rLook += 256;
 		for (int j = 0; j < 4; j++) {
-			long tcos = _cost[(uint8)rLook];
-			long tsin = _sint[(uint8)rLook];
-			rPincerPts[j][0] = (int)(((long)kRRPincerPts[j][0] * tcos - (long)kRRPincerPts[j][1] * tsin) >> 7);
-			rPincerPts[j][1] = (int)(((long)kRRPincerPts[j][0] * tsin + (long)kRRPincerPts[j][1] * tcos) >> 7);
+			int32 tcos = _cost[(uint8)rLook];
+			int32 tsin = _sint[(uint8)rLook];
+			rPincerPts[j][0] = (int)(((int32)kRRPincerPts[j][0] * tcos - (int32)kRRPincerPts[j][1] * tsin) >> 7);
+			rPincerPts[j][1] = (int)(((int32)kRRPincerPts[j][0] * tsin + (int32)kRRPincerPts[j][1] * tcos) >> 7);
 			rPincerPts[j][2] = kRRPincerPts[j][2];
 			rPincerPts[j][0] += 120;
 		}
@@ -754,8 +754,8 @@ void ColonyEngine::battleDrawTanks() {
 
 	// --- Projectile ---
 	if (_projon) {
-		long relX = _battleProj.xloc - _me.xloc;
-		long relY = _battleProj.yloc - _me.yloc;
+		int32 relX = _battleProj.xloc - _me.xloc;
+		int32 relY = _battleProj.yloc - _me.yloc;
 		if (ABS(relX) + ABS(relY) < 20000) {
 			const int forward = (int)((relX * _cost[_me.look] + relY * _sint[_me.look]) >> 7);
 			battleResetBounds(_screenR, _battleProj);
@@ -774,8 +774,8 @@ void ColonyEngine::battleDrawTanks() {
 
 	// --- Entrance ---
 	{
-		long relX = _battleEnter.xloc - _me.xloc;
-		long relY = _battleEnter.yloc - _me.yloc;
+		int32 relX = _battleEnter.xloc - _me.xloc;
+		int32 relY = _battleEnter.yloc - _me.yloc;
 		if (ABS(relX) + ABS(relY) < 20000) {
 			const int forward = (int)((relX * _cost[_me.look] + relY * _sint[_me.look]) >> 7);
 			battleResetBounds(_screenR, _battleEnter);
@@ -806,8 +806,8 @@ void ColonyEngine::battleDrawTanks() {
 
 	// --- Shuttle (only if not in orbit) ---
 	if (!_orbit) {
-		long relX = _battleShip.xloc - _me.xloc;
-		long relY = _battleShip.yloc - _me.yloc;
+		int32 relX = _battleShip.xloc - _me.xloc;
+		int32 relY = _battleShip.yloc - _me.yloc;
 		if (ABS(relX) + ABS(relY) < 20000) {
 			const int forward = (int)((relX * _cost[_me.look] + relY * _sint[_me.look]) >> 7);
 			battleResetBounds(_screenR, _battleShip);
@@ -889,12 +889,12 @@ void ColonyEngine::battleThink() {
 		}
 
 		uint8 &ang = _bfight[i].ang;
-		long dx = _bfight[i].xloc - _me.xloc;
-		long dy = _bfight[i].yloc - _me.yloc;
-		long adx = ABS(dx);
-		long ady = ABS(dy);
+		int32 dx = _bfight[i].xloc - _me.xloc;
+		int32 dy = _bfight[i].yloc - _me.yloc;
+		int32 adx = ABS(dx);
+		int32 ady = ABS(dy);
 		bool tooFar = false;
-		long distance = 0;
+		int32 distance = 0;
 
 		if (adx > 4000 || ady > 4000) {
 			dx >>= 8;
@@ -902,9 +902,9 @@ void ColonyEngine::battleThink() {
 			tooFar = true;
 		}
 
-		long dir = dx * _sint[ang] - dy * _cost[ang];
+		int32 dir = dx * _sint[ang] - dy * _cost[ang];
 		if (!tooFar) {
-			distance = (long)sqrt((double)(dx * dx + dy * dy));
+			distance = (int32)sqrt((double)(dx * dx + dy * dy));
 			if (distance > 0) {
 				dir /= distance;
 				if (ABS(dir) < 10) {
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 73d13979e96..63c1b3c1417 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -568,8 +568,8 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 		if (d < 1)
 			d = 1;
 		int rr = rtable[d];
-		int xx = centerX + (int)(((long long)s * rr) >> 7);
-		int yy = centerY + (int)(((long long)c * rr) >> 7);
+		int xx = centerX + (int)(((int64)s * rr) >> 7);
+		int yy = centerY + (int)(((int64)c * rr) >> 7);
 		if (xx >= 0 && xx < _width && yy >= 0 && yy < _height)
 			_gfx->setPixel(xx, yy, 0xFFFFFFFF);
 	}
@@ -590,15 +590,15 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 		int c = yang[i] = (int16)(_randomSource.getRandomNumber(0xFFFF)) >> 7;
 
 		int rr = rtable[d];
-		xsave1[i] = centerX + (int)(((long long)s * rr) >> 7);
-		ysave1[i] = centerY + (int)(((long long)c * rr) >> 7);
+		xsave1[i] = centerX + (int)(((int64)s * rr) >> 7);
+		ysave1[i] = centerY + (int)(((int64)c * rr) >> 7);
 
 		int d2 = d - deltapd;
 		if (d2 < 1)
 			d2 = 1;
 		rr = rtable[d2];
-		xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
-		ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
+		xsave2[i] = centerX + (int)(((int64)s * rr) >> 7);
+		ysave2[i] = centerY + (int)(((int64)c * rr) >> 7);
 
 		_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 0xFFFFFFFF);
 	}
@@ -621,8 +621,8 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 			if (dist[i] <= 0x030) {
 				dist[i] = MAXSTAR;
 				int rr = rtable[MAXSTAR];
-				xsave1[i] = centerX + (int)(((long long)s * rr) >> 7);
-				ysave1[i] = centerY + (int)(((long long)c * rr) >> 7);
+				xsave1[i] = centerX + (int)(((int64)s * rr) >> 7);
+				ysave1[i] = centerY + (int)(((int64)c * rr) >> 7);
 			} else {
 				xsave1[i] = xsave2[i];
 				ysave1[i] = ysave2[i];
@@ -632,8 +632,8 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 			if (d < 1)
 				d = 1;
 			int rr = rtable[d];
-			xsave2[i] = centerX + (int)(((long long)s * rr) >> 7);
-			ysave2[i] = centerY + (int)(((long long)c * rr) >> 7);
+			xsave2[i] = centerX + (int)(((int64)s * rr) >> 7);
+			ysave2[i] = centerY + (int)(((int64)c * rr) >> 7);
 
 			// Draw new star position
 			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 0xFFFFFFFF);
@@ -666,10 +666,10 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 				if (d2 < 1)
 					d2 = 1;
 				int rr2 = rtable[d2];
-				int x1 = centerX + (int)(((long long)s * rr1) >> 7);
-				int y1 = centerY + (int)(((long long)c * rr1) >> 7);
-				int x2 = centerX + (int)(((long long)s * rr2) >> 7);
-				int y2 = centerY + (int)(((long long)c * rr2) >> 7);
+				int x1 = centerX + (int)(((int64)s * rr1) >> 7);
+				int y1 = centerY + (int)(((int64)c * rr1) >> 7);
+				int x2 = centerX + (int)(((int64)s * rr2) >> 7);
+				int y2 = centerY + (int)(((int64)c * rr2) >> 7);
 				_gfx->drawLine(x1, y1, x2, y2, 0xFFFFFFFF);
 			}
 		}
@@ -729,12 +729,12 @@ bool ColonyEngine::makeBlackHole() {
 			for (int j = 0; j < 256; j += 8) {
 				int idx = (j + starcnt) & 0xFF;
 				int rt1 = rtable[MIN(i + k, 1023)];
-				int x1 = centerX + (int)(((long long)rt1 * _sint[idx]) >> 7);
-				int y1 = centerY + (int)(((long long)rt1 * _cost[idx]) >> 7);
+				int x1 = centerX + (int)(((int64)rt1 * _sint[idx]) >> 7);
+				int y1 = centerY + (int)(((int64)rt1 * _cost[idx]) >> 7);
 
 				int rt2 = rtable[MIN(i + k + 8, 1023)];
-				int x2 = centerX + (int)(((long long)rt2 * _sint[idx]) >> 7);
-				int y2 = centerY + (int)(((long long)rt2 * _cost[idx]) >> 7);
+				int x2 = centerX + (int)(((int64)rt2 * _sint[idx]) >> 7);
+				int y2 = centerY + (int)(((int64)rt2 * _cost[idx]) >> 7);
 
 				_gfx->drawLine(x1, y1, x2, y2, 128);
 			}
@@ -781,8 +781,8 @@ bool ColonyEngine::makePlanet() {
 	for (int i = 800; i > 32 && starcnt < STAR_COUNT - 4; i -= 16) {
 		for (int m = 0; m < 4; m++) {
 			int sindex = _randomSource.getRandomNumber(255);
-			int xx = centerx + (int)(((long)rtable[i] * _sint[sindex]) >> 7);
-			int yy = centery + (int)(((long)rtable[i] * _cost[sindex]) >> 7);
+			int xx = centerx + (int)(((int32)rtable[i] * _sint[sindex]) >> 7);
+			int yy = centery + (int)(((int32)rtable[i] * _cost[sindex]) >> 7);
 			if (starcnt < STAR_COUNT) {
 				xstars[starcnt] = xx;
 				ystars[starcnt] = yy;
@@ -800,14 +800,14 @@ bool ColonyEngine::makePlanet() {
 	bool zsave[MAX_POINTS];
 	int start = 0, dstart = 1;
 
-	long rt = rtable[800];
+	int32 rt = rtable[800];
 	int save = 0;
 	for (int j = 0; j < 256; j += PDELTA) {
 		for (int k = start; k < 128; k += PDELTA) {
 			int xx = (int)(((rt * _sint[j]) >> 7) * _cost[k] >> 7);
 			int zz = (int)(((rt * _sint[j]) >> 7) * _sint[k] >> 7);
-			int y = (int)((((rt * _cost[j]) >> 7) * (long)costheta - (long)zz * sintheta) >> 7);
-			zz = (int)(((long)_cost[j] * sintheta + (long)zz * costheta) >> 7);
+			int y = (int)((((rt * _cost[j]) >> 7) * (int32)costheta - (int32)zz * sintheta) >> 7);
+			zz = (int)(((int32)_cost[j] * sintheta + (int32)zz * costheta) >> 7);
 			if (save < MAX_POINTS) {
 				zsave[save] = (zz >= 0);
 				if (zsave[save]) {
@@ -840,10 +840,10 @@ bool ColonyEngine::makePlanet() {
 				rt = rtable[800];
 				int xx = (int)(((rt * _sint[j]) >> 7) * _cost[l] >> 7);
 				int zz = (int)(((rt * _sint[j]) >> 7) * _sint[l] >> 7);
-				int z = (int)(((long)_cost[j] * sintheta + (long)zz * costheta) >> 7);
+				int z = (int)(((int32)_cost[j] * sintheta + (int32)zz * costheta) >> 7);
 				zsave[save] = (z >= 0);
 				if (zsave[save]) {
-					ysave[save] = centery + (int)((((rt * _cost[j]) >> 7) * (long)costheta - (long)zz * sintheta) >> 7);
+					ysave[save] = centery + (int)((((rt * _cost[j]) >> 7) * (int32)costheta - (int32)zz * sintheta) >> 7);
 					xsave[save] = xx + centerx;
 					_gfx->setPixel(xsave[save], ysave[save], 0xFFFFFFFF);
 				}
@@ -886,10 +886,10 @@ bool ColonyEngine::makePlanet() {
 				rt = rtable[i];
 				int xx = (int)(((rt * _sint[j]) >> 7) * _cost[l] >> 7);
 				int zz = (int)(((rt * _sint[j]) >> 7) * _sint[l] >> 7);
-				int z = (int)(((long)_cost[j] * sintheta + (long)zz * costheta) >> 7);
+				int z = (int)(((int32)_cost[j] * sintheta + (int32)zz * costheta) >> 7);
 				zsave[save] = (z >= 0);
 				if (zsave[save]) {
-					ysave[save] = centery + (int)((((rt * _cost[j]) >> 7) * (long)costheta - (long)zz * sintheta) >> 7);
+					ysave[save] = centery + (int)((((rt * _cost[j]) >> 7) * (int32)costheta - (int32)zz * sintheta) >> 7);
 					xsave[save] = xx + centerx;
 					_gfx->setPixel(xsave[save], ysave[save], 0xFFFFFFFF);
 				}
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 4a3245beb59..69037b06504 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -121,7 +121,7 @@ void projectTunnelPoint(const Common::Rect &rect, int pnt[2], int rox, int roy)
 
 	const int centerX = (rect.left + rect.right) >> 1;
 	const int centerY = (rect.top + rect.bottom) >> 1;
-	long p = centerX + (((long)rox) * 512) / roy;
+	int32 p = centerX + (((int32)rox) * 512) / roy;
 
 	if (p < -32000)
 		p = -32000;
@@ -282,8 +282,8 @@ void ColonyEngine::clampToDiagonalWalls(Locate *p) {
 		const int wx = p->xloc - obj.where.xloc;
 		const int wy = p->yloc - obj.where.yloc;
 		const uint8 invAng = (uint8)(0 - obj.where.ang);
-		const int lx = (int)(((long)wx * _cost[invAng] - (long)wy * _sint[invAng]) >> 7);
-		const int ly = (int)(((long)wx * _sint[invAng] + (long)wy * _cost[invAng]) >> 7);
+		const int lx = (int)(((int32)wx * _cost[invAng] - (int32)wy * _sint[invAng]) >> 7);
+		const int ly = (int)(((int32)wx * _sint[invAng] + (int32)wy * _cost[invAng]) >> 7);
 
 		// Also reject if clearly outside the cell (local coords span -128..128)
 		if (lx < -140 || lx > 140 || ly < -140 || ly > 140)
@@ -301,8 +301,8 @@ void ColonyEngine::clampToDiagonalWalls(Locate *p) {
 
 			// Transform back to world space
 			const uint8 ang = obj.where.ang;
-			p->xloc = obj.where.xloc + (int)(((long)nlx * _cost[ang] - (long)nly * _sint[ang]) >> 7);
-			p->yloc = obj.where.yloc + (int)(((long)nlx * _sint[ang] + (long)nly * _cost[ang]) >> 7);
+			p->xloc = obj.where.xloc + (int)(((int32)nlx * _cost[ang] - (int32)nly * _sint[ang]) >> 7);
+			p->yloc = obj.where.yloc + (int)(((int32)nlx * _sint[ang] + (int32)nly * _cost[ang]) >> 7);
 			p->xindex = p->xloc >> 8;
 			p->yindex = p->yloc >> 8;
 		} else { // kObjFWall — flat wall along the diagonal
@@ -315,8 +315,8 @@ void ColonyEngine::clampToDiagonalWalls(Locate *p) {
 			const int nly = ly + push;
 
 			const uint8 ang = obj.where.ang;
-			p->xloc = obj.where.xloc + (int)(((long)nlx * _cost[ang] - (long)nly * _sint[ang]) >> 7);
-			p->yloc = obj.where.yloc + (int)(((long)nlx * _sint[ang] + (long)nly * _cost[ang]) >> 7);
+			p->xloc = obj.where.xloc + (int)(((int32)nlx * _cost[ang] - (int32)nly * _sint[ang]) >> 7);
+			p->yloc = obj.where.yloc + (int)(((int32)nlx * _sint[ang] + (int32)nly * _cost[ang]) >> 7);
 			p->xindex = p->xloc >> 8;
 			p->yindex = p->yloc >> 8;
 		}
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 5094ab71442..df117484514 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -305,10 +305,10 @@ bool isSurfaceVisible(const int *surface, int pointCount, const int *screenX, co
 		const int cur = surface[i];
 		const int next = surface[(i + 1) % pointCount];
 		const int next2 = surface[(i + 2) % pointCount];
-		const long dx = screenX[cur] - screenX[next];
-		const long dy = screenY[cur] - screenY[next];
-		const long dxp = screenX[next2] - screenX[next];
-		const long dyp = screenY[next2] - screenY[next];
+		const int32 dx = screenX[cur] - screenX[next];
+		const int32 dy = screenY[cur] - screenY[next];
+		const int32 dxp = screenX[next2] - screenX[next];
+		const int32 dyp = screenY[next2] - screenY[next];
 
 		if (dx < 0) {
 			if (dy == 0) {
@@ -317,7 +317,7 @@ bool isSurfaceVisible(const int *surface, int pointCount, const int *screenX, co
 				if (dyp < 0)
 					return true;
 			} else {
-				const long b = dy * dxp - dx * dyp;
+				const int32 b = dy * dxp - dx * dyp;
 				if (b > 0)
 					return false;
 				if (b < 0)
@@ -330,7 +330,7 @@ bool isSurfaceVisible(const int *surface, int pointCount, const int *screenX, co
 				if (dyp > 0)
 					return true;
 			} else {
-				const long b = dx * dyp - dy * dxp;
+				const int32 b = dx * dyp - dy * dxp;
 				if (b < 0)
 					return false;
 				if (b > 0)
@@ -444,8 +444,8 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 	// +32 compensates for the original sine table's 45° phase offset.
 	// Object angles from game data were stored assuming that offset.
 	const uint8 ang = (useLook ? obj.where.look : obj.where.ang) + 32;
-	const long rotCos = _cost[ang];
-	const long rotSin = _sint[ang];
+	const int32 rotCos = _cost[ang];
+	const int32 rotSin = _sint[ang];
 	const bool lit = (_corePower[_coreIndex] > 0);
 	float transformedX[32];
 	float transformedY[32];
@@ -459,8 +459,8 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 		const int ox = def.points[i][0];
 		const int oy = def.points[i][1];
 		const int oz = def.points[i][2];
-		const long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
-		const long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+		const int32 rx = ((int32)ox * rotCos - (int32)oy * rotSin) >> 7;
+		const int32 ry = ((int32)ox * rotSin + (int32)oy * rotCos) >> 7;
 
 		transformedX[i] = (float)(rx + obj.where.xloc);
 		transformedY[i] = (float)(ry + obj.where.yloc);
@@ -627,8 +627,8 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 	// DOS DrawLeaf: draws leaf surfaces as lines (MoveTo/LineTo), not filled polygons.
 	// PenColor is set to vGREEN by the caller (MakePlant).
 	const uint8 ang = obj.where.ang + 32;
-	const long rotCos = _cost[ang];
-	const long rotSin = _sint[ang];
+	const int32 rotCos = _cost[ang];
+	const int32 rotSin = _sint[ang];
 	const bool lit = (_corePower[_coreIndex] > 0);
 	// Mac color: c_plant bg; Mac B&W: black; EGA: green; unlit: white/black
 	uint32 color;
@@ -653,8 +653,8 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 			int ox = def.points[cur][0];
 			int oy = def.points[cur][1];
 			int oz = def.points[cur][2];
-			long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
-			long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+			int32 rx = ((int32)ox * rotCos - (int32)oy * rotSin) >> 7;
+			int32 ry = ((int32)ox * rotSin + (int32)oy * rotCos) >> 7;
 			px[count] = (float)(rx + obj.where.xloc);
 			py[count] = (float)(ry + obj.where.yloc);
 			pz[count] = (float)(oz - 160);
@@ -676,19 +676,19 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 	// radius is the full pt0↔pt1 distance, not half.
 	// Rendered as a billboard polygon facing the camera.
 	const uint8 ang = obj.where.ang + 32;
-	const long rotCos = _cost[ang];
-	const long rotSin = _sint[ang];
+	const int32 rotCos = _cost[ang];
+	const int32 rotSin = _sint[ang];
 	const bool lit = (_corePower[_coreIndex] > 0);
 
 	// Transform both points to world space
-	long rx0 = ((long)pt0x * rotCos - (long)pt0y * rotSin) >> 7;
-	long ry0 = ((long)pt0x * rotSin + (long)pt0y * rotCos) >> 7;
+	int32 rx0 = ((int32)pt0x * rotCos - (int32)pt0y * rotSin) >> 7;
+	int32 ry0 = ((int32)pt0x * rotSin + (int32)pt0y * rotCos) >> 7;
 	float wx0 = (float)(rx0 + obj.where.xloc);
 	float wy0 = (float)(ry0 + obj.where.yloc);
 	float wz0 = (float)(pt0z - 160);
 
-	long rx1 = ((long)pt1x * rotCos - (long)pt1y * rotSin) >> 7;
-	long ry1 = ((long)pt1x * rotSin + (long)pt1y * rotCos) >> 7;
+	int32 rx1 = ((int32)pt1x * rotCos - (int32)pt1y * rotSin) >> 7;
+	int32 ry1 = ((int32)pt1x * rotSin + (int32)pt1y * rotCos) >> 7;
 	float wx1 = (float)(rx1 + obj.where.xloc);
 	float wy1 = (float)(ry1 + obj.where.yloc);
 	float wz1 = (float)(pt1z - 160);
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 4af2da3bee6..d4c94f91c50 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -125,10 +125,10 @@ bool isProjectedSurfaceVisible(const int *surface, int pointCount, const int *sc
 		const int cur = surface[i];
 		const int next = surface[(i + 1) % pointCount];
 		const int next2 = surface[(i + 2) % pointCount];
-		const long dx = screenX[cur] - screenX[next];
-		const long dy = screenY[cur] - screenY[next];
-		const long dxp = screenX[next2] - screenX[next];
-		const long dyp = screenY[next2] - screenY[next];
+		const int32 dx = screenX[cur] - screenX[next];
+		const int32 dy = screenY[cur] - screenY[next];
+		const int32 dxp = screenX[next2] - screenX[next];
+		const int32 dyp = screenY[next2] - screenY[next];
 
 		if (dx < 0) {
 			if (dy == 0) {
@@ -137,7 +137,7 @@ bool isProjectedSurfaceVisible(const int *surface, int pointCount, const int *sc
 				if (dyp < 0)
 					return true;
 			} else {
-				const long b = dy * dxp - dx * dyp;
+				const int32 b = dy * dxp - dx * dyp;
 				if (b > 0)
 					return false;
 				if (b < 0)
@@ -150,7 +150,7 @@ bool isProjectedSurfaceVisible(const int *surface, int pointCount, const int *sc
 				if (dyp > 0)
 					return true;
 			} else {
-				const long b = dx * dyp - dy * dxp;
+				const int32 b = dx * dyp - dy * dxp;
 				if (b < 0)
 					return false;
 				if (b > 0)
@@ -187,8 +187,8 @@ bool isProjectedPrismSurfaceVisible(const Common::Rect &screenR, const Colony::T
 		return false;
 
 	const uint8 ang = (useLook ? thing.where.look : thing.where.ang) + 32;
-	const long rotCos = cost[ang];
-	const long rotSin = sint[ang];
+	const int32 rotCos = cost[ang];
+	const int32 rotSin = sint[ang];
 	int projectedX[32];
 	int projectedY[32];
 	bool projected[32];
@@ -199,8 +199,8 @@ bool isProjectedPrismSurfaceVisible(const Common::Rect &screenR, const Colony::T
 		const int ox = def.points[i][0];
 		const int oy = def.points[i][1];
 		const int oz = def.points[i][2];
-		const long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
-		const long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+		const int32 rx = ((int32)ox * rotCos - (int32)oy * rotSin) >> 7;
+		const int32 ry = ((int32)ox * rotSin + (int32)oy * rotCos) >> 7;
 		projected[i] = projectCorridorPointRaw(screenR, cameraLook, cameraLookY, sint, cost, cameraX, cameraY,
 												   (float)(rx + thing.where.xloc), (float)(ry + thing.where.yloc), (float)(oz - 160),
 												   projectedX[i], projectedY[i]);
@@ -1159,10 +1159,10 @@ int wrapAngle256(int angle) {
 }
 
 void rotatePoint(int angle, const int src[3], int dst[3], const int *cost, const int *sint) {
-	const long tcos = cost[angle];
-	const long tsin = sint[angle];
-	dst[0] = (int)(((long)src[0] * tcos - (long)src[1] * tsin) >> 7);
-	dst[1] = (int)(((long)src[0] * tsin + (long)src[1] * tcos) >> 7);
+	const int32 tcos = cost[angle];
+	const int32 tsin = sint[angle];
+	dst[0] = (int)(((int32)src[0] * tcos - (int32)src[1] * tsin) >> 7);
+	dst[1] = (int)(((int32)src[0] * tsin + (int32)src[1] * tcos) >> 7);
 	dst[2] = src[2];
 }
 
@@ -1200,8 +1200,8 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 		return;
 
 	const uint8 ang = (useLook ? thing.where.look : thing.where.ang) + 32;
-	const long rotCos = _cost[ang];
-	const long rotSin = _sint[ang];
+	const int32 rotCos = _cost[ang];
+	const int32 rotSin = _sint[ang];
 	const bool lit = (_corePower[_coreIndex] > 0);
 	float transformedX[32];
 	float transformedY[32];
@@ -1216,8 +1216,8 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 		const int ox = def.points[i][0];
 		const int oy = def.points[i][1];
 		const int oz = def.points[i][2];
-		const long rx = ((long)ox * rotCos - (long)oy * rotSin) >> 7;
-		const long ry = ((long)ox * rotSin + (long)oy * rotCos) >> 7;
+		const int32 rx = ((int32)ox * rotCos - (int32)oy * rotSin) >> 7;
+		const int32 ry = ((int32)ox * rotSin + (int32)oy * rotCos) >> 7;
 		transformedX[i] = (float)(rx + thing.where.xloc);
 		transformedY[i] = (float)(ry + thing.where.yloc);
 		transformedZ[i] = (float)(oz - 160);
@@ -1689,12 +1689,12 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kRobQueen:
 		{
-			const long s1 = _sint[obj.where.ang] >> 1;
-			const long c1 = _cost[obj.where.ang] >> 1;
-			const long s2 = s1 >> 1;
-			const long c2 = c1 >> 1;
-			const long eyeBaseX = obj.where.xloc + c1;
-			const long eyeBaseY = obj.where.yloc + s1;
+			const int32 s1 = _sint[obj.where.ang] >> 1;
+			const int32 c1 = _cost[obj.where.ang] >> 1;
+			const int32 s2 = s1 >> 1;
+			const int32 c2 = c1 >> 1;
+			const int32 eyeBaseX = obj.where.xloc + c1;
+			const int32 eyeBaseY = obj.where.yloc + s1;
 
 			Thing leftEye = obj;
 			leftEye.where.xloc = (int)(eyeBaseX - s2);
@@ -1706,9 +1706,9 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			rightEye.where.yloc = (int)(eyeBaseY - c2);
 			resetObjectBounds(_screenR, rightEye.where);
 
-			const long leftDist = (leftEye.where.xloc - _me.xloc) * (leftEye.where.xloc - _me.xloc) +
+			const int32 leftDist = (leftEye.where.xloc - _me.xloc) * (leftEye.where.xloc - _me.xloc) +
 				(leftEye.where.yloc - _me.yloc) * (leftEye.where.yloc - _me.yloc);
-			const long rightDist = (rightEye.where.xloc - _me.xloc) * (rightEye.where.xloc - _me.xloc) +
+			const int32 rightDist = (rightEye.where.xloc - _me.xloc) * (rightEye.where.xloc - _me.xloc) +
 				(rightEye.where.yloc - _me.yloc) * (rightEye.where.yloc - _me.yloc);
 			const bool leftFirst = leftDist >= rightDist;
 			Thing &farEye = leftFirst ? leftEye : rightEye;
@@ -1752,12 +1752,12 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		{
 			// DOS DRONE.C: body + seteyes()/draweyes() — same two-eye
 			// system as Queen, positioned at offsets from body center.
-			const long s1 = _sint[obj.where.ang] >> 1;
-			const long c1 = _cost[obj.where.ang] >> 1;
-			const long s2 = s1 >> 1;
-			const long c2 = c1 >> 1;
-			const long eyeBaseX = obj.where.xloc + c1;
-			const long eyeBaseY = obj.where.yloc + s1;
+			const int32 s1 = _sint[obj.where.ang] >> 1;
+			const int32 c1 = _cost[obj.where.ang] >> 1;
+			const int32 s2 = s1 >> 1;
+			const int32 c2 = c1 >> 1;
+			const int32 eyeBaseX = obj.where.xloc + c1;
+			const int32 eyeBaseY = obj.where.yloc + s1;
 
 			Thing leftEye = obj;
 			leftEye.where.xloc = (int)(eyeBaseX - s2);
@@ -1800,12 +1800,12 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			const PrismPartDef leftPincerDef = {4, leftPincerPts, 4, kDLPincerSurf};
 			const PrismPartDef rightPincerDef = {4, rightPincerPts, 4, kDRPincerSurf};
 
-			const long s1 = _sint[obj.where.ang] >> 1;
-			const long c1 = _cost[obj.where.ang] >> 1;
-			const long s2 = s1 >> 1;
-			const long c2 = c1 >> 1;
-			const long eyeBaseX = obj.where.xloc + c1;
-			const long eyeBaseY = obj.where.yloc + s1;
+			const int32 s1 = _sint[obj.where.ang] >> 1;
+			const int32 c1 = _cost[obj.where.ang] >> 1;
+			const int32 s2 = s1 >> 1;
+			const int32 c2 = c1 >> 1;
+			const int32 eyeBaseX = obj.where.xloc + c1;
+			const int32 eyeBaseY = obj.where.yloc + s1;
 
 			Thing leftEye = obj;
 			leftEye.where.xloc = (int)(eyeBaseX - s2);
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index e97b89e57a7..b6e512b7a8a 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -743,18 +743,18 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 
 	int xcorner[6];
 	int ycorner[6];
-	xcorner[0] = ccenterx + (((long)xloc * tsin - (long)yloc * tcos) >> 8);
-	ycorner[0] = ccentery - (((long)yloc * tsin + (long)xloc * tcos) >> 8);
-	xcorner[1] = ccenterx + (((long)(xloc + lExt) * tsin - (long)yloc * tcos) >> 8);
-	ycorner[1] = ccentery - (((long)yloc * tsin + (long)(xloc + lExt) * tcos) >> 8);
-	xcorner[2] = ccenterx + (((long)(xloc + lExt) * tsin - (long)(yloc + lExt) * tcos) >> 8);
-	ycorner[2] = ccentery - (((long)(yloc + lExt) * tsin + (long)(xloc + lExt) * tcos) >> 8);
-	xcorner[3] = ccenterx + (((long)xloc * tsin - (long)(yloc + lExt) * tcos) >> 8);
-	ycorner[3] = ccentery - (((long)(yloc + lExt) * tsin + (long)xloc * tcos) >> 8);
-	xcorner[4] = ccenterx + (((long)(xloc + sExt) * tsin - (long)(yloc + sExt) * tcos) >> 8);
-	ycorner[4] = ccentery - (((long)(yloc + sExt) * tsin + (long)(xloc + sExt) * tcos) >> 8);
-	xcorner[5] = ccenterx + (((long)(xloc + sExt) * tsin - (long)yloc * tcos) >> 8);
-	ycorner[5] = ccentery - (((long)yloc * tsin + (long)(xloc + sExt) * tcos) >> 8);
+	xcorner[0] = ccenterx + (((int32)xloc * tsin - (int32)yloc * tcos) >> 8);
+	ycorner[0] = ccentery - (((int32)yloc * tsin + (int32)xloc * tcos) >> 8);
+	xcorner[1] = ccenterx + (((int32)(xloc + lExt) * tsin - (int32)yloc * tcos) >> 8);
+	ycorner[1] = ccentery - (((int32)yloc * tsin + (int32)(xloc + lExt) * tcos) >> 8);
+	xcorner[2] = ccenterx + (((int32)(xloc + lExt) * tsin - (int32)(yloc + lExt) * tcos) >> 8);
+	ycorner[2] = ccentery - (((int32)(yloc + lExt) * tsin + (int32)(xloc + lExt) * tcos) >> 8);
+	xcorner[3] = ccenterx + (((int32)xloc * tsin - (int32)(yloc + lExt) * tcos) >> 8);
+	ycorner[3] = ccentery - (((int32)(yloc + lExt) * tsin + (int32)xloc * tcos) >> 8);
+	xcorner[4] = ccenterx + (((int32)(xloc + sExt) * tsin - (int32)(yloc + sExt) * tcos) >> 8);
+	ycorner[4] = ccentery - (((int32)(yloc + sExt) * tsin + (int32)(xloc + sExt) * tcos) >> 8);
+	xcorner[5] = ccenterx + (((int32)(xloc + sExt) * tsin - (int32)yloc * tcos) >> 8);
+	ycorner[5] = ccentery - (((int32)yloc * tsin + (int32)(xloc + sExt) * tcos) >> 8);
 
 	const int dx = xcorner[1] - xcorner[0];
 	const int dy = ycorner[0] - ycorner[1];
@@ -870,8 +870,8 @@ bool isPassableFeature(int feat) {
 }
 
 void ColonyEngine::automapCellCorner(int dx, int dy, int xloc, int yloc, int lExt, int tsin, int tcos, int ccx, int ccy, int &sx, int &sy) {
-	const long ox = xloc + (long)dx * lExt;
-	const long oy = yloc + (long)dy * lExt;
+	const int32 ox = xloc + (int32)dx * lExt;
+	const int32 oy = yloc + (int32)dy * lExt;
 	sx = ccx + (int)((ox * tsin - oy * tcos) >> 8);
 	sy = ccy - (int)((oy * tsin + ox * tcos) >> 8);
 }


Commit: 3cb777b8632f76928a16de3a39fb78dbe07bd233
    https://github.com/scummvm/scummvm/commit/3cb777b8632f76928a16de3a39fb78dbe07bd233
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:58+02:00

Commit Message:
COLONY: use << for powers of two constants

Changed paths:
    engines/colony/movement.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 69037b06504..6eacfad4946 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -133,10 +133,10 @@ void projectTunnelPoint(const Common::Rect &rect, int pnt[2], int rox, int roy)
 }
 
 enum {
-	kTunnelClipLeft = 1,
-	kTunnelClipRight = 2,
-	kTunnelClipTop = 4,
-	kTunnelClipBottom = 8
+	kTunnelClipLeft = 1 << 0,
+	kTunnelClipRight = 1 << 1,
+	kTunnelClipTop = 1 << 2,
+	kTunnelClipBottom = 1 << 3
 };
 
 int tunnelClipCode(const Common::Rect &rect, int x, int y) {
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index b6e512b7a8a..bdaf623f395 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -1324,13 +1324,13 @@ bool ColonyEngine::clipLineToRect(int &x1, int &y1, int &x2, int &y2, const Comm
 	auto outCode = [&](int x, int y) {
 		int code = 0;
 		if (x < l)
-			code |= 1;
+			code |= (1 << 0);
 		else if (x > r)
-			code |= 2;
+			code |= (1 << 1);
 		if (y < t)
-			code |= 4;
+			code |= (1 << 2);
 		else if (y > b)
-			code |= 8;
+			code |= (1 << 3);
 		return code;
 	};
 


Commit: 5bfe039bca6596ec75ae0af9ae3edc3fcb42854c
    https://github.com/scummvm/scummvm/commit/5bfe039bca6596ec75ae0af9ae3edc3fcb42854c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:59+02:00

Commit Message:
COLONY: fix or document redundant cases in switches before a default

Changed paths:
    engines/colony/intro.cpp
    engines/colony/render_objects.cpp


diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 63c1b3c1417..a29c7705f84 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -1156,7 +1156,6 @@ void ColonyEngine::terminateGame(bool blowup) {
 					return;
 				}
 				break;
-			case Graphics::kMacDialogQuitRequested:
 			default:
 				quitGame();
 				return;
@@ -1184,7 +1183,6 @@ void ColonyEngine::terminateGame(bool blowup) {
 				return;
 			}
 			break;
-		case GUI::kMessageAlt + 1:
 		default:
 			quitGame();
 			return;
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index d4c94f91c50..04eae40cc01 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -79,7 +79,7 @@ int mapEyeOverlayColorToMacPattern(int colorIdx) {
 	case kColorIris:
 	case kColorEyeIris:
 	case kColorMiniEyeIris:
-	case kColorQueenEye:
+	case kColorQueenEye: // fallthrough: explicitly grouped with default
 	default:
 		return kPatternGray;
 	}


Commit: e6d163a6b8a48fe4b1f52e6bae3607112c1e9f64
    https://github.com/scummvm/scummvm/commit/e6d163a6b8a48fe4b1f52e6bae3607112c1e9f64
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:59+02:00

Commit Message:
COLONY: use int64 for some variables that can be large when calculating projects

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


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index df117484514..842a8ebf7be 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -317,7 +317,7 @@ bool isSurfaceVisible(const int *surface, int pointCount, const int *screenX, co
 				if (dyp < 0)
 					return true;
 			} else {
-				const int32 b = dy * dxp - dx * dyp;
+				const int64 b = (int64)dy * dxp - (int64)dx * dyp;
 				if (b > 0)
 					return false;
 				if (b < 0)
@@ -330,7 +330,7 @@ bool isSurfaceVisible(const int *surface, int pointCount, const int *screenX, co
 				if (dyp > 0)
 					return true;
 			} else {
-				const int32 b = dx * dyp - dy * dxp;
+				const int64 b = (int64)dx * dyp - (int64)dy * dxp;
 				if (b < 0)
 					return false;
 				if (b > 0)
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 04eae40cc01..fb058c62588 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -137,7 +137,7 @@ bool isProjectedSurfaceVisible(const int *surface, int pointCount, const int *sc
 				if (dyp < 0)
 					return true;
 			} else {
-				const int32 b = dy * dxp - dx * dyp;
+				const int64 b = (int64)dy * dxp - (int64)dx * dyp;
 				if (b > 0)
 					return false;
 				if (b < 0)
@@ -150,7 +150,7 @@ bool isProjectedSurfaceVisible(const int *surface, int pointCount, const int *sc
 				if (dyp > 0)
 					return true;
 			} else {
-				const int32 b = dx * dyp - dy * dxp;
+				const int64 b = (int64)dx * dyp - (int64)dy * dxp;
 				if (b < 0)
 					return false;
 				if (b > 0)


Commit: 649e76722ce69f9e136e0911039c3186f51a67fa
    https://github.com/scummvm/scummvm/commit/649e76722ce69f9e136e0911039c3186f51a67fa
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:31:59+02:00

Commit Message:
COLONY: added missing cases in some event loops

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


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 56773d8887c..e00dd15f723 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -47,6 +47,8 @@ void responsiveAnimationDelay(OSystem *system, uint32 delayMs) {
 	while (elapsed < delayMs) {
 		Common::Event event;
 		while (system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
 			if (event.type == Common::EVENT_MOUSEMOVE) {
 				mouseMoveCount++;
 			} else {
@@ -574,7 +576,12 @@ void ColonyEngine::playAnimation() {
 	while (_animationRunning && !shouldQuit()) {
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
-			if (event.type == Common::EVENT_LBUTTONDOWN) {
+			if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				_animationRunning = false;
+				return;
+			} else if (event.type == Common::EVENT_SCREEN_CHANGED) {
+				_gfx->computeScreenViewport();
+			} else if (event.type == Common::EVENT_LBUTTONDOWN) {
 				int item = whichSprite(event.mouse);
 				if (item > 0) {
 					handleAnimationClick(item);
@@ -1758,6 +1765,10 @@ void ColonyEngine::moveObject(int index) {
 		Common::Event event;
 		bool buttonDown = true;
 		while (_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (event.type == Common::EVENT_SCREEN_CHANGED)
+				_gfx->computeScreenViewport();
 			if (event.type == Common::EVENT_LBUTTONUP) {
 				buttonDown = false;
 				break;
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index e6c4d07e39e..de0ceb510ba 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -893,7 +893,11 @@ Common::Error ColonyEngine::run() {
 				}
 			}
 
-			if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
+			if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				return Common::kNoError;
+			} else if (event.type == Common::EVENT_SCREEN_CHANGED) {
+				_gfx->computeScreenViewport();
+			} else if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
 				switch (event.customType) {
 				case kActionMoveForward:
 					_moveForward = true;


Commit: 6a038d52eac5c3474a3e52801485bb5b15063307
    https://github.com/scummvm/scummvm/commit/6a038d52eac5c3474a3e52801485bb5b15063307
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:00+02:00

Commit Message:
COLONY: allow to access main menu during animation loop

Changed paths:
    engines/colony/animation.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index e00dd15f723..8f7834ab4a4 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -592,6 +592,11 @@ void ColonyEngine::playAnimation() {
 				_animationRunning = false;
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
 				debugC(5, kColonyDebugAnimation, "Animation Mouse: %d, %d", event.mouse.x, event.mouse.y);
+			} else if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
+				if (event.customType == kActionEscape) {
+					openMainMenuDialog();
+					_gfx->computeScreenViewport();
+				}
 			} else if (event.type == Common::EVENT_KEYDOWN) {
 				int item = 0;
 				if (event.kbd.keycode >= Common::KEYCODE_0 && event.kbd.keycode <= Common::KEYCODE_9) {
@@ -602,8 +607,6 @@ void ColonyEngine::playAnimation() {
 					item = 12; // Enter
 				} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE || event.kbd.keycode == Common::KEYCODE_DELETE) {
 					item = 11; // Clear
-				} else if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					_animationRunning = false;
 				}
 
 				if (item > 0) {


Commit: 1bb20696caf05b650f47c9bce9e193cec0e5dc43
    https://github.com/scummvm/scummvm/commit/1bb20696caf05b650f47c9bce9e193cec0e5dc43
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:00+02:00

Commit Message:
COLONY: renamed constants from mc_camerlCase to kMcCamelCase

Changed paths:
    engines/colony/animation.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 8f7834ab4a4..62717b0c49f 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -83,16 +83,16 @@ bool isBackdoorCode111111(const uint8 display[6]) {
 
 // Mac color indices from colordef.h enum (cColor[] table in Color256).
 enum {
-	mc_dwall = 6, mc_lwall = 7,
-	mc_char0 = 8,    // char0..char6 = 8..14
-	mc_bulkhead = 15, mc_door = 16,
-	mc_desk = 58, mc_desktop = 59, mc_screen = 62,
-	mc_proj = 72, mc_console = 79, mc_powerbase = 81,
-	mc_box1 = 84, mc_forklift = 86, mc_flglass = 87,
-	mc_cryo = 90, mc_ccore = 111,
-	mc_teleport = 93, mc_teledoor = 94,
-	mc_vanity = 96, mc_mirror = 103,
-	mc_airlock = 25, mc_elevator = 23
+	kMcDwall = 6, kMcLwall = 7,
+	kMcChar0 = 8,    // char0..char6 = 8..14
+	kMcBulkhead = 15, kMcDoor = 16,
+	kMcDesk = 58, kMcDesktop = 59, kMcScreen = 62,
+	kMcProj = 72, kMcConsole = 79, kMcPowerbase = 81,
+	kMcBox1 = 84, kMcForklift = 86, kMcFlglass = 87,
+	kMcCryo = 90, kMcCcore = 111,
+	kMcTeleport = 93, kMcTeledoor = 94,
+	kMcVanity = 96, kMcMirror = 103,
+	kMcAirlock = 25, kMcElevator = 23
 };
 
 // Mac Toolbox BackColor() constants.
@@ -107,21 +107,21 @@ enum {
 // Index 0 = background top, 1 = background image, 2+ = per-sprite fill.
 // Positive = cColor[] index, negative = -MacSystemColor, 0 = level-based.
 const int16 kBMC_Desk[] = {
-	0, mc_desktop,
+	0, kMcDesktop,
 	-kMacRed, -kMacCyan, -kMacCyan, -kMacCyan, -kMacCyan,
-	-kMacWhite, -kMacWhite, -kMacMagenta, -kMacYellow, mc_desk,
-	mc_desk, mc_desk, mc_desk, mc_desk, mc_desk,
-	-kMacWhite, mc_screen, -kMacMagenta, -kMacCyan, -kMacCyan,
+	-kMacWhite, -kMacWhite, -kMacMagenta, -kMacYellow, kMcDesk,
+	kMcDesk, kMcDesk, kMcDesk, kMcDesk, kMcDesk,
+	-kMacWhite, kMcScreen, -kMacMagenta, -kMacCyan, -kMacCyan,
 	-kMacBlue, -kMacWhite, -kMacRed, -kMacWhite, -kMacYellow
 };
 const int16 kBMC_Vanity[] = {
-	0, mc_vanity,
-	mc_mirror, -kMacRed, -kMacCyan, -kMacWhite, -kMacYellow,
+	0, kMcVanity,
+	kMcMirror, -kMacRed, -kMacCyan, -kMacWhite, -kMacYellow,
 	-kMacGreen, -kMacBlue, -kMacRed, -kMacMagenta, -kMacRed,
-	mc_vanity, -kMacWhite, -kMacYellow, mc_mirror
+	kMcVanity, -kMacWhite, -kMacYellow, kMcMirror
 };
 const int16 kBMC_Reactor[] = {
-	0, mc_console,
+	0, kMcConsole,
 	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
 	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
 	-kMacYellow, -kMacYellow, -kMacRed, -kMacRed, -kMacRed,
@@ -130,7 +130,7 @@ const int16 kBMC_Reactor[] = {
 	-kMacMagenta, -kMacWhite
 };
 const int16 kBMC_Security[] = {
-	0, mc_console,
+	0, kMcConsole,
 	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
 	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow,
 	-kMacYellow, -kMacYellow, -kMacRed, -kMacRed, -kMacRed,
@@ -139,42 +139,42 @@ const int16 kBMC_Security[] = {
 	-kMacRed, -kMacCyan, -kMacCyan, -kMacCyan, -kMacCyan
 };
 const int16 kBMC_Teleport[] = {
-	0, mc_teleport, 0, mc_teledoor
+	0, kMcTeleport, 0, kMcTeledoor
 };
 const int16 kBMC_Creatures[] = {
-	-kMacWhite, 0, -kMacWhite, -kMacCyan, mc_proj,
+	-kMacWhite, 0, -kMacWhite, -kMacCyan, kMcProj,
 	-kMacBlue, -kMacMagenta, -kMacMagenta
 };
 const int16 kBMC_Controls[] = {
-	0, mc_console,
-	-kMacRed, -kMacYellow, -kMacYellow, -kMacBlue, -kMacYellow, -kMacGreen, mc_screen
+	0, kMcConsole,
+	-kMacRed, -kMacYellow, -kMacYellow, -kMacBlue, -kMacYellow, -kMacGreen, kMcScreen
 };
 const int16 kBMC_Lift[] = {
-	0, mc_flglass,
-	mc_teleport, mc_box1, mc_cryo, mc_ccore, 0,
+	0, kMcFlglass,
+	kMcTeleport, kMcBox1, kMcCryo, kMcCcore, 0,
 	-kMacRed, -kMacRed, -kMacCyan, -kMacCyan
 };
 const int16 kBMC_Powersuit[] = {
-	0, mc_powerbase,
-	-kMacMagenta, -kMacMagenta, -kMacYellow, -kMacYellow, mc_powerbase, -kMacWhite
+	0, kMcPowerbase,
+	-kMacMagenta, -kMacMagenta, -kMacYellow, -kMacYellow, kMcPowerbase, -kMacWhite
 };
 const int16 kBMC_Forklift[] = {
-	0, mc_forklift, mc_forklift, mc_forklift
+	0, kMcForklift, kMcForklift, kMcForklift
 };
 const int16 kBMC_Door[] = {
-	0, mc_bulkhead, 0, mc_door, -kMacYellow
+	0, kMcBulkhead, 0, kMcDoor, -kMacYellow
 };
 const int16 kBMC_Bulkhead[] = {
-	0, mc_bulkhead, 0, mc_bulkhead, -kMacYellow
+	0, kMcBulkhead, 0, kMcBulkhead, -kMacYellow
 };
 const int16 kBMC_Airlock[] = {
-	0, mc_bulkhead, mc_bulkhead, -kMacRed, mc_airlock
+	0, kMcBulkhead, kMcBulkhead, -kMacRed, kMcAirlock
 };
 const int16 kBMC_Elevator[] = {
-	0, mc_bulkhead, 0, mc_elevator, mc_elevator, -kMacYellow
+	0, kMcBulkhead, 0, kMcElevator, kMcElevator, -kMacYellow
 };
 const int16 kBMC_Elevator2[] = {
-	0, mc_bulkhead, 0, -kMacMagenta, mc_elevator, mc_elevator,
+	0, kMcBulkhead, 0, -kMacMagenta, kMcElevator, kMcElevator,
 	-kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow, -kMacYellow
 };
 
@@ -675,8 +675,8 @@ uint32 ColonyEngine::resolveAnimColor(int16 bmEntry) const {
 		//   if(corepower[coreindex]) RGBBackColor(&cColor[c_char0+level-1].f);
 		//   else RGBBackColor(&cColor[c_dwall].b);
 		if (_corePower[_coreIndex] > 0 && _level >= 1 && _level <= 7)
-			return packMacColorBG(_macColors[mc_char0 + _level - 1].fg);
-		return packMacColorBG(_macColors[mc_dwall].bg);
+			return packMacColorBG(_macColors[kMcChar0 + _level - 1].fg);
+		return packMacColorBG(_macColors[kMcDwall].bg);
 	}
 }
 
@@ -701,7 +701,7 @@ void ColonyEngine::drawAnimation() {
 		const bool powered = (_corePower[_coreIndex] > 0);
 		uint32 topBG = resolveAnimColor(_animBMColors[0]);
 		// Bottom: only uses c_lwall.f when powered; unpowered inherits top color
-		uint32 botBG = powered ? packMacColorBG(_macColors[mc_lwall].fg) : topBG;
+		uint32 botBG = powered ? packMacColorBG(_macColors[kMcLwall].fg) : topBG;
 		for (int y = 0; y < 264; y++) {
 			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
 			byte row = pat[y % 8];


Commit: 5c178c4f6cea909b4720898fe87b519cec6226ab
    https://github.com/scummvm/scummvm/commit/5c178c4f6cea909b4720898fe87b519cec6226ab
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:01+02:00

Commit Message:
COLONY: renamed crypt -> cryptArray to avoid collisions

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


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 62717b0c49f..7231c7162f5 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -1198,7 +1198,7 @@ void ColonyEngine::handleKeypadClick(int item) {
 		uint8 testarray[6];
 		if (_animationName == "reactor") {
 			uint8 *decode = (_level == 1) ? _decode2 : _decode3;
-			crypt(testarray, decode[3] - 2, decode[2] - 2, decode[1] - 2, decode[0] - 2);
+			cryptArray(testarray, decode[3] - 2, decode[2] - 2, decode[1] - 2, decode[0] - 2);
 
 			debug("Reactor code check: decode=[%d,%d,%d,%d] expected=[%d,%d,%d,%d,%d,%d] entered=[%d,%d,%d,%d,%d,%d]",
 				decode[0], decode[1], decode[2], decode[3],
@@ -1233,7 +1233,7 @@ void ColonyEngine::handleKeypadClick(int item) {
 			}
 			_animationRunning = false;
 		} else if (_animationName == "security") {
-			crypt(testarray, _decode1[0] - 2, _decode1[1] - 2, _decode1[2] - 2, _decode1[3] - 2);
+			cryptArray(testarray, _decode1[0] - 2, _decode1[1] - 2, _decode1[2] - 2, _decode1[3] - 2);
 			bool match = true;
 			for (int i = 0; i < 6; i++) {
 				if (testarray[i] != _animDisplay[5 - i])
@@ -1982,7 +1982,7 @@ void ColonyEngine::refreshAnimationDisplay() {
 	}
 }
 
-void ColonyEngine::crypt(uint8 sarray[6], int i, int j, int k, int l) {
+void ColonyEngine::cryptArray(uint8 sarray[6], int i, int j, int k, int l) {
 	int res[6];
 	res[0] = ((3 * l) ^ i ^ j ^ k) % 10;
 	res[1] = ((i * 3) ^ (j * 7) ^ (k * 11) ^ (l * 13)) % 10;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index b1dc5824b4b..f376b2120ad 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -749,7 +749,7 @@ private:
 	int objectState(int num) const;
 	void setObjectOnOff(int num, bool on);
 	void refreshAnimationDisplay();
-	void crypt(uint8 sarray[6], int i, int j, int k, int l);
+	void cryptArray(uint8 sarray[6], int i, int j, int k, int l);
 	void terminateGame(bool blowup);
 
 	// think.c / shoot.c: colony robot AI, egg growth, and egg eating


Commit: f63a44b0b69e70ff3f9a4b7b32809dbb38a3b43c
    https://github.com/scummvm/scummvm/commit/f63a44b0b69e70ff3f9a4b7b32809dbb38a3b43c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:01+02:00

Commit Message:
COLONY: use debugChannelSet instead of gDebugLevel

Changed paths:
    engines/colony/animation.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 7231c7162f5..5c073017173 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -984,7 +984,7 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
 	}
 
 	// Dump accurately calculated bounds if debug is high enough
-	if (gDebugLevel >= 2) {
+	if (debugChannelSet(2, kColonyDebugAnimation)) {
 		for (int i = 0; i < (int)_lSprites.size(); i++) {
 			ComplexSprite *ls = _lSprites[i];
 			if (ls->onoff) {


Commit: f3f66ac3ea9b2863c6af37a49cacf80e2e884b92
    https://github.com/scummvm/scummvm/commit/f3f66ac3ea9b2863c6af37a49cacf80e2e884b92
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:01+02:00

Commit Message:
COLONY: use tag2str instead of typeNames

Changed paths:
    engines/colony/colony.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index de0ceb510ba..de49b997e3f 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -367,10 +367,6 @@ void ColonyEngine::loadMacCursorResources() {
 		MKTAG('C', 'U', 'R', 'S'),
 		MKTAG('c', 'r', 's', 'r')
 	};
-	const char *typeNames[] = {
-		"CURS",
-		"crsr"
-	};
 	const int cursorScale = getMacCursorScaleFactor(_system);
 	const byte *arrowData = nullptr;
 	const byte *arrowPalette = nullptr;
@@ -400,13 +396,13 @@ void ColonyEngine::loadMacCursorResources() {
 				continue;
 
 			warning("Colony cursor: found %s 1000 in %s resource fork (stream size=%u)",
-				typeNames[typeIdx], sources[sourceIdx].label, (uint)cursorStream->size());
+				tag2str(types[typeIdx]), sources[sourceIdx].label, (uint)cursorStream->size());
 
 			Graphics::MacCursor *cursor = new Graphics::MacCursor();
 			const bool forceCURSFormat = (types[typeIdx] == MKTAG('C', 'U', 'R', 'S'));
 			if (!cursor->readFromStream(*cursorStream, false, 0xff, forceCURSFormat)) {
 				warning("Colony cursor: failed to decode %s 1000 from %s resource fork",
-					typeNames[typeIdx], sources[sourceIdx].label);
+					tag2str(types[typeIdx]), sources[sourceIdx].label);
 				delete cursor;
 				cursor = nullptr;
 			} else {
@@ -415,7 +411,7 @@ void ColonyEngine::loadMacCursorResources() {
 				cursor = nullptr;
 				_macCrossCursor = scaledCursor;
 				warning("Colony cursor: loaded %s 1000 from %s resource fork at %dx scale (%ux%u hotspot=%u,%u)",
-					typeNames[typeIdx], sources[sourceIdx].label,
+					tag2str(types[typeIdx]), sources[sourceIdx].label,
 					cursorScale,
 					_macCrossCursor->getWidth(), _macCrossCursor->getHeight(),
 					_macCrossCursor->getHotspotX(), _macCrossCursor->getHotspotY());


Commit: ef2f3a5ddbe401b12346fc1c419cb4de706b4ce7
    https://github.com/scummvm/scummvm/commit/ef2f3a5ddbe401b12346fc1c419cb4de706b4ce7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:01+02:00

Commit Message:
COLONY: use kDesk enum instead of magic constants

Changed paths:
    engines/colony/animation.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 5c073017173..ab19c22dbe0 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -209,6 +209,21 @@ const AnimColorEntry kAnimColors[] = {
 	{ nullptr, nullptr, 0 }
 };
 
+// Desk animation sprite indices (from desk.pic ComplexSprite layout).
+enum {
+	kDeskTeeth = 6,
+	kDeskLetterEnv = 7,    // letter envelope
+	kDeskLetterOpen = 8,   // letter opened
+	kDeskClipboard = 9,
+	kDeskScreen = 17,
+	kDeskJackFirst = 18,   // jack-in-the-box sprites 18..21
+	kDeskJackLast = 21,
+	kDeskBook = 22,
+	kDeskCigarette1 = 23,
+	kDeskCigarette2 = 24,
+	kDeskPostIt = 25
+};
+
 // Convert Mac Toolbox BackColor constant to ARGB.
 uint32 macSysColorToARGB(int sysColor) {
 	switch (sysColor) {
@@ -449,13 +464,13 @@ void ColonyEngine::playAnimation() {
 		}
 
 		if (_action0 != 10) {
-			setObjectOnOff(23, false);
-			setObjectOnOff(24, false);
+			setObjectOnOff(kDeskCigarette1, false);
+			setObjectOnOff(kDeskCigarette2, false);
 		}
 		if (_action0 != 30)
-			setObjectOnOff(6, false); // Teeth
-		if (_action0 != 33) { // Jack-in-the-box
-			for (int i = 18; i <= 21; i++)
+			setObjectOnOff(kDeskTeeth, false);
+		if (_action0 != 33) {
+			for (int i = kDeskJackFirst; i <= kDeskJackLast; i++)
 				setObjectOnOff(i, false);
 		}
 
@@ -465,34 +480,34 @@ void ColonyEngine::playAnimation() {
 		case 1:
 		case 2:
 		case 3:
-			setObjectOnOff(7, false);
-			setObjectOnOff(8, false);
-			setObjectOnOff(9, false);
-			setObjectOnOff(22, false);
-			setObjectOnOff(25, false);
+			setObjectOnOff(kDeskLetterEnv, false);
+			setObjectOnOff(kDeskLetterOpen, false);
+			setObjectOnOff(kDeskClipboard, false);
+			setObjectOnOff(kDeskBook, false);
+			setObjectOnOff(kDeskPostIt, false);
 			break;
 		case 4: // letters
-			setObjectOnOff(22, false);
-			setObjectOnOff(9, false);
-			setObjectOnOff(25, false);
+			setObjectOnOff(kDeskBook, false);
+			setObjectOnOff(kDeskClipboard, false);
+			setObjectOnOff(kDeskPostIt, false);
 			break;
 		case 5: // book
-			setObjectOnOff(7, false);
-			setObjectOnOff(8, false);
-			setObjectOnOff(9, false);
-			setObjectOnOff(25, false);
+			setObjectOnOff(kDeskLetterEnv, false);
+			setObjectOnOff(kDeskLetterOpen, false);
+			setObjectOnOff(kDeskClipboard, false);
+			setObjectOnOff(kDeskPostIt, false);
 			break;
 		case 6: // clipboard
-			setObjectOnOff(22, false);
-			setObjectOnOff(7, false);
-			setObjectOnOff(8, false);
-			setObjectOnOff(25, false);
+			setObjectOnOff(kDeskBook, false);
+			setObjectOnOff(kDeskLetterEnv, false);
+			setObjectOnOff(kDeskLetterOpen, false);
+			setObjectOnOff(kDeskPostIt, false);
 			break;
 		case 7: // postit
-			setObjectOnOff(22, false);
-			setObjectOnOff(7, false);
-			setObjectOnOff(8, false);
-			setObjectOnOff(9, false);
+			setObjectOnOff(kDeskBook, false);
+			setObjectOnOff(kDeskLetterEnv, false);
+			setObjectOnOff(kDeskLetterOpen, false);
+			setObjectOnOff(kDeskClipboard, false);
 			break;
 		}
 	} else if (_animationName == "vanity") {
@@ -1091,19 +1106,19 @@ void ColonyEngine::handleDeskClick(int item) {
 			drawAnimation();
 			_gfx->copyToScreen();
 		}
-	} else if (item == 7) { // Letter
-		if (_lSprites[6]->current > 0)
+	} else if (item == kDeskLetterEnv) {
+		if (_lSprites[kDeskLetterEnv - 1]->current > 0)
 			doText(_action1, 0);
-	} else if (item == 9) { // Clipboard
+	} else if (item == kDeskClipboard) {
 		doText(_action1, 0);
-	} else if (item == 17) { // Screen
+	} else if (item == kDeskScreen) {
 		doText(_action0, 0);
-	} else if (item == 22) { // Book
+	} else if (item == kDeskBook) {
 		doText(_action1, 0);
-	} else if (item == 24) { // Cigarette
+	} else if (item == kDeskCigarette2) { // Cigarette
 		doText(55, 0);
 		terminateGame(false);
-	} else if (item == 25) { // Post-it
+	} else if (item == kDeskPostIt) {
 		doText(_action1, 0);
 	}
 }


Commit: 9d606a1a50aa60ef1af19c08d180758133b171a4
    https://github.com/scummvm/scummvm/commit/9d606a1a50aa60ef1af19c08d180758133b171a4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:02+02:00

Commit Message:
COLONY: converted comments to C++ style

Changed paths:
    engines/colony/render.cpp


diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 842a8ebf7be..dd1f6129792 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -359,8 +359,8 @@ void ColonyEngine::quadrant() {
 	int remain;
 	int quad;
 
-	quad = _me.look >> 6;				/*divide by 64		*/
-	remain = _me.look - (quad << 6);		/*multiply by 64	*/
+	quad = _me.look >> 6;				// divide by 64
+	remain = _me.look - (quad << 6);		// multiply by 64
 	_tsin = _sint[remain];
 	_tcos = _cost[remain];
 


Commit: c25219ba4ddcab6f10fd33eb25050dfee5902cf7
    https://github.com/scummvm/scummvm/commit/c25219ba4ddcab6f10fd33eb25050dfee5902cf7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:02+02:00

Commit Message:
COLONY: do not build this engine by default

Changed paths:
    engines/colony/configure.engine


diff --git a/engines/colony/configure.engine b/engines/colony/configure.engine
index ee31d680cfd..9cc4df07430 100644
--- a/engines/colony/configure.engine
+++ b/engines/colony/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
-add_engine colony "The Colony" yes "" "" "highres 16bit 3d" ""
+add_engine colony "The Colony" no "" "" "highres 16bit 3d" ""


Commit: 32fa446da769da893af12e47a061a7b4b17e9877
    https://github.com/scummvm/scummvm/commit/32fa446da769da893af12e47a061a7b4b17e9877
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:02+02:00

Commit Message:
COLONY: added CData into the search data and improved game detection

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/detection.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index ab19c22dbe0..bab733d9e50 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -272,8 +272,9 @@ bool ColonyEngine::loadAnimation(const Common::String &name) {
 		}
 	}
 
-	// DOS uses short names (suit.pic, elev.pic, etc.); Mac uses full names
-	// (spacesuit, elevator, etc.) without extensions, in a CData folder.
+	// DOS uses short names with .pic extension (suit.pic, elev.pic, etc.);
+	// Mac uses full names without extensions (spacesuit, elevator, etc.)
+	// stored in a CData folder (added to SearchMan at engine start).
 	const struct { const char *dosName; const char *macName; } nameMap[] = {
 		{ "suit",   "spacesuit" },
 		{ "elev",   "elevator" },
@@ -285,36 +286,17 @@ bool ColonyEngine::loadAnimation(const Common::String &name) {
 	Common::String fileName = name + ".pic";
 	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
 	if (!file) {
-		// Try lowercase (Mac resource fork)
-		fileName = name;
-		fileName.toLowercase();
-		file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
+		// Try without extension (Mac resource fork — SearchMan is case-agnostic)
+		file = Common::MacResManager::openFileOrDataFork(Common::Path(name));
 	}
 	if (!file) {
-		// Try Mac long name mapping
-		Common::String macName;
+		// Try Mac long name mapping (e.g. "suit" -> "spacesuit")
 		for (int i = 0; nameMap[i].dosName; i++) {
 			if (nameLower == nameMap[i].dosName) {
-				macName = nameMap[i].macName;
+				file = Common::MacResManager::openFileOrDataFork(Common::Path(nameMap[i].macName));
 				break;
 			}
 		}
-		if (!macName.empty())
-			file = Common::MacResManager::openFileOrDataFork(Common::Path(macName));
-	}
-	if (!file) {
-		// Try CData directory with both DOS and Mac names
-		fileName = "CData/" + nameLower;
-		file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
-		if (!file) {
-			for (int i = 0; nameMap[i].dosName; i++) {
-				if (nameLower == nameMap[i].dosName) {
-					fileName = Common::String("CData/") + nameMap[i].macName;
-					file = Common::MacResManager::openFileOrDataFork(Common::Path(fileName));
-					break;
-				}
-			}
-		}
 	}
 	if (!file) {
 		warning("Could not open animation file %s", name.c_str());
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index de49b997e3f..b744eda035d 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -25,6 +25,7 @@
  *
  */
 
+#include "common/archive.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/debug-channels.h"
@@ -301,19 +302,14 @@ void ColonyEngine::loadMacColors() {
 	Common::SeekableReadStream *file = nullptr;
 
 	// Try MacResManager first (for resource fork / AppleDouble files)
+	// CData/ is in SearchMan, so plain "Color256" finds it in either location.
 	Common::Path path("Color256");
 	file = Common::MacResManager::openFileOrDataFork(path);
-	if (!file) {
-		path = Common::Path("CData/Color256");
-		file = Common::MacResManager::openFileOrDataFork(path);
-	}
 
 	// Fallback to plain file open (for raw data files)
 	if (!file) {
 		Common::File *f = new Common::File();
-		if (f->open(Common::Path("Color256"))) {
-			file = f;
-		} else if (f->open(Common::Path("CData/Color256"))) {
+		if (f->open(path)) {
 			file = f;
 		} else {
 			delete f;
@@ -717,6 +713,11 @@ void ColonyEngine::startNewGame() {
 }
 
 Common::Error ColonyEngine::run() {
+	// Add CData subdirectory to search path so Mac animation files
+	// and color data are found without manual path prefixing.
+	const Common::FSNode gameDataDir(ConfMan.getPath("path"));
+	SearchMan.addSubDirectoryMatching(gameDataDir, "CData");
+
 	// Open Colony resource fork (must happen in run(), not constructor,
 	// because SearchMan doesn't have the game path until now)
 	if (getPlatform() == Common::kPlatformMacintosh) {
diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
index a15226a5a47..9accb58418c 100644
--- a/engines/colony/detection.cpp
+++ b/engines/colony/detection.cpp
@@ -60,8 +60,9 @@ const ADGameDescription gameDescriptions[] = {
 	{
 		"colony",
 		"",
-		AD_ENTRY2s("logo1.pic", "70d44e40ac19ea0413f1253b781399de", 6689,
-			"MAP.1", "ab40dc3d9658e8cdc0bee63c2ca9c79b", 3350),
+		AD_ENTRY3s("logo1.pic", "70d44e40ac19ea0413f1253b781399de", 6689,
+			"MAP.1", "ab40dc3d9658e8cdc0bee63c2ca9c79b", 3350,
+			"COL.EXE", "3fa09322759a47d817d2bda36aae9255", 250837),
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,


Commit: 995e9da2590578e0d9a72311701ad2fa86b5f87e
    https://github.com/scummvm/scummvm/commit/995e9da2590578e0d9a72311701ad2fa86b5f87e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:03+02:00

Commit Message:
COLONY: Renamed debugger.cpp/h -> console.cpp/h

Changed paths:
  A engines/colony/console.cpp
  A engines/colony/console.h
  R engines/colony/debugger.cpp
  R engines/colony/debugger.h
    engines/colony/colony.cpp
    engines/colony/module.mk


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index b744eda035d..e5993a6560d 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -41,7 +41,7 @@
 #include "graphics/paletteman.h"
 
 #include "colony/colony.h"
-#include "colony/debugger.h"
+#include "colony/console.h"
 #include "colony/renderer.h"
 
 namespace Colony {
diff --git a/engines/colony/debugger.cpp b/engines/colony/console.cpp
similarity index 99%
rename from engines/colony/debugger.cpp
rename to engines/colony/console.cpp
index 5f9cf592397..63835093b0b 100644
--- a/engines/colony/debugger.cpp
+++ b/engines/colony/console.cpp
@@ -26,7 +26,7 @@
  */
 
 #include "colony/colony.h"
-#include "colony/debugger.h"
+#include "colony/console.h"
 
 namespace Colony {
 
diff --git a/engines/colony/debugger.h b/engines/colony/console.h
similarity index 97%
rename from engines/colony/debugger.h
rename to engines/colony/console.h
index d7aa9c7b5a4..8a2b0630d9a 100644
--- a/engines/colony/debugger.h
+++ b/engines/colony/console.h
@@ -25,8 +25,8 @@
  *
  */
 
-#ifndef COLONY_DEBUGGER_H
-#define COLONY_DEBUGGER_H
+#ifndef COLONY_CONSOLE_H
+#define COLONY_CONSOLE_H
 
 #include "gui/debugger.h"
 
diff --git a/engines/colony/module.mk b/engines/colony/module.mk
index 0a3606dfef6..f2d1a05b782 100644
--- a/engines/colony/module.mk
+++ b/engines/colony/module.mk
@@ -4,7 +4,7 @@ MODULE_OBJS := \
 	animation.o \
 	battle.o \
 	colony.o \
-	debugger.o \
+	console.o \
 	gfx.o \
 	interaction.o \
 	intro.o \


Commit: dfdf4b3a7413a6294b5afa3d16b57564bab055bf
    https://github.com/scummvm/scummvm/commit/dfdf4b3a7413a6294b5afa3d16b57564bab055bf
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:03+02:00

Commit Message:
COLONY: added hasFeature with extended sabes and loading during startup

Changed paths:
    engines/colony/metaengine.cpp


diff --git a/engines/colony/metaengine.cpp b/engines/colony/metaengine.cpp
index 25b2cecdde1..1754c24a0e3 100644
--- a/engines/colony/metaengine.cpp
+++ b/engines/colony/metaengine.cpp
@@ -70,6 +70,11 @@ public:
 		return Common::kNoError;
 	}
 
+	bool hasFeature(MetaEngineFeature f) const override {
+		return checkExtendedSaves(f) ||
+			(f == kSupportsLoadingDuringStartup);
+	}
+
 	void getSavegameThumbnail(Graphics::Surface &thumb) override;
 	Common::KeymapArray initKeymaps(const char *target) const override;
 };


Commit: e726faa2cb487011febac54842c4ebe77331714b
    https://github.com/scummvm/scummvm/commit/e726faa2cb487011febac54842c4ebe77331714b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:03+02:00

Commit Message:
COLONY: added POTFILES

Changed paths:
  A engines/colony/POTFILES


diff --git a/engines/colony/POTFILES b/engines/colony/POTFILES
new file mode 100644
index 00000000000..f3a17c30b5e
--- /dev/null
+++ b/engines/colony/POTFILES
@@ -0,0 +1,2 @@
+engines/colony/metaengine.cpp
+engines/colony/savegame.cpp


Commit: 6f52d307a221a08ee3fce91480454aeecf9d9906
    https://github.com/scummvm/scummvm/commit/6f52d307a221a08ee3fce91480454aeecf9d9906
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:04+02:00

Commit Message:
COLONY: simplified loadMap loops conditions

Changed paths:
    engines/colony/map.cpp


diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 67c9ef7f4e8..e6e504c3d49 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -119,40 +119,41 @@ void ColonyEngine::loadMap(int mnum) {
 	for (int i = 0; i < 32; i++) {
 		for (int j = 0; j < 32; j++) {
 			_wall[i][j] = buffer[c++];
-			if (i < 31 && j < 31) {
-				for (int k = 0; k < 5; k++) {
-					if (_wall[i][j] & (1 << (k + 2))) {
-						for (int l = 0; l < 5; l++) {
-							_mapData[i][j][k][l] = buffer[c++];
-						}
-						// PACKIT.C: center feature type 6 marks static map objects.
-						if (k == 4 && _mapData[i][j][4][0] == 6 && i < 31 && j < 31) {
-							Thing obj;
-							clearThing(obj);
-							obj.alive = 1;
-							obj.visible = 0;
-							obj.type = _mapData[i][j][4][1] + kBaseObject;
-							obj.where.xloc = (i << 8) + 128;
-							obj.where.yloc = (j << 8) + 128;
-							obj.where.xindex = i;
-							obj.where.yindex = j;
-							obj.where.ang = (uint8)(_mapData[i][j][4][2] + 32);
-							obj.where.look = obj.where.ang;
-							if ((int)_objects.size() >= kMaxObjectSlots) {
-								warning("loadMap: object table full on level %d, skipping static object type %d at (%d,%d)",
-									mnum, obj.type, i, j);
-								continue;
-							}
-							_objects.push_back(obj);
-							const int objNum = (int)_objects.size(); // 1-based, DOS-style robot slots
-							// CWall/FWall use diagonal collision, not cell-based blocking.
-							if (obj.type != kObjFWall && obj.type != kObjCWall &&
-								objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
-								_robotArray[i][j] = (uint8)objNum;
+			if (i >= 31 || j >= 31)
+				continue;
+
+			for (int k = 0; k < 5; k++) {
+				if (_wall[i][j] & (1 << (k + 2))) {
+					for (int l = 0; l < 5; l++) {
+						_mapData[i][j][k][l] = buffer[c++];
+					}
+					// PACKIT.C: center feature type 6 marks static map objects.
+					if (k == 4 && _mapData[i][j][4][0] == 6) {
+						Thing obj;
+						clearThing(obj);
+						obj.alive = 1;
+						obj.visible = 0;
+						obj.type = _mapData[i][j][4][1] + kBaseObject;
+						obj.where.xloc = (i << 8) + 128;
+						obj.where.yloc = (j << 8) + 128;
+						obj.where.xindex = i;
+						obj.where.yindex = j;
+						obj.where.ang = (uint8)(_mapData[i][j][4][2] + 32);
+						obj.where.look = obj.where.ang;
+						if ((int)_objects.size() >= kMaxObjectSlots) {
+							warning("loadMap: object table full on level %d, skipping static object type %d at (%d,%d)",
+								mnum, obj.type, i, j);
+							continue;
 						}
-					} else {
-						_mapData[i][j][k][0] = 0;
+						_objects.push_back(obj);
+						const int objNum = (int)_objects.size(); // 1-based, DOS-style robot slots
+						// CWall/FWall use diagonal collision, not cell-based blocking.
+						if (obj.type != kObjFWall && obj.type != kObjCWall &&
+							objNum > 0 && objNum < 256 && _robotArray[i][j] == 0)
+							_robotArray[i][j] = (uint8)objNum;
 					}
+				} else {
+					_mapData[i][j][k][0] = 0;
 				}
 			}
 		}


Commit: 2ea2ed1f918e81a3a5893248116245637b5a9da7
    https://github.com/scummvm/scummvm/commit/2ea2ed1f918e81a3a5893248116245637b5a9da7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:04+02:00

Commit Message:
COLONY: removed CData code from path loading

Changed paths:
    engines/colony/map.cpp
    engines/colony/sound.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index e6e504c3d49..00786136d94 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -88,13 +88,8 @@ void ColonyEngine::loadMap(int mnum) {
 	Common::Path mapPath(Common::String::format("MAP.%d", mnum));
 	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(mapPath);
 	if (!file) {
-		// Try Mac-style path
-		mapPath = Common::Path(Common::String::format("CData/map.%d", mnum));
-		file = Common::MacResManager::openFileOrDataFork(mapPath);
-		if (!file) {
-			warning("Could not open map file %s", mapPath.toString().c_str());
-			return;
-		}
+		warning("Could not open map file %s", mapPath.toString().c_str());
+		return;
 	}
 
 	file->readUint32BE(); // "DAVE" header
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index b2ea6f277fe..1870bf6c615 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -44,9 +44,7 @@ void Sound::init() {
 	// Must be called from run() after SearchMan has the game data path.
 	_resMan = new Common::MacResManager();
 	if (!_resMan->open("Zounds")) {
-		if (!_resMan->open("CData/Zounds")) {
-			debugC(1, kColonyDebugSound, "Could not open Zounds resource file");
-		}
+		debugC(1, kColonyDebugSound, "Could not open Zounds resource file");
 	}
 
 	// Open Colony application binary (contains snd resources for
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index bdaf623f395..dcb1ceb28bf 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -1173,12 +1173,11 @@ void ColonyEngine::makeMessageRect(Common::Rect &rr) {
 
 void ColonyEngine::doText(int entry, int center) {
 	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(Common::Path("T.DAT"));
+	if (!file)
+		file = Common::MacResManager::openFileOrDataFork(Common::Path("Tdata"));
 	if (!file) {
-		file = Common::MacResManager::openFileOrDataFork(Common::Path("CData/Tdata"));
-		if (!file) {
-			warning("doText: Could not open text file");
-			return;
-		}
+		warning("doText: Could not open text file");
+		return;
 	}
 
 	uint32 entries = file->readUint32BE();


Commit: 287cb7008b8d6166c2a08a5eb8ac2ab8dbad6e05
    https://github.com/scummvm/scummvm/commit/287cb7008b8d6166c2a08a5eb8ac2ab8dbad6e05
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:04+02:00

Commit Message:
COLONY: minimize header deps in colony.h using forward declarations

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/intro.cpp
    engines/colony/map.cpp
    engines/colony/movement.cpp
    engines/colony/savegame.cpp
    engines/colony/think.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index bab733d9e50..f3acdd80461 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -28,11 +28,14 @@
 #include "common/debug.h"
 #include "common/events.h"
 #include "common/file.h"
+#include "common/macresman.h"
+#include "common/stream.h"
 #include "common/system.h"
 #include "graphics/cursorman.h"
 
 #include "colony/colony.h"
 #include "colony/renderer.h"
+#include "colony/sound.h"
 
 namespace Colony {
 
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index e5993a6560d..5a054f17fba 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -32,17 +32,22 @@
 #include "common/events.h"
 #include "common/file.h"
 #include "common/keyboard.h"
+#include "common/macresman.h"
 #include "common/system.h"
 #include "common/util.h"
 #include "engines/util.h"
 #include "graphics/cursorman.h"
+#include "graphics/framelimiter.h"
 #include "graphics/maccursor.h"
+#include "graphics/managed_surface.h"
+#include "graphics/macgui/macwindowmanager.h"
 #include "graphics/palette.h"
 #include "graphics/paletteman.h"
 
 #include "colony/colony.h"
 #include "colony/console.h"
 #include "colony/renderer.h"
+#include "colony/sound.h"
 
 namespace Colony {
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index f376b2120ad..40bb8e47f65 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -32,20 +32,29 @@
 #include "common/random.h"
 #include "common/rect.h"
 #include "common/rendermode.h"
-#include "common/stream.h"
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
-#include "graphics/cursor.h"
-#include "graphics/framelimiter.h"
-#include "graphics/macgui/macmenu.h"
-#include "graphics/macgui/macwindowmanager.h"
 
-#include "colony/renderer.h"
-#include "colony/sound.h"
+namespace Common {
+class MacResManager;
+class SeekableReadStreamEndian;
+}
 
+namespace Graphics {
+class Cursor;
+class Font;
+class FrameLimiter;
+class MacMenu;
+class MacWindowManager;
+class ManagedSurface;
+class Surface;
+}
 
 namespace Colony {
 
+class Renderer;
+class Sound;
+
 enum ColonyAction {
 	kActionNone,
 	kActionMoveForward,
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index a29c7705f84..75d60532974 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -27,6 +27,7 @@
 
 #include "common/debug.h"
 #include "common/events.h"
+#include "common/macresman.h"
 #include "common/system.h"
 #include "common/translation.h"
 #include "graphics/cursorman.h"
@@ -35,11 +36,14 @@
 #include "graphics/fonts/macfont.h"
 #include "graphics/macgui/macdialog.h"
 #include "graphics/macgui/mactext.h"
+#include "graphics/macgui/macwindowmanager.h"
+#include "graphics/managed_surface.h"
 #include "gui/message.h"
 #include "image/pict.h"
 
 #include "colony/colony.h"
 #include "colony/renderer.h"
+#include "colony/sound.h"
 
 namespace Colony {
 
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 00786136d94..538cd08c28d 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -27,6 +27,7 @@
 
 #include "common/debug.h"
 #include "common/file.h"
+#include "common/macresman.h"
 
 #include "colony/colony.h"
 
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 6eacfad4946..667231e777b 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -30,6 +30,7 @@
 
 #include "colony/colony.h"
 #include "colony/renderer.h"
+#include "colony/sound.h"
 
 namespace Colony {
 
diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
index 330f6fb1066..4dec96aa614 100644
--- a/engines/colony/savegame.cpp
+++ b/engines/colony/savegame.cpp
@@ -21,6 +21,7 @@
  *
  */
 
+#include "common/stream.h"
 #include "common/translation.h"
 
 #include "colony/colony.h"
diff --git a/engines/colony/think.cpp b/engines/colony/think.cpp
index bd3d1ce829f..3c4233c0637 100644
--- a/engines/colony/think.cpp
+++ b/engines/colony/think.cpp
@@ -28,6 +28,7 @@
 #include "common/debug.h"
 
 #include "colony/colony.h"
+#include "colony/sound.h"
 
 namespace Colony {
 
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index dcb1ceb28bf..22fb2d60b7b 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -28,17 +28,20 @@
 #include "common/debug.h"
 #include "common/events.h"
 #include "common/file.h"
+#include "common/macresman.h"
 #include "common/system.h"
 #include "common/util.h"
 #include "graphics/fontman.h"
 #include "graphics/fonts/dosfont.h"
 #include "graphics/macgui/macfontmanager.h"
 #include "graphics/macgui/macwindowborder.h"
+#include "graphics/macgui/macwindowmanager.h"
 #include "graphics/palette.h"
 #include "image/pict.h"
 
 #include "colony/colony.h"
 #include "colony/renderer.h"
+#include "colony/sound.h"
 
 namespace Colony {
 


Commit: 742eb9f04ddaa17422e4b064e18cbcdfe8d2209d
    https://github.com/scummvm/scummvm/commit/742eb9f04ddaa17422e4b064e18cbcdfe8d2209d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:05+02:00

Commit Message:
COLONY: fixed CI issues: struct/class mismatch and double-to-float truncation

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 6bf4802dcf6..5ec5031274b 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -792,10 +792,10 @@ void ColonyEngine::battleDrawTanks() {
 			// on top of the coplanar entrance wall (same fill color,
 			// but the black outline becomes clearly visible).
 			_gfx->setDepthState(true, false);
-			_gfx->setDepthRange(0.0, 0.999);
+			_gfx->setDepthRange(0.0f, 0.999f);
 			draw3DBattlePrism(kEntDoorDef, _battleEnter.xloc, _battleEnter.yloc,
 				_battleEnter.ang, -kFloor);
-			_gfx->setDepthRange(0.0, 1.0);
+			_gfx->setDepthRange(0.0f, 1.0f);
 			_gfx->setDepthState(true, true);
 			if (_battleMaxP < 100) {
 				_battlePwh[_battleMaxP] = &_battleEnter;
@@ -847,10 +847,10 @@ void ColonyEngine::battleDrawTanks() {
 			draw3DBattlePrism(kFRightDef, _battleShip.xloc, _battleShip.yloc,
 				_battleShip.ang, -kFloor);
 			_gfx->setDepthState(true, false);
-			_gfx->setDepthRange(0.0, 0.999);
+			_gfx->setDepthRange(0.0f, 0.999f);
 			draw3DBattlePrism(kSDoorDef, _battleShip.xloc, _battleShip.yloc,
 				_battleShip.ang, -kFloor);
-			_gfx->setDepthRange(0.0, 1.0);
+			_gfx->setDepthRange(0.0f, 1.0f);
 			_gfx->setDepthState(true, true);
 			if (_battleMaxP < 100) {
 				_battlePwh[_battleMaxP] = &_battleShip;
@@ -1196,7 +1196,7 @@ void ColonyEngine::renderBattle() {
 
 	// Phase 4: 3D objects with depth testing.
 	_gfx->setDepthState(true, true);
-	_gfx->setDepthRange(0.0, 1.0);
+	_gfx->setDepthRange(0.0f, 1.0f);
 
 	// Draw pyramids (obstacles)
 	battleDrawPyramids();
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 40bb8e47f65..5ba166a3a7c 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -47,7 +47,7 @@ class FrameLimiter;
 class MacMenu;
 class MacWindowManager;
 class ManagedSurface;
-class Surface;
+struct Surface;
 }
 
 namespace Colony {
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index dd1f6129792..d40a6de5edf 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -942,7 +942,7 @@ void ColonyEngine::renderCorridor3D() {
 	// --- Phase 2: Walls ---
 	// Depth test + write enabled. Pushed-back depth range so features/objects beat walls.
 	_gfx->setDepthState(true, true);
-	_gfx->setDepthRange(0.01, 1.0);
+	_gfx->setDepthRange(0.01f, 1.0f);
 	_gfx->setWireframe(true, wallFill);
 
 	for (int y = 0; y < 32; y++) {
@@ -960,12 +960,12 @@ void ColonyEngine::renderCorridor3D() {
 	// --- Phase 3: Wall & cell features ---
 	// Closer depth range than walls  features always beat their own wall surface.
 	// Depth test still active so far-away features are hidden behind nearer walls.
-	_gfx->setDepthRange(0.005, 1.0);
+	_gfx->setDepthRange(0.005f, 1.0f);
 	drawWallFeatures3D();
 
 	// --- Phase 4: Objects ---
 	// Full depth range  objects beat walls and features at the same distance.
-	_gfx->setDepthRange(0.0, 1.0);
+	_gfx->setDepthRange(0.0f, 1.0f);
 
 	// F7 toggles object fill.
 	// EGA: default is filled (wall background); F7 = outline-only (see-through).
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index fb058c62588..a13f4076449 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1337,12 +1337,12 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kObjCChair:
 		for (int i = 0; i < 5; i++) {
-			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((4 - i) * 0.002f, 1.0f);
 			// Flat quad parts (seat, arms, back) need forceVisible to avoid
 			// edge-on culling; box base (i==4) uses normal culling.
 			draw3DPrism(obj, kCChairParts[i], false, -1, true, i < 4);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kObjPlant:
 		// DOS MakePlant draw order: top pot, then green leaf lines, then pot on top.
@@ -1355,24 +1355,24 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	case kObjChair: {
 		const PrismPartDef *parts = (obj.type == kObjCouch) ? kCouchParts : kChairParts;
 		for (int i = 0; i < 4; i++) {
-			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((3 - i) * 0.002f, 1.0f);
 			draw3DPrism(obj, parts[i], false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	}
 	case kObjTV:
-		_gfx->setDepthRange(0.002, 1.0); // body base layer (pushed back)
+		_gfx->setDepthRange(0.002f, 1.0f); // body base layer (pushed back)
 		draw3DPrism(obj, kTVParts[0], false, -1, true, false);
-		_gfx->setDepthRange(0.0, 1.0);   // screen on top of body face
+		_gfx->setDepthRange(0.0f, 1.0f);   // screen on top of body face
 		draw3DPrism(obj, kTVParts[1], false, -1, true, false);
 		break;
 	case kObjDrawer:
 		for (int i = 0; i < 2; i++) {
-			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((1 - i) * 0.002f, 1.0f);
 			draw3DPrism(obj, kDrawerParts[i], false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kObjFWall:
 		if (_renderMode == Common::kRenderMacintosh)
@@ -1391,27 +1391,27 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kObjTable:
 		for (int i = 0; i < 2; i++) {
-			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((1 - i) * 0.002f, 1.0f);
 			draw3DPrism(obj, kTableParts[i], false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kObjBed:
 	case kObjBBed: {
 		const PrismPartDef *parts = (obj.type == kObjBBed) ? kBBedParts : kBedParts;
 		for (int i = 0; i < 3; i++) {
-			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((2 - i) * 0.002f, 1.0f);
 			draw3DPrism(obj, parts[i], false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	}
 	case kObjDesk:
 		for (int i = 0; i < 10; i++) {
-			_gfx->setDepthRange((9 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((9 - i) * 0.002f, 1.0f);
 			draw3DPrism(obj, kDeskParts[i], false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kObjBox1:
 		draw3DPrism(obj, kBox1Part, false, -1, true, false);
@@ -1421,15 +1421,15 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kObjCBench:
 		for (int i = 0; i < 2; i++) {
-			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((1 - i) * 0.002f, 1.0f);
 			draw3DPrism(obj, kCBenchParts[i], false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kObjBox2:
-		_gfx->setDepthRange(0.002, 1.0);
+		_gfx->setDepthRange(0.002f, 1.0f);
 		draw3DPrism(obj, kBox2Parts[1], false, -1, true, false); // base first
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		draw3DPrism(obj, kBox2Parts[0], false, -1, true, false); // top second
 		break;
 	case kObjReactor: {
@@ -1500,15 +1500,15 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 
 		// Depth separation matches the original draw order closely, but the
 		// per-face colors now follow MakeReactor() exactly.
-		_gfx->setDepthRange(0.004, 1.0);
+		_gfx->setDepthRange(0.004f, 1.0f);
 		draw3DPrism(obj, modTopDef, false, -1, true, false);
-		_gfx->setDepthRange(0.002, 1.0);
+		_gfx->setDepthRange(0.002f, 1.0f);
 		draw3DPrism(obj, modRingDef, false, -1, true, false);
 		if (_coreState[_coreIndex] < 2) {
-			_gfx->setDepthRange(0.0, 1.0);
+			_gfx->setDepthRange(0.0f, 1.0f);
 			draw3DPrism(obj, modCoreDef, false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	}
 	case kObjPowerSuit: {
@@ -1518,10 +1518,10 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		int sourceColor = kSuitCycle[_displayCount % 6];
 
 		for (int i = 0; i < 4; i++) {
-			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((4 - i) * 0.002f, 1.0f);
 			draw3DPrism(obj, kPowerSuitParts[i], false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		draw3DPrism(obj, kPowerSuitParts[4], false, sourceColor, true, false);
 		break;
 	}
@@ -1529,9 +1529,9 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		draw3DPrism(obj, kTelePart, false, -1, true, false);
 		break;
 	case kObjCryo:
-		_gfx->setDepthRange(0.002, 1.0);
+		_gfx->setDepthRange(0.002f, 1.0f);
 		draw3DPrism(obj, kCryoParts[1], false, -1, true, false); // base first
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		draw3DPrism(obj, kCryoParts[0], false, -1, true, false); // top second
 		break;
 	case kObjProjector: {
@@ -1541,66 +1541,66 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		// depth range bucket rather than true inter-part depth sorting.
 		const bool lensFrontVisible = isProjectedPrismSurfaceVisible(_screenR, obj, kProjectorParts[2], false, 6,
 															_me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
-		_gfx->setDepthRange(0.008, 1.0);
+		_gfx->setDepthRange(0.008f, 1.0f);
 		draw3DPrism(obj, kTableParts[1], false, -1, true, false); // table base
-		_gfx->setDepthRange(0.006, 1.0);
+		_gfx->setDepthRange(0.006f, 1.0f);
 		draw3DPrism(obj, kTableParts[0], false, -1, true, false); // table top
-		_gfx->setDepthRange(0.004, 1.0);
+		_gfx->setDepthRange(0.004f, 1.0f);
 		draw3DPrism(obj, kProjectorParts[1], false, -1, true, true); // stand
 		if (lensFrontVisible) {
-			_gfx->setDepthRange(0.002, 1.0);
+			_gfx->setDepthRange(0.002f, 1.0f);
 			draw3DPrism(obj, kProjectorParts[0], false, -1, true, false); // body
-			_gfx->setDepthRange(0.0, 1.0);
+			_gfx->setDepthRange(0.0f, 1.0f);
 			draw3DPrism(obj, kProjectorParts[2], false, -1, true, false); // lens
 		} else {
-			_gfx->setDepthRange(0.002, 1.0);
+			_gfx->setDepthRange(0.002f, 1.0f);
 			draw3DPrism(obj, kProjectorParts[2], false, -1, true, false); // lens
-			_gfx->setDepthRange(0.0, 1.0);
+			_gfx->setDepthRange(0.0f, 1.0f);
 			draw3DPrism(obj, kProjectorParts[0], false, -1, true, false); // body
 		}
 		break;
 	}
 	case kObjTub:
 		for (int i = 0; i < 2; i++) {
-			_gfx->setDepthRange((1 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((1 - i) * 0.002f, 1.0f);
 			draw3DPrism(obj, kTubParts[i], false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kObjSink:
 		for (int i = 0; i < 3; i++) {
-			_gfx->setDepthRange((2 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((2 - i) * 0.002f, 1.0f);
 			draw3DPrism(obj, kSinkParts[i], false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kObjToilet:
 		for (int i = 0; i < 4; i++) {
-			_gfx->setDepthRange((3 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((3 - i) * 0.002f, 1.0f);
 			draw3DPrism(obj, kToiletParts[i], false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kObjPToilet:
 		for (int i = 0; i < 5; i++) {
-			_gfx->setDepthRange((4 - i) * 0.002, 1.0);
+			_gfx->setDepthRange((4 - i) * 0.002f, 1.0f);
 			draw3DPrism(obj, kPToiletParts[i], false, -1, true, false);
 		}
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kObjForkLift:
 		// Draw order: forks, arms, treads, cab (back-to-front)
-		_gfx->setDepthRange(0.010, 1.0);
+		_gfx->setDepthRange(0.010f, 1.0f);
 		draw3DPrism(obj, kForkliftParts[3], false, -1, true, false); // FLLL (left fork)
-		_gfx->setDepthRange(0.008, 1.0);
+		_gfx->setDepthRange(0.008f, 1.0f);
 		draw3DPrism(obj, kForkliftParts[2], false, -1, true, false); // FLUL (left arm)
-		_gfx->setDepthRange(0.006, 1.0);
+		_gfx->setDepthRange(0.006f, 1.0f);
 		draw3DPrism(obj, kForkliftParts[5], false, -1, true, false); // FLLR (right fork)
-		_gfx->setDepthRange(0.004, 1.0);
+		_gfx->setDepthRange(0.004f, 1.0f);
 		draw3DPrism(obj, kForkliftParts[4], false, -1, true, false); // FLUR (right arm)
-		_gfx->setDepthRange(0.002, 1.0);
+		_gfx->setDepthRange(0.002f, 1.0f);
 		draw3DPrism(obj, kForkliftParts[1], false, -1, true, false); // treads
-		_gfx->setDepthRange(0.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		draw3DPrism(obj, kForkliftParts[0], false, -1, true, false); // cab
 		break;
 	// === Robot types (1-20) ===
@@ -1613,17 +1613,17 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		drawEyeOverlays3D(obj, kEyeIrisDef, -1, kEyePupilDef, pupilColor, false);
 		break;
 	case kRobPyramid:
-		_gfx->setDepthRange(0.030, 1.0);
+		_gfx->setDepthRange(0.030f, 1.0f);
 		draw3DPrism(obj, kPShadowDef, false, -1, true, false);
-		_gfx->setDepthRange(0.020, 1.0);
+		_gfx->setDepthRange(0.020f, 1.0f);
 		draw3DPrism(obj, kPyramidBodyDef, false, -1, true, false);
-		_gfx->setDepthRange(0.004, 1.0);
+		_gfx->setDepthRange(0.004f, 1.0f);
 		_gfx->setDepthState(true, false);
-		_gfx->setDepthRange(0.0, 1.0);
+		_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.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kRobCube:
 		// DOS CUBE.C: body + draweye() (same shared eye as Pyramid/UPyramid)
@@ -1636,17 +1636,17 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	case kRobUPyramid:
 		// DOS UPYRAMID.C: draweye() drawn first, then shadow, then body.
 		// Same eye geometry as Pyramid (shared draweye function).
-		_gfx->setDepthRange(0.030, 1.0);
+		_gfx->setDepthRange(0.030f, 1.0f);
 		draw3DPrism(obj, kUPShadowDef, false, -1, true, false);
-		_gfx->setDepthRange(0.020, 1.0);
+		_gfx->setDepthRange(0.020f, 1.0f);
 		draw3DPrism(obj, kUPyramidBodyDef, false, -1, true, false);
-		_gfx->setDepthRange(0.004, 1.0);
+		_gfx->setDepthRange(0.004f, 1.0f);
 		_gfx->setDepthState(true, false);
-		_gfx->setDepthRange(0.0, 1.0);
+		_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.0, 1.0);
+		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kRobFEye:
 		draw3DSphere(obj, 0, 0, 0, 0, 0, 100, eyeballColor, kColorBlack, true);


Commit: 0d4beef4a6073c39fa54dba989e64d74353443d2
    https://github.com/scummvm/scummvm/commit/0d4beef4a6073c39fa54dba989e64d74353443d2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:05+02:00

Commit Message:
COLONY: added comment regarding framebuffer

Changed paths:
    engines/colony/animation.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index f3acdd80461..694def0a7b1 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -681,6 +681,7 @@ uint32 ColonyEngine::resolveAnimColor(int16 bmEntry) const {
 }
 
 void ColonyEngine::drawAnimation() {
+	// Full clear required: OpenGL framebuffer is undefined between frames.
 	_gfx->clear(0);
 
 	// Center 416x264 animation area on screen (from original InitDejaVu)


Commit: ce6162b4409fced3d546ca6f49c27923514e20a3
    https://github.com/scummvm/scummvm/commit/ce6162b4409fced3d546ca6f49c27923514e20a3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-03T15:32:05+02:00

Commit Message:
AUDIO: fix PC speaker divide-by-zero in frequency calculation

Changed paths:
    audio/softsynth/pcspk.cpp


diff --git a/audio/softsynth/pcspk.cpp b/audio/softsynth/pcspk.cpp
index 0caaa9eda1f..c356eabf083 100644
--- a/audio/softsynth/pcspk.cpp
+++ b/audio/softsynth/pcspk.cpp
@@ -118,7 +118,7 @@ int PCSpeakerStream::readBuffer(int16 *buffer, const int numSamples) {
 			// Note that this will end playback started by the play method.
 			Command command = _commandQueue->pop();
 			_wave = command.waveForm;
-			_oscLength = (uint32)(_rate / command.frequency);
+			_oscLength = command.frequency > 0 ? (uint32)(_rate / command.frequency) : 0;
 			_oscSamples = 0;
 			// Length is in microseconds.
 			_remainingSamples = ((uint64)_rate * (uint64)command.length) / 1000000;




More information about the Scummvm-git-logs mailing list