[Scummvm-git-logs] scummvm master -> b398c8b1942442a47cfada35d6cce8d3f54943c0
neuromancer
noreply at scummvm.org
Tue Oct 20 11:27:23 UTC 2026
This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
9d816efb68 COLONY: corrected mac b&w intro
6b97d8529e COLONY: avoid fractional scaling in widescreen
fbe4c3482f COLONY: avoid camera of getting too close to objects
b398c8b194 COLONY: show UI when player interacts with an animation
Commit: 9d816efb68101cc8a94b9af8a0946f153c160634
https://github.com/scummvm/scummvm/commit/9d816efb68101cc8a94b9af8a0946f153c160634
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-20T13:26:51+02:00
Commit Message:
COLONY: corrected mac b&w intro
Changed paths:
engines/colony/intro.cpp
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index d65a727e5ad..44ce75aba05 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -645,12 +645,12 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
// Set up gradient palette entries (200-213) for story text
// Mac original: tColor.blue starts at 0xFFFF and decreases by 4096 per visible line
- // B&W Mac: white gradient instead of blue
- const bool bwMac = (macFont && !isMacColorMode());
+ // B&W Mac uses solid white text.
+ const bool bwMac = isMacRenderMode() && !isMacColorMode();
byte pal[14 * 3]; // storyLength entries
memset(pal, 0, sizeof(pal));
for (int i = 0; i < storyLength; i++) {
- int val = 255 - i * 16;
+ int val = bwMac ? 255 : 255 - i * 16;
if (val < 0)
val = 0;
pal[i * 3 + 0] = bwMac ? val : 0; // R
@@ -881,10 +881,11 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
bool ColonyEngine::makeBlackHole() {
// Original: Makeblackhole() in intro.c
- // Mac original draws spiral lines with fading colors:
+ // Color 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.
+ // Palette entry 128 holds the current pen color.
+ const bool bwMac = isMacRenderMode() && !isMacColorMode();
int centerX = _width / 2;
int centerY = _height / 2;
int dec = 16;
@@ -922,7 +923,11 @@ bool ColonyEngine::makeBlackHole() {
uint8 palB = (colorB >> 8) & 0xFF;
// Use palette entry 128 for current step color
- byte pal[3] = { palR, palG, palB };
+ byte pal[3] = {
+ (byte)(bwMac ? 255 : palR),
+ (byte)(bwMac ? 255 : palG),
+ (byte)(bwMac ? 255 : palB)
+ };
_gfx->setPalette(pal, 128, 1);
starcnt++;
@@ -1322,8 +1327,8 @@ bool ColonyEngine::explodePlanet() {
bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *macFont, bool gameOver) {
// Original: TimeSquare() in intro.c
// Mac and DOS use different presentation here. DOS is a monochrome/gray
- // warning band with 16-pixel blits and white text; Mac uses the colorful
- // gradient treatment.
+ // warning band with 16-pixel blits and white text; color Mac uses the
+ // gradient treatment, while B&W Mac uses thin, dithered borders.
_gfx->clear(_gfx->black());
@@ -1344,19 +1349,19 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
const uint32 textIndex = 176;
const Common::Rect textBand(0, centery + 1, _width, centery + 16);
- if (macStyle) {
+ if (macStyle && !bwMac) {
// Mac original: blue starts at 0xFFFF and decreases by 4096 per line pair.
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 };
+ byte pal[3] = { 0, 0, (byte)val };
_gfx->setPalette(pal, 160 + i, 1);
}
- // Text is red in color, white in B&W.
+ // Color Mac uses red text.
{
- byte pal[3] = { 255, (byte)(bwMac ? 255 : 0), (byte)(bwMac ? 255 : 0) };
+ byte pal[3] = { 255, 0, 0 };
_gfx->setPalette(pal, textIndex, 1);
}
@@ -1366,6 +1371,17 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
_gfx->fillRect(Common::Rect(0, centery - 3 - i * 2, _width, centery - 1 - i * 2), 160 + i);
_gfx->fillRect(Common::Rect(0, centery + 16 + i * 2, _width, centery + 18 + i * 2), 160 + i);
}
+ } else if (bwMac) {
+ const byte whitePal[3] = { 255, 255, 255 };
+ _gfx->setPalette(whitePal, textIndex, 1);
+
+ // Original B&W TimeSquare: white borders with QuickDraw gray middle rows.
+ _gfx->fillRect(Common::Rect(0, centery - 3, _width, centery), textIndex);
+ _gfx->fillRect(Common::Rect(0, centery + 17, _width, centery + 20), textIndex);
+ for (int x = centery & 1; x < _width; x += 2) {
+ _gfx->setPixel(x, centery - 2, _gfx->black());
+ _gfx->setPixel(x, centery + 18, _gfx->black());
+ }
} else {
// DOS warning band: white outer lines, gray inner lines, white text.
const byte grayPal[3] = { 170, 170, 170 };
Commit: 6b97d8529ecf1e6a38c18dedfc2febc708de0243
https://github.com/scummvm/scummvm/commit/6b97d8529ecf1e6a38c18dedfc2febc708de0243
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-20T13:26:51+02:00
Commit Message:
COLONY: avoid fractional scaling in widescreen
Changed paths:
engines/colony/animation.cpp
engines/colony/colony.cpp
engines/colony/colony.h
engines/colony/gfx.cpp
engines/colony/renderer.h
engines/colony/renderer_opengl.cpp
engines/colony/renderer_opengl_shaders.cpp
graphics/macgui/macmenu.cpp
graphics/macgui/macmenu.h
diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 8c3ea9e87aa..ea19afa29d7 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -602,6 +602,13 @@ void ColonyEngine::playAnimation() {
uint32 lastDrawTime = 0;
bool needsDraw = true;
while (_animationRunning && !shouldQuit()) {
+ if (updateScreenSize()) {
+ _animExitPressed = _animExitInside = false;
+ _coderPressed = -1;
+ _coderPressInside = false;
+ drawAnimation();
+ needsDraw = true;
+ }
Common::Event event;
while (_system->getEventManager()->pollEvent(event)) {
if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RETURN_TO_LAUNCHER) {
@@ -609,6 +616,12 @@ void ColonyEngine::playAnimation() {
break;
} else if (event.type == Common::EVENT_SCREEN_CHANGED) {
_gfx->computeScreenViewport();
+ if (updateScreenSize()) {
+ _animExitPressed = _animExitInside = false;
+ _coderPressed = -1;
+ _coderPressInside = false;
+ }
+ drawAnimation();
needsDraw = true;
} else if (event.type == Common::EVENT_LBUTTONDOWN) {
const Common::Point pt = eventMouseToLogical(event.mouse);
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 47320c98dbd..31231e01a10 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -321,6 +321,32 @@ void ColonyEngine::warpMouseLogical(int x, int y) {
_system->warpMouse(p.x, p.y);
}
+bool ColonyEngine::updateScreenSize() {
+ if (isMacRenderMode()) {
+ const Common::Point size = computeMacCanvasSize(_system->getWidth(), _system->getHeight(), _widescreen);
+ if (size.x != _width || size.y != _height) {
+ _width = size.x;
+ _height = size.y;
+ _gfx->setCanvasSize(_width, _height);
+ const bool menuWasActive = _wm && _wm->isMenuActive();
+ if (_wm) {
+ _macMenu->closeMenu();
+ _menuSurface->create(_width, _height, _gfx->getPixelFormat());
+ _wm->setScreen(_menuSurface);
+ _macMenu->resize(_width, _height);
+ }
+ updateViewportLayout();
+ _mousePos = eventMouseToLogical(_system->getEventManager()->getMousePos());
+ if (menuWasActive)
+ updateMouseCapture(true);
+ else if (_mouseLocked && !_animationRunning)
+ warpMouseLogical(_centerX, _centerY);
+ return true;
+ }
+ }
+ return false;
+}
+
void ColonyEngine::pauseEngineIntern(bool pause) {
if (pause && _gfx && _level >= 1 && _level <= 7) {
if (_savedScreen) {
@@ -811,9 +837,8 @@ Common::Error ColonyEngine::run() {
_sound->init();
}
- // 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.
+ // DOS keeps its 640x350 EGA canvas. Mac starts at 640x480 and adapts
+ // its layout to the window once the graphics backend is initialized.
if (isMacRenderMode()) {
_width = 640;
_height = 480;
@@ -833,7 +858,9 @@ Common::Error ColonyEngine::run() {
if (!_gfx)
return Common::kUserCanceled;
+ _gfx->setSquarePixelViewport(isMacRenderMode());
loadMacColors();
+ updateScreenSize();
updateViewportLayout();
const Graphics::PixelFormat format = _system->getScreenFormat();
debugC(1, kColonyDebugRender, "Screen format: %d bytesPerPixel. Actual size: %dx%d", format.bytesPerPixel, _width, _height);
@@ -896,6 +923,8 @@ Common::Error ColonyEngine::run() {
uint32 lastCenterTick = lastMoveTick;
while (!shouldQuit()) {
_frameLimiter->startFrame();
+ // Modal screens keep their layout stable until the next full redraw.
+ updateScreenSize();
auto qlog = [](int32 x) -> int {
int i = 0;
@@ -989,6 +1018,7 @@ Common::Error ColonyEngine::run() {
return Common::kNoError;
} else if (event.type == Common::EVENT_SCREEN_CHANGED) {
_gfx->computeScreenViewport();
+ updateScreenSize();
} else if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
switch (event.customType) {
case kActionMoveForward:
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 429a8abe7a2..5638c1cb675 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -810,6 +810,7 @@ private:
void updateMouseCapture(bool recenter = true);
Common::Point getAimPoint() const;
void updateViewportLayout();
+ bool updateScreenSize();
void drawDashboardStep1();
void drawDashboardMac();
void drawDOSBarGraph(int x, int y, int height);
diff --git a/engines/colony/gfx.cpp b/engines/colony/gfx.cpp
index aa96483488b..784ff1cffb7 100644
--- a/engines/colony/gfx.cpp
+++ b/engines/colony/gfx.cpp
@@ -58,6 +58,48 @@ Common::Point canvasToWindow(const Common::Rect &viewport, const Common::Point &
viewport.top + (int)((int64)p.y * viewport.height() / canvasH));
}
+Common::Rect computeScreenViewport(OSystem *system, int width, int height, bool squarePixels) {
+ const int screenWidth = system->getWidth();
+ const int screenHeight = system->getHeight();
+ int viewportWidth = screenWidth;
+ int viewportHeight = screenHeight;
+
+ // Fractional enlargement makes bitmap fonts and dithering uneven.
+ if (squarePixels) {
+ const int scale = MIN(screenWidth / width, screenHeight / height);
+ if (scale >= 1) {
+ viewportWidth = width * scale;
+ viewportHeight = height * scale;
+ } else {
+ // Fit smaller windows without cropping the UI.
+ viewportWidth = MIN(screenWidth, (screenHeight * width + height / 2) / height);
+ viewportHeight = MIN(screenHeight, (screenWidth * height + width / 2) / width);
+ }
+ } else if (ConfMan.getBool("widescreen_mod")) {
+ viewportWidth = MIN(screenWidth, (screenHeight * 16 + 4) / 9);
+ viewportHeight = MIN(screenHeight, (screenWidth * 9 + 8) / 16);
+ } else if (system->getFeatureState(OSystem::kFeatureAspectRatioCorrection)) {
+ viewportWidth = MIN(screenWidth, screenHeight * 4 / 3);
+ viewportHeight = MIN(screenHeight, screenWidth * 3 / 4);
+ }
+
+ Common::Rect viewport(viewportWidth, viewportHeight);
+ viewport.translate((screenWidth - viewportWidth) / 2, (screenHeight - viewportHeight) / 2);
+ return viewport;
+}
+
+Common::Point computeMacCanvasSize(int screenWidth, int screenHeight, bool widescreen) {
+ if (!widescreen) {
+ const int width = MIN(screenWidth, screenHeight * 4 / 3);
+ screenHeight = MIN(screenHeight, screenWidth * 3 / 4);
+ screenWidth = width;
+ }
+
+ // Grow the layout to fill the window while keeping bitmap pixels uniform.
+ const int scale = MAX(1, MIN(screenWidth / 640, screenHeight / 480));
+ return Common::Point(MAX(640, screenWidth / scale), MAX(480, screenHeight / scale));
+}
+
// Pick the renderer type. Honors --renderer=<code> on the command line /
// ConfMan key, restricted to what was compiled in.
//
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index 47ed2aae323..95488fa4896 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -77,6 +77,7 @@ public:
virtual void setFeatureClipX(int left, int right) {}
virtual void clearFeatureClipX() {}
virtual void setSquarePixelViewport(bool enable) = 0;
+ virtual void setCanvasSize(int width, int height) = 0;
virtual void computeScreenViewport() = 0;
// Window-pixel rect the logical canvas is currently drawn into.
@@ -102,6 +103,9 @@ protected:
Common::Point windowToCanvas(const Common::Rect &viewport, const Common::Point &p, int canvasW, int canvasH);
Common::Point canvasToWindow(const Common::Rect &viewport, const Common::Point &p, int canvasW, int canvasH);
+Common::Rect computeScreenViewport(OSystem *system, int width, int height, bool squarePixels);
+Common::Point computeMacCanvasSize(int screenWidth, int screenHeight, bool widescreen);
+
// Factory function (follows Freescape pattern: picks best available renderer)
Renderer *createRenderer(OSystem *system, int width, int height);
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 88312b5807a..89040b34113 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -25,7 +25,6 @@
*
*/
-#include "common/config-manager.h"
#include "common/system.h"
#include "graphics/font.h"
#include "graphics/surface.h"
@@ -118,6 +117,7 @@ public:
applyScissorRect(_active3DViewport);
}
void setSquarePixelViewport(bool enable) override;
+ void setCanvasSize(int width, int height) override;
void computeScreenViewport() override;
void drawSurface(const Graphics::Surface *surf, int x, int y) override;
Graphics::Surface *getScreenshot() override;
@@ -127,9 +127,7 @@ public:
private:
void useColor(uint32 color);
- // Set glLineWidth scaled to window/logical ratio. Mirrors Freescape
- // (gfx_opengl_shaders.cpp:692). Cached so back-to-back same-width
- // line draws don't re-issue the GL call.
+ // Scale line widths with the canvas, excluding letterboxing.
void applyLineWidthForLines();
float _lineWidth = 1.0f;
GLuint _overlayTexId = 0;
@@ -201,10 +199,7 @@ void OpenGLRenderer::useColor(uint32 color) {
}
void OpenGLRenderer::applyLineWidthForLines() {
- const int sysW = _system ? _system->getWidth() : 0;
- float w = 1.0f;
- if (sysW > _width)
- w = MAX(1.0f, (float)sysW / (float)_width);
+ const float w = MAX(1.0f, (float)_screenViewport.width() / _width);
if (w != _lineWidth) {
glLineWidth(w);
_lineWidth = w;
@@ -583,46 +578,24 @@ void OpenGLRenderer::setSquarePixelViewport(bool enable) {
}
void OpenGLRenderer::computeScreenViewport() {
- int32 screenWidth = _system->getWidth();
- int32 screenHeight = _system->getHeight();
-
- bool widescreen = ConfMan.getBool("widescreen_mod");
-
- if (_squarePixelViewport) {
- // Animation bitmaps use square pixels. Fit the uncorrected logical
- // canvas so their authored 416x264 geometry matches the Mac version.
- const int32 viewportWidth = MIN<int32>(screenWidth,
- (screenHeight * _width + _height / 2) / _height);
- const int32 viewportHeight = MIN<int32>(screenHeight,
- (screenWidth * _height + _width / 2) / _width);
- _screenViewport = Common::Rect(viewportWidth, viewportHeight);
- _screenViewport.translate((screenWidth - viewportWidth) / 2,
- (screenHeight - viewportHeight) / 2);
- } else if (widescreen) {
- // Widescreen is a 16:9 presentation even when fullscreen uses a
- // taller or wider display. Keep it centered instead of stretching it.
- const int32 viewportWidth = MIN<int32>(screenWidth, (screenHeight * 16 + 4) / 9);
- const int32 viewportHeight = MIN<int32>(screenHeight, (screenWidth * 9 + 8) / 16);
- _screenViewport = Common::Rect(viewportWidth, viewportHeight);
- _screenViewport.translate((screenWidth - viewportWidth) / 2,
- (screenHeight - viewportHeight) / 2);
- } 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);
- }
+ const int screenHeight = _system->getHeight();
+ _screenViewport = Colony::computeScreenViewport(_system, _width, _height, _squarePixelViewport);
glViewport(_screenViewport.left, screenHeight - _screenViewport.bottom, _screenViewport.width(), _screenViewport.height());
glScissor(_screenViewport.left, screenHeight - _screenViewport.bottom, _screenViewport.width(), _screenViewport.height());
}
+void OpenGLRenderer::setCanvasSize(int width, int height) {
+ _width = width;
+ _height = height;
+ computeScreenViewport();
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, _width, _height, 0, -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+}
+
void OpenGLRenderer::drawEllipse(int x, int y, int rx, int ry, uint32 color) {
GLint savedViewport[4];
GLint savedScissor[4];
@@ -697,10 +670,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);
+ // Each checker covers a logical pixel, even on a scaled viewport.
+ glBegin(GL_QUADS);
for (int y = rect.top; y < rect.bottom; y++) {
for (int x = rect.left + (y % 2); x < rect.right; x += 2) {
glVertex2i(x, y);
+ glVertex2i(x + 1, y);
+ glVertex2i(x + 1, y + 1);
+ glVertex2i(x, y + 1);
}
}
glEnd();
diff --git a/engines/colony/renderer_opengl_shaders.cpp b/engines/colony/renderer_opengl_shaders.cpp
index 5bc655fced7..28767b69da6 100644
--- a/engines/colony/renderer_opengl_shaders.cpp
+++ b/engines/colony/renderer_opengl_shaders.cpp
@@ -26,7 +26,7 @@
*/
#include "common/scummsys.h"
-#include "common/config-manager.h"
+#include "common/array.h"
#include "common/debug.h"
#include "common/system.h"
#include "common/textconsole.h"
@@ -84,6 +84,7 @@ public:
void clearFeatureClipX() override;
void applyScissorRect(const Common::Rect &logical);
void setSquarePixelViewport(bool enable) override;
+ void setCanvasSize(int width, int height) override;
void computeScreenViewport() override;
void drawSurface(const Graphics::Surface *surf, int x, int y) override;
@@ -102,9 +103,7 @@ private:
void uploadSolid(const float *positions, int vertCount);
void drawSolid(GLenum mode, const float *positions, int vertCount, const float rgba[4]);
void drawTexturedQuad(int x, int y, int w, int h);
- // Set glLineWidth to scale with the window size (Freescape pattern,
- // gfx_opengl_shaders.cpp:692). No-op when mode isn't a line primitive.
- // Cached to avoid redundant state changes across same-width draws.
+ // Scale line widths with the canvas, excluding letterboxing.
void applyLineWidth(GLenum mode);
void uploadSolid3D(const float *positions, int vertCount);
@@ -175,9 +174,7 @@ private:
bool _stipplePatternDirty = false;
bool _stippleColorsDirty = true;
- // Solid VBO holds vec2 position only. Sized for the worst-case 2D
- // primitive â the dither overlay can stream up to width*height/2 dots,
- // so leave room for typical screens (â170k floats for an 800Ã600 split).
+ // Initial 2D buffer size; uploadSolid reallocates it for each draw.
enum { kSolidVertexCapacity = 320 * 1024 };
// 3D VBO holds vec3 positions. Corridor polygons are typically <16
// vertices but a few features (sprite billboards, stairs) push higher.
@@ -333,15 +330,7 @@ void OpenGLShaderRenderer::uploadSolid(const float *positions, int vertCount) {
void OpenGLShaderRenderer::applyLineWidth(GLenum mode) {
if (mode != GL_LINES && mode != GL_LINE_STRIP && mode != GL_LINE_LOOP)
return;
- // Scale by window pixel width versus engine logical width â mirrors
- // Freescape (gfx_opengl_shaders.cpp:692), MAX(1, ...) for safety.
- // With kSupportsArbitraryResolutions, _system->getWidth() returns the
- // overlay (window) pixel width; without it, getWidth() == _width and
- // the ratio is 1.
- const int sysW = _system ? _system->getWidth() : 0;
- float w = 1.0f;
- if (sysW > _width)
- w = MAX(1.0f, (float)sysW / (float)_width);
+ const float w = MAX(1.0f, (float)_screenViewport.width() / _width);
if (w != _lineWidth) {
glLineWidth(w);
_lineWidth = w;
@@ -499,24 +488,26 @@ void OpenGLShaderRenderer::fillDitherRect(const Common::Rect &rect, uint32 c1, u
if (w <= 0 || h <= 0)
return;
- // 50% checkerboard: place a dot on every other pixel, alternating per row.
- // Capacity guard â fall back to solid c2 if the rect is larger than our
- // streaming buffer (very rare: only the dashboard background hits this
- // path, and it is much smaller than kSolidVertexCapacity).
- const int maxDots = kSolidVertexCapacity;
- int dots = 0;
- float *verts = new float[maxDots * 2];
- for (int yi = 0; yi < h && dots < maxDots; yi++) {
- const int yy = rect.top + yi;
- for (int xi = (yi & 1); xi < w && dots < maxDots; xi += 2) {
- verts[dots * 2 + 0] = (float)(rect.left + xi);
- verts[dots * 2 + 1] = (float)yy;
- dots++;
+ // GL_POINTS stay one framebuffer pixel wide. Use logical pixel cells
+ // so the checkerboard retains its 50% coverage at every scale.
+ Common::Array<float> verts;
+ verts.reserve(((w + 1) / 2) * h * 12);
+ for (int y = rect.top; y < rect.bottom; y++) {
+ for (int x = rect.left + (y & 1); x < rect.right; x += 2) {
+ const float cell[] = {
+ (float)x, (float)y,
+ (float)(x + 1), (float)y,
+ (float)x, (float)(y + 1),
+ (float)(x + 1), (float)y,
+ (float)(x + 1), (float)(y + 1),
+ (float)x, (float)(y + 1)
+ };
+ for (uint i = 0; i < ARRAYSIZE(cell); i++)
+ verts.push_back(cell[i]);
}
}
- if (dots > 0)
- drawSolid(GL_POINTS, verts, dots, rgba);
- delete[] verts;
+ if (!verts.empty())
+ drawSolid(GL_TRIANGLES, &verts[0], verts.size() / 2, rgba);
}
// ---------------------------------------------------------------------------
@@ -641,34 +632,8 @@ void OpenGLShaderRenderer::setSquarePixelViewport(bool enable) {
}
void OpenGLShaderRenderer::computeScreenViewport() {
- const int32 screenWidth = _system->getWidth();
- const int32 screenHeight = _system->getHeight();
- const bool widescreen = ConfMan.getBool("widescreen_mod");
-
- if (_squarePixelViewport) {
- // Animation bitmaps use square pixels. Fit the uncorrected logical
- // canvas so their authored 416x264 geometry matches the Mac version.
- const int32 vpW = MIN<int32>(screenWidth,
- (screenHeight * _width + _height / 2) / _height);
- const int32 vpH = MIN<int32>(screenHeight,
- (screenWidth * _height + _width / 2) / _width);
- _screenViewport = Common::Rect(vpW, vpH);
- _screenViewport.translate((screenWidth - vpW) / 2, (screenHeight - vpH) / 2);
- } else if (widescreen) {
- // Widescreen is a 16:9 presentation even when fullscreen uses a
- // taller or wider display. Keep it centered instead of stretching it.
- const int32 vpW = MIN<int32>(screenWidth, (screenHeight * 16 + 4) / 9);
- const int32 vpH = MIN<int32>(screenHeight, (screenWidth * 9 + 8) / 16);
- _screenViewport = Common::Rect(vpW, vpH);
- _screenViewport.translate((screenWidth - vpW) / 2, (screenHeight - vpH) / 2);
- } else if (_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection)) {
- const int32 vpW = MIN<int32>(screenWidth, screenHeight * 4 / 3);
- const int32 vpH = MIN<int32>(screenHeight, screenWidth * 3 / 4);
- _screenViewport = Common::Rect(vpW, vpH);
- _screenViewport.translate((screenWidth - vpW) / 2, (screenHeight - vpH) / 2);
- } else {
- _screenViewport = Common::Rect(screenWidth, screenHeight);
- }
+ const int screenHeight = _system->getHeight();
+ _screenViewport = Colony::computeScreenViewport(_system, _width, _height, _squarePixelViewport);
glViewport(_screenViewport.left, screenHeight - _screenViewport.bottom,
_screenViewport.width(), _screenViewport.height());
@@ -681,6 +646,14 @@ void OpenGLShaderRenderer::copyToScreen() {
_system->updateScreen();
}
+void OpenGLShaderRenderer::setCanvasSize(int width, int height) {
+ _width = width;
+ _height = height;
+ computeScreenViewport();
+ rebuildProjection();
+ uploadProjectionUniform();
+}
+
Graphics::Surface *OpenGLShaderRenderer::getScreenshot() {
Graphics::Surface *surface = new Graphics::Surface();
surface->create(_screenViewport.width(), _screenViewport.height(),
diff --git a/graphics/macgui/macmenu.cpp b/graphics/macgui/macmenu.cpp
index 16f0b1de21c..d0f07424bd4 100644
--- a/graphics/macgui/macmenu.cpp
+++ b/graphics/macgui/macmenu.cpp
@@ -589,6 +589,14 @@ void MacMenu::calcDimensions() {
_dimensionsDirty = false;
}
+void MacMenu::resize(int width, int height) {
+ _composeSurface->create(width, height, _wm->_pixelformat);
+ _tempSurface.create(width, _font->getFontHeight(), _wm->_pixelformat);
+ _bbox.right = width;
+ _dimensionsDirty = true;
+ _contentIsDirty = true;
+}
+
void MacMenu::loadMenuResource(Common::MacResManager *resFork, uint16 id) {
Common::SeekableReadStream *res = resFork->getResource(MKTAG('M', 'E', 'N', 'U'), id);
assert(res);
diff --git a/graphics/macgui/macmenu.h b/graphics/macgui/macmenu.h
index 5e56ff27aa8..7acaaaf7cee 100644
--- a/graphics/macgui/macmenu.h
+++ b/graphics/macgui/macmenu.h
@@ -111,6 +111,7 @@ public:
void addStaticMenus(const MacMenuData *data);
void calcDimensions();
+ void resize(int width, int height);
int numberOfMenus();
int numberOfMenuItems(MacMenuItem *menu);
Commit: fbe4c3482f5c5d60eceaa1bfe42e713890fbe874
https://github.com/scummvm/scummvm/commit/fbe4c3482f5c5d60eceaa1bfe42e713890fbe874
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-20T13:26:51+02:00
Commit Message:
COLONY: avoid camera of getting too close to objects
Changed paths:
engines/colony/colony.h
engines/colony/render.cpp
engines/colony/render_features.cpp
engines/colony/render_objects.cpp
engines/colony/renderer.h
engines/colony/renderer_opengl.cpp
engines/colony/renderer_opengl_shaders.cpp
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 5638c1cb675..2d878c831af 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -668,6 +668,7 @@ private:
int _direction = 0;
float _eyeDepthPull = 0.0f; // world units the eye parts are pulled at the camera
+ Common::Point _renderCamera;
// think.c: the snoop's snout bobs while it hunts (sniff/csniff).
int _snoopSnoutZ = 0;
int _snoopSniff = 5;
@@ -729,6 +730,8 @@ private:
bool drawStaticObjectPrisms3D(Thing &obj);
void initRobots();
void renderCorridor3D();
+ void computeRenderCamera();
+ void separateRenderCameraFromWall(int x, int y, uint8 bit, int separation, bool featuresVisible);
void drawWallFeatures3D();
bool isWallFeatureFacingCamera(int cellX, int cellY, int direction) const;
void drawWallFeature3D(int cellX, int cellY, int direction);
@@ -736,6 +739,7 @@ private:
void getWallFace3D(int cellX, int cellY, int direction, float corners[4][3]);
bool isVisibleRecessFeature(int x, int y, int direction) const;
bool wallSegmentIsOpenWell(int x, int y, uint8 bit) const;
+ bool wallSegmentIsRendered(int x, int y, uint8 bit) const;
void getWallRecess3D(const float corners[4][3], float farC[4][3]) const;
void recessPoint(const float nearC[4][3], const float farC[4][3], float u, float v, float depth, float out[3]) const;
void recessLine(const float nearC[4][3], const float farC[4][3], float u1, float v1, float d1,
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 66fdb975f44..cf92834b9a8 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -322,7 +322,7 @@ void projectCorridorPointClamped(const Common::Rect &screenR, int look, int look
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 depth = MAX(-eyeZ, kProjectionNearClip);
const float centerX = screenR.left + screenR.width() * 0.5f;
const float centerY = screenR.top + screenR.height() * 0.5f;
@@ -455,6 +455,11 @@ bool ColonyEngine::wallSegmentIsOpenWell(int x, int y, uint8 bit) const {
return isVisibleRecessFeature(x, y, kDirWest) || isVisibleRecessFeature(x - 1, y, kDirEast);
}
+bool ColonyEngine::wallSegmentIsRendered(int x, int y, uint8 bit) const {
+ return x >= 0 && x < 32 && y >= 0 && y < 32 &&
+ (_wall[x][y] & bit) && !wallSegmentIsOpenWell(x, y, bit);
+}
+
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;
@@ -481,7 +486,7 @@ bool projectCorridorPoint(const Common::Rect &screenR, uint8 look, int8 lookY,
const float eyeX = side;
const float eyeY = dz * cosPitch + forward * sinPitch;
const float eyeZ = dz * sinPitch - forward * cosPitch;
- if (eyeZ >= -1.0f)
+ if (eyeZ >= -kProjectionNearClip)
return false;
const float centerX = screenR.left + screenR.width() * 0.5f;
@@ -517,14 +522,14 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
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,
+ projectCorridorPointClamped(_screenR, _me.look, _me.lookY, _sint, _cost, _renderCamera.x, _renderCamera.y,
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,
+ if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _renderCamera.x, _renderCamera.y,
transformedX[i], transformedY[i], transformedZ[i], sx, sy)) {
obj.where.xmn = MIN(obj.where.xmn, sx);
obj.where.xmx = MAX(obj.where.xmx, sx);
@@ -721,14 +726,14 @@ void ColonyEngine::pullTowardCamera(float *px, float *py, float *pz, int count)
if (_eyeDepthPull <= 0.0f)
return;
for (int i = 0; i < count; i++) {
- const float dx = px[i] - (float)_me.xloc;
- const float dy = py[i] - (float)_me.yloc;
+ const float dx = px[i] - (float)_renderCamera.x;
+ const float dy = py[i] - (float)_renderCamera.y;
const float d = sqrtf(dx * dx + dy * dy + pz[i] * pz[i]);
if (d <= _eyeDepthPull)
continue;
const float k = (d - _eyeDepthPull) / d;
- px[i] = (float)_me.xloc + dx * k;
- py[i] = (float)_me.yloc + dy * k;
+ px[i] = (float)_renderCamera.x + dx * k;
+ py[i] = (float)_renderCamera.y + dy * k;
pz[i] *= k;
}
}
@@ -769,9 +774,9 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
// Billboard turned to face the camera in 3D. The original drew the ball as a
// screen-space oval and never tilted the view; keeping the disc upright in
// world Z instead flattens it to a sliver when looking down at a floor egg.
- // Camera is at (_me.xloc, _me.yloc, 0).
- float viewX = cx - (float)_me.xloc;
- float viewY = cy - (float)_me.yloc;
+ // Camera is at (_renderCamera.x, _renderCamera.y, 0).
+ float viewX = cx - (float)_renderCamera.x;
+ float viewY = cy - (float)_renderCamera.y;
float viewZ = cz;
float viewLen = sqrtf(viewX * viewX + viewY * viewY + viewZ * viewZ);
if (viewLen < 0.001f)
@@ -813,7 +818,7 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
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,
+ if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _renderCamera.x, _renderCamera.y,
px[i], py[i], pz[i], sx, sy)) {
obj.where.xmn = MIN(obj.where.xmn, sx);
obj.where.xmx = MAX(obj.where.xmx, sx);
@@ -958,8 +963,61 @@ void ColonyEngine::computeVisibleCells() {
}
}
+void ColonyEngine::separateRenderCameraFromWall(int x, int y, uint8 bit, int separation, bool featuresVisible) {
+ if (!wallSegmentIsRendered(x, y, bit))
+ return;
+
+ const bool horizontal = (bit == 0x01);
+ const int along = horizontal ? _me.xloc : _me.yloc;
+ const int start = (horizontal ? x : y) * 256;
+ if (along < start - separation || along > start + 256 + separation)
+ return;
+
+ const bool positiveSide = horizontal ? _me.yindex >= y : _me.xindex >= x;
+ float wall = (horizontal ? y : x) * 256.0f;
+ const int cellX = horizontal || positiveSide ? x : x - 1;
+ const int cellY = !horizontal || positiveSide ? y : y - 1;
+ const int direction = horizontal ? (positiveSide ? kDirSouth : kDirNorth)
+ : (positiveSide ? kDirWest : kDirEast);
+ const uint8 *feature = mapFeatureAt(cellX, cellY, direction);
+ if (featuresVisible && feature && feature[0] != kWallFeatureNone && _visibleCell[cellX][cellY]) {
+ float corners[4][3];
+ getWallFace3D(cellX, cellY, direction, corners);
+ wall = corners[0][horizontal ? 1 : 0];
+ }
+
+ int camera = horizontal ? _renderCamera.y : _renderCamera.x;
+ camera = positiveSide ? MAX(camera, (int)ceilf(wall + separation))
+ : MIN(camera, (int)floorf(wall - separation));
+ if (horizontal)
+ _renderCamera.y = camera;
+ else
+ _renderCamera.x = camera;
+}
+
+void ColonyEngine::computeRenderCamera() {
+ _renderCamera = Common::Point(_me.xloc, _me.yloc);
+
+ // Enclose the near-plane corners for every view angle, including pitch.
+ const float halfWidth = kProjectionNearClip * _screenR.width() * 0.5f / kProjectionFocalLength;
+ const float halfHeight = kProjectionNearClip * _screenR.height() * 0.5f / kProjectionFocalLength;
+ const int separation = (int)ceilf(sqrtf(kProjectionNearClip * kProjectionNearClip +
+ halfWidth * halfWidth + halfHeight * halfHeight));
+ const bool featuresVisible = isMacColorMode() || _corePower[_coreIndex] > 0;
+
+ // Like Freescape, separate only the rendering camera. Touching a wall must
+ // still reach the door, stair and other interaction triggers.
+ for (int y = 0; y < 32; y++) {
+ for (int x = 0; x < 32; x++) {
+ separateRenderCameraFromWall(x, y, 0x01, separation, featuresVisible);
+ separateRenderCameraFromWall(x, y, 0x02, separation, featuresVisible);
+ }
+ }
+}
+
void ColonyEngine::renderCorridor3D() {
computeVisibleCells();
+ computeRenderCamera();
bool lit = (_corePower[_coreIndex] > 0);
bool macMode = isMacRenderMode();
@@ -990,7 +1048,7 @@ void ColonyEngine::renderCorridor3D() {
ceilColor = macMode ? (lit ? 255 : 0) : wallFill;
}
- _gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
+ _gfx->begin3D(_renderCamera.x, _renderCamera.y, 0, _me.look, _me.lookY, _screenR);
_gfx->clear(ceilColor);
uint32 wallColor = wallLine;
@@ -1037,12 +1095,11 @@ void ColonyEngine::renderCorridor3D() {
for (int y = 0; y < 32; y++) {
for (int x = 0; x < 32; x++) {
- uint8 w = _wall[x][y];
// A stair well is a hole: the feature draws the opening itself.
- if ((w & 0x01) && !wallSegmentIsOpenWell(x, y, 0x01)) {
+ if (wallSegmentIsRendered(x, y, 0x01)) {
_gfx->draw3DWall(x, y, x + 1, y, wallColor);
}
- if ((w & 0x02) && !wallSegmentIsOpenWell(x, y, 0x02)) {
+ if (wallSegmentIsRendered(x, y, 0x02)) {
_gfx->draw3DWall(x, y, x, y + 1, wallColor);
}
}
diff --git a/engines/colony/render_features.cpp b/engines/colony/render_features.cpp
index 140e250ec21..5f294dd8411 100644
--- a/engines/colony/render_features.cpp
+++ b/engines/colony/render_features.cpp
@@ -109,8 +109,8 @@ void ColonyEngine::getWallRecess3D(const float corners[4][3], float farC[4][3])
ny /= len;
nz /= len;
- const float awayX = corners[0][0] - (float)_me.xloc;
- const float awayY = corners[0][1] - (float)_me.yloc;
+ const float awayX = corners[0][0] - (float)_renderCamera.x;
+ const float awayY = corners[0][1] - (float)_renderCamera.y;
const float awayZ = corners[0][2];
if (nx * awayX + ny * awayY + nz * awayZ < 0.0f) {
nx = -nx;
@@ -171,7 +171,7 @@ void ColonyEngine::clipToWallFace(const float corners[4][3]) {
for (int i = 0; i < 4; i++) {
int sx = 0;
int sy = 0;
- projectCorridorPointClamped(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+ projectCorridorPointClamped(_screenR, _me.look, _me.lookY, _sint, _cost, _renderCamera.x, _renderCamera.y,
corners[i][0], corners[i][1], corners[i][2], sx, sy);
if (i == 0) {
minX = sx;
@@ -603,13 +603,13 @@ bool ColonyEngine::isWallFeatureFacingCamera(int cellX, int cellY, int direction
// Keep features visible while standing on the boundary.
switch (direction) {
case kDirNorth:
- return _me.yloc <= (cellY + 1) * 256;
+ return _renderCamera.y <= (cellY + 1) * 256;
case kDirSouth:
- return _me.yloc >= cellY * 256;
+ return _renderCamera.y >= cellY * 256;
case kDirWest:
- return _me.xloc >= cellX * 256;
+ return _renderCamera.x >= cellX * 256;
case kDirEast:
- return _me.xloc <= (cellX + 1) * 256;
+ return _renderCamera.x <= (cellX + 1) * 256;
default:
return true;
}
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 6794814e1a2..ab5daddba88 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -111,7 +111,7 @@ bool projectCorridorPointRaw(const Common::Rect &screenR, uint8 look, int8 lookY
const float eyeX = side;
const float eyeY = dz * cosPitch + forward * sinPitch;
const float eyeZ = dz * sinPitch - forward * cosPitch;
- if (eyeZ >= -1.0f)
+ if (eyeZ >= -kProjectionNearClip)
return false;
const float centerX = screenR.left + screenR.width() * 0.5f;
@@ -1344,7 +1344,7 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
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,
+ projected[i] = projectCorridorPointRaw(_screenR, _me.look, _me.lookY, _sint, _cost, _renderCamera.x, _renderCamera.y,
transformedX[i], transformedY[i], transformedZ[i],
projectedX[i], projectedY[i]);
}
@@ -1494,7 +1494,7 @@ void ColonyEngine::drawDOSEyeSlit3D(Thing &thing, const PrismPartDef &irisDef, b
void ColonyEngine::drawEyeOverlays3D(Thing &thing, const PrismPartDef &irisDef, int irisColorOverride,
const PrismPartDef &pupilDef, int pupilColorOverride, bool useLook, bool dosFill) {
if (!isProjectedPrismSurfaceVisible(_screenR, thing, irisDef, useLook, 0,
- _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost)) {
+ _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost)) {
return;
}
@@ -1521,8 +1521,8 @@ void ColonyEngine::drawEnemyEye3D(Thing &obj, Thing &eye, int eyeballColor, int
const uint8 eyeAng = obj.where.ang + 32;
const int32 eyeBaseX = obj.where.xloc + (_cost[eyeAng] >> 1);
const int32 eyeBaseY = obj.where.yloc + (_sint[eyeAng] >> 1);
- const int64 dx = eyeBaseX - _me.xloc;
- const int64 dy = eyeBaseY - _me.yloc;
+ const int64 dx = eyeBaseX - _renderCamera.x;
+ const int64 dy = eyeBaseY - _renderCamera.y;
dosFill = dx * dx + dy * dy > 64 * 64;
}
@@ -1940,7 +1940,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
// 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);
+ _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
_gfx->setDepthRange(0.008f, 1.0f);
draw3DPrism(obj, kTableParts[1], false, -1, true, false); // table base
_gfx->setDepthRange(0.006f, 1.0f);
@@ -1985,7 +1985,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
case kObjPToilet: {
// MakePToilet draws the two-sided partition first when it is behind the toilet.
const bool partitionBehind = isProjectedPrismSurfaceVisible(_screenR, obj, kPToiletParts[4], false, 0,
- _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+ _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
for (int i = 0; i < 5; i++) {
const int part = partitionBehind ? (i == 0 ? 4 : i - 1) : i;
_gfx->setDepthRange((4 - i) * 0.002f, 1.0f);
@@ -2011,8 +2011,8 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
break;
// === 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) {
+ if ((obj.where.xloc - _renderCamera.x) * (obj.where.xloc - _renderCamera.x) +
+ (obj.where.yloc - _renderCamera.y) * (obj.where.yloc - _renderCamera.y) <= 64 * 64) {
break;
}
// Writes off, as in drawEnemyEye3D().
@@ -2120,7 +2120,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
break;
case kRobQueen:
{
- EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+ EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
Thing &firstEye = eyes.leftFirst ? eyes.left : eyes.right;
Thing &secondEye = eyes.leftFirst ? eyes.right : eyes.left;
const PrismPartDef &firstWing = eyes.leftFirst ? kQLWingDef : kQRWingDef;
@@ -2150,7 +2150,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
draw3DPrism(obj, kQAbdomenDef, false, -1, true, false);
};
- const int body = queenBodyOrder(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+ const int body = queenBodyOrder(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
if (body == 0) {
drawThorax();
drawAbdomen();
@@ -2158,7 +2158,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
drawWingEye(firstEye, firstWing);
if (body == 1) {
const bool abdomenBackVisible = prismSurfaceVisible(_screenR, obj, kQAbdomenDef, 8,
- _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+ _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
if (abdomenBackVisible) {
drawAbdomen();
drawThorax();
@@ -2177,7 +2177,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
break;
case kRobDrone:
{
- EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+ EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
Thing &firstEye = eyes.leftFirst ? eyes.left : eyes.right;
Thing &secondEye = eyes.leftFirst ? eyes.right : eyes.left;
const int enemyEyeballColor = isMacRenderMode() ? eyeballColor : 5;
@@ -2194,7 +2194,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
drawEnemyEye3D(obj, eye, enemyEyeballColor, kColorDroneEye, pupilColor);
};
- const int body = droneBodyOrder(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+ const int body = droneBodyOrder(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
if (body == 0)
drawBody();
drawEye(firstEye);
@@ -2225,12 +2225,12 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
const PrismPartDef leftPincerDef = {4, leftPincerPts, 4, kDLPincerSurf};
const PrismPartDef rightPincerDef = {4, rightPincerPts, 4, kDRPincerSurf};
- EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+ EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
Thing &firstEye = eyes.leftFirst ? eyes.left : eyes.right;
Thing &secondEye = eyes.leftFirst ? eyes.right : eyes.left;
const int enemyEyeballColor = isMacRenderMode() ? eyeballColor : 5;
- const float leftPincerDepth = prismPointForwardDepth(obj, leftPincerDef, 1, _me.look, _me.xloc, _me.yloc, _sint, _cost);
- const float rightPincerDepth = prismPointForwardDepth(obj, rightPincerDef, 1, _me.look, _me.xloc, _me.yloc, _sint, _cost);
+ const float leftPincerDepth = prismPointForwardDepth(obj, leftPincerDef, 1, _me.look, _renderCamera.x, _renderCamera.y, _sint, _cost);
+ const float rightPincerDepth = prismPointForwardDepth(obj, rightPincerDef, 1, _me.look, _renderCamera.x, _renderCamera.y, _sint, _cost);
int layer = 8;
auto setNextDepthRange = [&]() {
_gfx->setDepthRange((layer--) * 0.002f, 1.0f);
@@ -2257,7 +2257,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
}
};
- const int body = droneBodyOrder(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+ const int body = droneBodyOrder(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
switch (body) {
case 0:
drawBody();
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index 95488fa4896..86073313501 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -37,6 +37,7 @@ namespace Colony {
// CALCROBO.C projects both axes with (coordinate << 8) / depth.
const float kProjectionFocalLength = 256.0f;
+const float kProjectionNearClip = 1.0f;
class Renderer {
public:
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 89040b34113..af782578d97 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -307,7 +307,7 @@ void OpenGLRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY
// The original projection is center + (coordinate << 8) / depth, so
// derive the frustum from its fixed 256-logical-pixel focal length.
- const float nearClip = 1.0f;
+ const float nearClip = kProjectionNearClip;
const float farClip = 10000.0f;
const float xmax = nearClip * viewport.width() * 0.5f / kProjectionFocalLength;
const float ymax = nearClip * viewport.height() * 0.5f / kProjectionFocalLength;
diff --git a/engines/colony/renderer_opengl_shaders.cpp b/engines/colony/renderer_opengl_shaders.cpp
index 28767b69da6..8f58fd40dc8 100644
--- a/engines/colony/renderer_opengl_shaders.cpp
+++ b/engines/colony/renderer_opengl_shaders.cpp
@@ -772,7 +772,7 @@ void OpenGLShaderRenderer::begin3D(int camX, int camY, int camZ, int angle, int
// Match CALCROBO.C's center + (coordinate << 8) / depth projection and
// the fixed-function path by using a 256-logical-pixel focal length.
- const float nearClip = 1.0f;
+ const float nearClip = kProjectionNearClip;
const float farClip = 10000.0f;
const float xmax = nearClip * viewport.width() * 0.5f / kProjectionFocalLength;
const float ymax = nearClip * viewport.height() * 0.5f / kProjectionFocalLength;
Commit: b398c8b1942442a47cfada35d6cce8d3f54943c0
https://github.com/scummvm/scummvm/commit/b398c8b1942442a47cfada35d6cce8d3f54943c0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-20T13:26:51+02:00
Commit Message:
COLONY: show UI when player interacts with an animation
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 ea19afa29d7..c6927241681 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -423,6 +423,7 @@ void ColonyEngine::playAnimation() {
_rotateRight = false;
_animationRunning = true;
+ syncMacMenuChecks();
_animExitPressed = _animExitInside = false;
_animExitStrip = _animExitButton = Common::Rect();
_coderCursor = 0;
@@ -610,11 +611,12 @@ void ColonyEngine::playAnimation() {
needsDraw = true;
}
Common::Event event;
- while (_system->getEventManager()->pollEvent(event)) {
+ while (_animationRunning && _system->getEventManager()->pollEvent(event)) {
if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RETURN_TO_LAUNCHER) {
_animationRunning = false;
break;
- } else if (event.type == Common::EVENT_SCREEN_CHANGED) {
+ }
+ if (event.type == Common::EVENT_SCREEN_CHANGED) {
_gfx->computeScreenViewport();
if (updateScreenSize()) {
_animExitPressed = _animExitInside = false;
@@ -623,7 +625,25 @@ void ColonyEngine::playAnimation() {
}
drawAnimation();
needsDraw = true;
- } else if (event.type == Common::EVENT_LBUTTONDOWN) {
+ continue;
+ }
+ // Only an open menu captures input; the menu bar stays visible.
+ if (_macMenu && _macMenu->hasAllFocus() && event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START &&
+ event.customType == kActionEscape) {
+ _macMenu->closeMenu();
+ needsDraw = true;
+ continue;
+ }
+ if (processMacMenuEvent(event)) {
+ needsDraw = true;
+ if (!_animationRunning || shouldQuit())
+ break;
+ continue;
+ }
+ if (_macMenu && _macMenu->hasAllFocus())
+ continue;
+
+ if (event.type == Common::EVENT_LBUTTONDOWN) {
const Common::Point pt = eventMouseToLogical(event.mouse);
_messageSourceRect = Common::Rect();
if (handleColonyCoderClick(pt)) {
@@ -751,11 +771,14 @@ void ColonyEngine::playAnimation() {
if (useSquarePixelViewport)
_gfx->setSquarePixelViewport(false);
- _system->lockMouse(true);
- CursorMan.showMouse(false);
+ _animationRunning = false;
+ if (_macMenu)
+ _macMenu->closeMenu();
CursorMan.popAllCursors();
+ updateMouseCapture(true);
+ syncMacMenuChecks();
- // Purge pending mouse/keyboard events AFTER re-locking the mouse,
+ // Purge pending mouse/keyboard events after restoring mouse capture,
// 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();
@@ -812,6 +835,9 @@ void ColonyEngine::drawAnimation() {
// Full clear required: OpenGL framebuffer is undefined between frames.
_gfx->clear(0);
+ if (isMacRenderMode())
+ drawDashboardStep1();
+
// Center 416x264 animation area on screen (from original InitDejaVu)
int ox = _screenR.left + (_screenR.width() - 416) / 2;
ox = (ox / 8) * 8;
@@ -919,6 +945,7 @@ void ColonyEngine::drawAnimation() {
drawAnimationExitButton(ox, oy);
drawColonyCoder(ox, oy);
+ drawMacMenuBar();
}
// gamesprt.c DrawButton(): 30px strip below the 416x264 scene with a framed
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 31231e01a10..bb3acf3c1a8 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -43,6 +43,8 @@
#include "graphics/macgui/macwindowmanager.h"
#include "graphics/palette.h"
#include "graphics/paletteman.h"
+#include "gui/message.h"
+#include "gui/saveload.h"
#include "colony/colony.h"
#include "colony/console.h"
@@ -499,6 +501,9 @@ void ColonyEngine::menuCommandsCallback(int action, Common::String &text, void *
}
bool ColonyEngine::showSaveDialog() {
+ if (!canSaveGameStateCurrently())
+ return false;
+
_system->lockMouse(false);
CursorMan.setDefaultArrowCursor();
CursorMan.showMouse(true);
@@ -521,6 +526,12 @@ Graphics::MacMenuItem *ColonyEngine::macMenuItemForAction(int menuIndex, int act
}
void ColonyEngine::syncMacMenuChecks() {
+ const bool canSave = canSaveGameStateCurrently();
+ if (Graphics::MacMenuItem *item = macMenuItemForAction(kMenuFile, kMenuActionSave))
+ _macMenu->setEnabled(item, canSave);
+ if (Graphics::MacMenuItem *item = macMenuItemForAction(kMenuFile, kMenuActionSaveAs))
+ _macMenu->setEnabled(item, canSave);
+
// inits.c:50-53. "Faster" is checked when polygon fill is OFF.
if (Graphics::MacMenuItem *item = macMenuItemForAction(kMenuOptions, kMenuActionSound))
_macMenu->setCheckMark(item, _soundOn);
@@ -533,13 +544,14 @@ void ColonyEngine::syncMacMenuChecks() {
}
void ColonyEngine::updateMouseCapture(bool recenter) {
- _system->lockMouse(_mouseLocked);
- CursorMan.showMouse(!_mouseLocked);
+ const bool mouseLocked = _mouseLocked && !_animationRunning;
+ _system->lockMouse(mouseLocked);
+ CursorMan.showMouse(!mouseLocked);
int cursorMode = 0;
- if (!_mouseLocked && isMacRenderMode() && _wm) {
- if (_macCrossCursor) {
+ if (!mouseLocked && isMacRenderMode() && _wm) {
+ if (_macCrossCursor && !_animationRunning) {
cursorMode = 1;
_wm->replaceCursor(Graphics::kMacCursorCustom, _macCrossCursor);
} else if (_macArrowCursor) {
@@ -564,9 +576,9 @@ void ColonyEngine::updateMouseCapture(bool recenter) {
}
_lastLoggedCursorMode = cursorMode;
}
- CursorMan.showMouse(!_mouseLocked);
+ CursorMan.showMouse(!mouseLocked);
- if (_mouseLocked && recenter) {
+ if (mouseLocked && recenter) {
_mousePos = Common::Point(_centerX, _centerY);
warpMouseLogical(_centerX, _centerY);
_system->getEventManager()->purgeMouseEvents();
@@ -587,26 +599,44 @@ void ColonyEngine::handleMenuAction(int action) {
case kMenuActionAbout:
runMacAbout();
break;
- // gmain.c DoCommand: New, Open and Quit all go through QSave() first, so
- // an unsaved game is never dropped on the floor. Yes saves and falls
- // through to the action, No goes straight to it, Cancel does nothing.
+ // QSave() precedes New, Open and Quit, except when an animation prevents saving.
case kMenuActionNew: {
- const int answer = runMacSaveQuery();
+ const int answer = _animationRunning ? 2 : runMacSaveQuery();
if (answer == 1)
showSaveDialog();
- if (answer)
- startNewGame();
+ if (answer) {
+ if (_animationRunning)
+ _pendingMenuAction = kMenuActionNew;
+ else
+ startNewGame();
+ }
break;
}
case kMenuActionOpen: {
- const int answer = runMacSaveQuery();
+ const int answer = _animationRunning ? 2 : runMacSaveQuery();
if (answer == 1)
showSaveDialog();
if (answer) {
_system->lockMouse(false);
CursorMan.setDefaultArrowCursor();
CursorMan.showMouse(true);
- loadGameDialog();
+ if (_animationRunning) {
+ // Select now, but replace the game only after animation callers return.
+ if (_macMenu)
+ _macMenu->closeMenu();
+ GUI::SaveLoadChooser dialog(false);
+ int slot;
+ {
+ PauseToken pause = pauseEngine();
+ slot = dialog.runModalWithCurrentTarget();
+ }
+ if (slot >= 0) {
+ _pendingLoadSlot = slot;
+ _pendingMenuAction = kMenuActionOpen;
+ }
+ } else {
+ loadGameDialog();
+ }
updateMouseCapture(true);
}
break;
@@ -616,7 +646,7 @@ void ColonyEngine::handleMenuAction(int action) {
showSaveDialog();
break;
case kMenuActionQuit: {
- const int answer = runMacSaveQuery();
+ const int answer = _animationRunning ? 2 : runMacSaveQuery();
// Quit only if the save went through, matching "if(saved)notDone=FALSE".
if (answer == 2 || (answer == 1 && showSaveDialog()))
quitGame();
@@ -647,6 +677,65 @@ void ColonyEngine::handleMenuAction(int action) {
default:
break;
}
+
+ if (_animationRunning && (_pendingMenuAction || shouldQuit())) {
+ _animationResult = 0;
+ _animationRunning = false;
+ }
+}
+
+bool ColonyEngine::processMacMenuEvent(const Common::Event &event) {
+ if (!_wm)
+ return false;
+
+ syncMacMenuChecks();
+ Common::Event wmEvent = event;
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONDOWN:
+ case Common::EVENT_RBUTTONUP:
+ case Common::EVENT_MBUTTONDOWN:
+ case Common::EVENT_MBUTTONUP:
+ wmEvent.mouse = eventMouseToLogical(event.mouse);
+ break;
+ default:
+ break;
+ }
+ return _wm->processEvent(wmEvent);
+}
+
+void ColonyEngine::processPendingMenuAction() {
+ const int action = _pendingMenuAction;
+ const int slot = _pendingLoadSlot;
+ _pendingMenuAction = 0;
+ _pendingLoadSlot = -1;
+
+ if (action == kMenuActionNew) {
+ startNewGame();
+ } else if (action == kMenuActionOpen) {
+ const Common::Error error = loadGameState(slot);
+ if (error.getCode() != Common::kNoError) {
+ _system->lockMouse(false);
+ CursorMan.setDefaultArrowCursor();
+ CursorMan.showMouse(true);
+ GUI::MessageDialog dialog(error.getDesc());
+ dialog.runModal();
+ }
+ }
+ updateMouseCapture(true);
+}
+
+void ColonyEngine::drawMacMenuBar() {
+ if (!_macMenu || !_menuSurface || _fullscreen)
+ return;
+
+ syncMacMenuChecks();
+ // Render through our surface; the WM's screen copies bypass OpenGL.
+ _menuSurface->fillRect(Common::Rect(_width, _height), _menuSurface->format.ARGBToColor(0, 0, 0, 0));
+ _macMenu->draw(_menuSurface, true);
+ _gfx->drawSurface(&_menuSurface->rawSurface(), 0, 0);
}
void ColonyEngine::initMacMenus() {
@@ -923,6 +1012,14 @@ Common::Error ColonyEngine::run() {
uint32 lastCenterTick = lastMoveTick;
while (!shouldQuit()) {
_frameLimiter->startFrame();
+ if (_pendingMenuAction) {
+ processPendingMenuAction();
+ mouseDX = mouseDY = 0;
+ mouseLookAccumX = mouseLookAccumY = 0.0f;
+ mouseMoved = false;
+ lastMoveTick = lastColonyTick = lastBattleTick = lastCenterTick = _system->getMillis();
+ _lastColonyThinkTime = lastColonyTick;
+ }
// Modal screens keep their layout stable until the next full redraw.
updateScreenSize();
@@ -975,27 +1072,11 @@ Common::Error ColonyEngine::run() {
}
Common::Event event;
- while (_system->getEventManager()->pollEvent(event)) {
- // Let MacWindowManager handle menu events first. The menu bar is
- // drawn into _menuSurface (engine logical coords, _widthÃ_height
- // per colony.cpp:570), so its hit-testing rects are in logical
- // space. With kSupportsArbitraryResolutions, event.mouse arrives
- // in window pixels â pass a coord-scaled copy so the WM resolves
- // menu clicks correctly. The original event is preserved for the
- // engine's own handlers downstream.
+ while (!_pendingMenuAction && _system->getEventManager()->pollEvent(event)) {
+ // Menu events use logical coordinates; keep the original event for gameplay.
if (_wm) {
bool wasMenuActive = _wm->isMenuActive();
- Common::Event wmEvent = event;
- if (event.type == Common::EVENT_MOUSEMOVE
- || event.type == Common::EVENT_LBUTTONDOWN
- || event.type == Common::EVENT_LBUTTONUP
- || event.type == Common::EVENT_RBUTTONDOWN
- || event.type == Common::EVENT_RBUTTONUP
- || event.type == Common::EVENT_MBUTTONDOWN
- || event.type == Common::EVENT_MBUTTONUP) {
- wmEvent.mouse = eventMouseToLogical(event.mouse);
- }
- if (_wm->processEvent(wmEvent)) {
+ if (processMacMenuEvent(event)) {
// WM consumed the event (menu interaction)
if (!wasMenuActive && _wm->isMenuActive()) {
_system->lockMouse(false);
@@ -1166,6 +1247,9 @@ Common::Error ColonyEngine::run() {
}
}
+ if (_pendingMenuAction)
+ continue;
+
if (mouseMoved && _mouseLocked) {
if (mouseDX != 0) {
mouseLookAccumX -= mouseDX * _mouseSensitivity;
@@ -1267,6 +1351,9 @@ Common::Error ColonyEngine::run() {
}
}
+ if (_pendingMenuAction)
+ continue;
+
if (_gameMode == kModeBattle) {
renderBattle();
drawForkliftOverlay();
@@ -1285,13 +1372,7 @@ Common::Error ColonyEngine::run() {
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) {
- _menuSurface->fillRect(Common::Rect(_width, _height), _menuSurface->format.ARGBToColor(0, 0, 0, 0));
- _macMenu->draw(_menuSurface, true);
- _gfx->drawSurface(&_menuSurface->rawSurface(), 0, 0);
- }
+ drawMacMenuBar();
_displayCount++; // Mac: count++ after Display()
_frameLimiter->delayBeforeSwap();
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 2d878c831af..b57457fe225 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -38,6 +38,7 @@
#include "graphics/surface.h"
namespace Common {
+struct Event;
class MacResManager;
class SeekableReadStreamEndian;
}
@@ -652,7 +653,12 @@ private:
Graphics::MacMenu *_macMenu = nullptr;
Graphics::ManagedSurface *_menuSurface = nullptr;
int _menuBarHeight = 0;
+ int _pendingMenuAction = 0;
+ int _pendingLoadSlot = -1;
void initMacMenus();
+ void drawMacMenuBar();
+ bool processMacMenuEvent(const Common::Event &event);
+ void processPendingMenuAction();
void loadMacCursorResources();
void handleMenuAction(int action);
static void menuCommandsCallback(int action, Common::String &text, void *data);
More information about the Scummvm-git-logs
mailing list