[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