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

neuromancer noreply at scummvm.org
Mon Jul 13 19:52:11 UTC 2026


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

Summary:
e9bc2e1b28 COLONY: allow to exit the animation using the exit button in mac
f343066328 COLONY: added decoder window to allow player to type the correct symbols


Commit: e9bc2e1b282677cd4e106153e22507349493a547
    https://github.com/scummvm/scummvm/commit/e9bc2e1b282677cd4e106153e22507349493a547
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-13T21:51:56+02:00

Commit Message:
COLONY: allow to exit the animation using the exit button in mac

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


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 4636d660682..b257543733b 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -32,6 +32,10 @@
 #include "common/stream.h"
 #include "common/system.h"
 #include "graphics/cursorman.h"
+#include "graphics/fontman.h"
+#include "graphics/macgui/macfontmanager.h"
+#include "graphics/macgui/macwindowmanager.h"
+#include "graphics/managed_surface.h"
 #include "graphics/surface.h"
 
 #include "colony/colony.h"
@@ -395,6 +399,8 @@ void ColonyEngine::playAnimation() {
 	_rotateRight = false;
 
 	_animationRunning = true;
+	_animExitPressed = _animExitInside = false;
+	_animExitStrip = _animExitButton = Common::Rect();
 	_system->lockMouse(false);
 	warpMouseLogical(_centerX, _centerY);
 	const char *cursorName = "default arrow cursor";
@@ -590,9 +596,27 @@ void ColonyEngine::playAnimation() {
 				_gfx->computeScreenViewport();
 				needsDraw = true;
 			} else if (event.type == Common::EVENT_LBUTTONDOWN) {
-				int item = whichSprite(eventMouseToLogical(event.mouse));
-				if (item > 0) {
-					handleAnimationClick(item);
+				const Common::Point pt = eventMouseToLogical(event.mouse);
+				if (!_animExitStrip.isEmpty() && _animExitStrip.contains(pt)) {
+					// gamesprt.c TestButton(): strip clicks never reach sprites
+					if (_animExitButton.contains(pt)) {
+						_animExitPressed = _animExitInside = true;
+						needsDraw = true;
+					}
+				} else {
+					int item = whichSprite(pt);
+					if (item > 0) {
+						handleAnimationClick(item);
+						needsDraw = true;
+					}
+				}
+			} else if (event.type == Common::EVENT_LBUTTONUP) {
+				if (_animExitPressed) {
+					if (_animExitInside) {
+						debugC(1, kColonyDebugAnimation, "Animation: EXIT button");
+						_animationRunning = false;
+					}
+					_animExitPressed = _animExitInside = false;
 					needsDraw = true;
 				}
 			} else if (event.type == Common::EVENT_RBUTTONDOWN) {
@@ -602,6 +626,13 @@ void ColonyEngine::playAnimation() {
 				_animationRunning = false;
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
 				const Common::Point logical = eventMouseToLogical(event.mouse);
+				if (_animExitPressed) {
+					const bool inside = _animExitButton.contains(logical);
+					if (inside != _animExitInside) {
+						_animExitInside = inside;
+						needsDraw = true;
+					}
+				}
 				debugC(5, kColonyDebugAnimation, "Animation Mouse: %d, %d", logical.x, logical.y);
 			} else if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
 				if (event.customType == kActionEscape) {
@@ -820,6 +851,49 @@ void ColonyEngine::drawAnimation() {
 		if (_lSprites[i]->onoff)
 			drawComplexSprite(i, ox, oy);
 	}
+
+	drawAnimationExitButton(ox, oy);
+}
+
+// gamesprt.c DrawButton(): 30px strip below the 416x264 scene with a framed
+// "EXIT" default button; TestButton() inverts it while pressed inside.
+void ColonyEngine::drawAnimationExitButton(int ox, int oy) {
+	if (!isMacRenderMode())
+		return;
+
+	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)
+		return;
+
+	Graphics::ManagedSurface strip;
+	strip.create(416, 30, _gfx->getPixelFormat());
+	const uint32 white = strip.format.ARGBToColor(255, 255, 255, 255);
+	const uint32 black = strip.format.ARGBToColor(255, 0, 0, 0);
+	strip.fillRect(Common::Rect(0, 0, 416, 30), white);
+	strip.hLine(0, 0, 415, black);
+
+	const int wd = font->getStringWidth("EXIT") / 2;
+	const Common::Rect button(208 - (wd + 15), 6, 208 + (wd + 15), 24);
+	const bool invert = _animExitPressed && _animExitInside;
+	strip.drawRoundRect(button, 8, black, invert);
+	Common::Rect ring(button.left - 3, button.top - 3, button.right + 3, button.bottom + 3);
+	strip.drawRoundRect(ring, 11, black, false);
+	ring.grow(-1);
+	strip.drawRoundRect(ring, 10, black, false);
+
+	const int textY = button.top + (button.height() - font->getFontHeight()) / 2 + 1;
+	font->drawString(&strip, "EXIT", button.left, textY, button.width(),
+		invert ? white : black, Graphics::kTextAlignCenter);
+
+	_gfx->drawSurface(&strip.rawSurface(), ox, oy + 264);
+	strip.free();
+
+	_animExitStrip = Common::Rect(ox, oy + 264, ox + 416, oy + 294);
+	_animExitButton = button;
+	_animExitButton.translate(ox, oy + 264);
 }
 
 void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 294f276aba2..2d87254937c 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -802,6 +802,10 @@ private:
 	Common::Array<int16> _animBMColors;
 	bool _animationRunning;
 	int _animationResult;
+	bool _animExitPressed = false;
+	bool _animExitInside = false;
+	Common::Rect _animExitStrip;
+	Common::Rect _animExitButton;
 	bool _doorOpen;
 	int _liftObject = 0;  // sprite index for the carried object in lift animation
 	bool _liftUp = false;     // current lift state: true=raised, false=lowered
@@ -825,6 +829,7 @@ private:
 	void playAnimation();
 	void updateAnimation();
 	void drawAnimation();
+	void drawAnimationExitButton(int ox, int oy);
 	void drawComplexSprite(int index, int ox, int oy);
 	void drawAnimationImage(Image *img, Image *mask, int x, int y, uint32 fillColor,
 			Graphics::Surface *&bakedCache, uint64 &bakedCacheKey);


Commit: f34306632812142ccf2c6b26eebdec0e44db30e2
    https://github.com/scummvm/scummvm/commit/f34306632812142ccf2c6b26eebdec0e44db30e2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-13T21:51:56+02:00

Commit Message:
COLONY: added decoder window to allow player to type the correct symbols

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 b257543733b..f2c7fca3f8f 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -401,6 +401,12 @@ void ColonyEngine::playAnimation() {
 	_animationRunning = true;
 	_animExitPressed = _animExitInside = false;
 	_animExitStrip = _animExitButton = Common::Rect();
+	_coderCursor = 0;
+	for (int i = 0; i < 4; i++)
+		_coderPick[i] = 0;
+	_coderWin = Common::Rect();
+	_coderPressed = -1;
+	_coderPressInside = false;
 	_system->lockMouse(false);
 	warpMouseLogical(_centerX, _centerY);
 	const char *cursorName = "default arrow cursor";
@@ -597,7 +603,9 @@ void ColonyEngine::playAnimation() {
 				needsDraw = true;
 			} else if (event.type == Common::EVENT_LBUTTONDOWN) {
 				const Common::Point pt = eventMouseToLogical(event.mouse);
-				if (!_animExitStrip.isEmpty() && _animExitStrip.contains(pt)) {
+				if (handleColonyCoderClick(pt)) {
+					needsDraw = true;
+				} else if (!_animExitStrip.isEmpty() && _animExitStrip.contains(pt)) {
 					// gamesprt.c TestButton(): strip clicks never reach sprites
 					if (_animExitButton.contains(pt)) {
 						_animExitPressed = _animExitInside = true;
@@ -611,6 +619,20 @@ void ColonyEngine::playAnimation() {
 					}
 				}
 			} else if (event.type == Common::EVENT_LBUTTONUP) {
+				if (_coderPressed >= 0) {
+					if (_coderPressInside) {
+						if (_coderCursor >= 4) {
+							for (int j = 0; j < 4; j++)
+								_coderPick[j] = 0;
+							_coderCursor = 0;
+						}
+						_coderPick[_coderCursor++] = _coderPressed + 1;
+						_sound->play(Sound::kDit);
+					}
+					_coderPressed = -1;
+					_coderPressInside = false;
+					needsDraw = true;
+				}
 				if (_animExitPressed) {
 					if (_animExitInside) {
 						debugC(1, kColonyDebugAnimation, "Animation: EXIT button");
@@ -626,6 +648,13 @@ void ColonyEngine::playAnimation() {
 				_animationRunning = false;
 			} else if (event.type == Common::EVENT_MOUSEMOVE) {
 				const Common::Point logical = eventMouseToLogical(event.mouse);
+				if (_coderPressed >= 0) {
+					const bool inside = _coderIconRects[_coderPressed].contains(logical);
+					if (inside != _coderPressInside) {
+						_coderPressInside = inside;
+						needsDraw = true;
+					}
+				}
 				if (_animExitPressed) {
 					const bool inside = _animExitButton.contains(logical);
 					if (inside != _animExitInside) {
@@ -853,6 +882,7 @@ void ColonyEngine::drawAnimation() {
 	}
 
 	drawAnimationExitButton(ox, oy);
+	drawColonyCoder(ox, oy);
 }
 
 // gamesprt.c DrawButton(): 30px strip below the 416x264 scene with a framed
@@ -896,6 +926,226 @@ void ColonyEngine::drawAnimationExitButton(int ox, int oy) {
 	_animExitButton.translate(ox, oy + 264);
 }
 
+void ColonyEngine::loadCoderTiles() {
+	_coderTilesLoaded = true;
+
+	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(Common::Path("security"));
+	if (!file)
+		return;
+	Common::SeekableReadStreamEndianWrapper stream(file, true, DisposeAfterUse::YES);
+
+	byte bg[8];
+	stream.read(bg, 8);
+	stream.read(bg, 8);
+	stream.readSint16();
+	if (stream.readSint16() != 0) {
+		readRect(stream);
+		readRect(stream);
+		delete loadImage(stream);
+		delete loadImage(stream);
+	}
+
+	Common::Array<Image *> imgs;
+	const int16 maxsprite = stream.readSint16();
+	stream.readSint16();
+	for (int i = 0; i < maxsprite && !stream.err(); i++) {
+		imgs.push_back(loadImage(stream));
+		delete loadImage(stream); // mask
+		stream.readSint16();
+		readRect(stream);
+		readRect(stream);
+	}
+
+	// Object 2 (a digit key) provides the button faces (states 1..2),
+	// object 27 the four symbol tiles (states 2..5).
+	const int16 maxl = stream.readSint16();
+	stream.readSint16();
+	for (int i = 0; i < maxl && !stream.err(); i++) {
+		const int16 size = stream.readSint16();
+		for (int j = 0; j < size; j++) {
+			const int16 spritenum = stream.readSint16();
+			stream.readSint16();
+			stream.readSint16();
+			if (stream.err() || spritenum < 0 || spritenum >= (int16)imgs.size())
+				continue;
+			if (i == 1 && j == 0 && !_coderBtnUp) {
+				_coderBtnUp = imgs[spritenum];
+				imgs[spritenum] = nullptr;
+			} else if (i == 1 && j == 1 && !_coderBtnDown) {
+				_coderBtnDown = imgs[spritenum];
+				imgs[spritenum] = nullptr;
+			} else if (i == 26 && j >= 1 && j <= 4 && !_coderTiles[j - 1]) {
+				_coderTiles[j - 1] = imgs[spritenum];
+				imgs[spritenum] = nullptr;
+			}
+		}
+		readRect(stream);
+		for (int k = 0; k < 7; k++)
+			stream.readSint16();
+		stream.readByte();
+		stream.readByte();
+		stream.readByte();
+		stream.readSint16();
+		stream.readSint16();
+		stream.readSint16();
+	}
+
+	for (uint i = 0; i < imgs.size(); i++)
+		delete imgs[i];
+}
+
+static void drawCoderTile(Graphics::ManagedSurface &s, const Image *img, int x, int y, uint32 fg, uint32 bg) {
+	if (!img || !img->data)
+		return;
+	for (int r = 0; r < img->height; r++) {
+		for (int c = 0; c < img->width; c++) {
+			const bool on = (img->data[r * img->rowBytes + (c >> 3)] >> (7 - (c & 7))) & 1;
+			s.setPixel(x + c, y + r, on ? fg : bg);
+		}
+	}
+}
+
+void ColonyEngine::drawColonyCoder(int animOx, int animOy) {
+	if (!isMacRenderMode())
+		return;
+	if (_animationName != "security" && _animationName != "reactor")
+		return;
+
+	if (!_coderTilesLoaded)
+		loadCoderTiles();
+	if (!_coderTiles[0] || !_coderTiles[1] || !_coderTiles[2] || !_coderTiles[3] ||
+			!_coderBtnUp || !_coderBtnDown)
+		return;
+
+	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)
+		return;
+	Graphics::MacFont genevaFont(Graphics::kMacFontGeneva, 9);
+	const Graphics::Font *instrFont = (_wm && _wm->_fontMan) ? _wm->_fontMan->getFont(genevaFont) : nullptr;
+	if (!instrFont)
+		instrFont = font;
+
+	const int contentW = 172;
+	const char *instrText = (_animationName == "security")
+		? "Press the symbols as shown on the display to determine the correct value."
+		: "Press the symbols from the desk in reverse order to determine the correct value.";
+	Common::Array<Common::U32String> instrLines;
+	instrFont->wordWrapText(Common::U32String(instrText), contentW - 16, instrLines);
+	const int instrLineH = instrFont->getFontHeight() + 1;
+
+	const int btnSize = _coderBtnUp->width;
+	const int tile = _coderTiles[0]->width;
+	const int pitch = btnSize + 4;
+	const int x0 = (contentW - (3 * pitch + btnSize)) / 2;
+	const int btnY = 8;
+	const int slotY = btnY + btnSize + 9;
+	const int codeY = slotY + tile + 16;
+	const int instrY = codeY + font->getFontHeight() + 8;
+	const int contentH = instrY + (int)instrLines.size() * instrLineH + 8;
+
+	Graphics::MacWindowBorder border;
+	Graphics::BorderOffsets offsets;
+	bool hasBorder = false;
+	if (_wm) {
+		border.setWindowManager(_wm);
+		border.setBorderType(Graphics::kWindowWindow);
+		if (border.hasBorder(Graphics::kWindowBorderActive) && border.hasOffsets()) {
+			hasBorder = true;
+			offsets = border.getOffset();
+		}
+	}
+	const int offL = hasBorder ? offsets.left : 2;
+	const int offR = hasBorder ? offsets.right : 2;
+	const int offT = hasBorder ? offsets.top : 2;
+	const int offB = hasBorder ? offsets.bottom : 2;
+	const int winW = contentW + offL + offR;
+	const int winH = contentH + offT + offB;
+
+	// Right of the scene, vertically centered on the 416x294 animation window
+	int wx = animOx + 416 + 4;
+	if (wx + winW > _screenR.right)
+		wx = MAX<int>(_screenR.left, _screenR.right - winW - 2);
+	int wy = animOy + (294 - winH) / 2;
+	wy = MAX<int>(_screenR.top, wy);
+
+	Graphics::ManagedSurface win;
+	win.create(winW, winH, _gfx->getPixelFormat());
+	const uint32 white = win.format.ARGBToColor(255, 255, 255, 255);
+	const uint32 black = win.format.ARGBToColor(255, 0, 0, 0);
+	win.fillRect(Common::Rect(0, 0, winW, winH), white);
+	if (hasBorder) {
+		border.setTitle("Colony Coder", winW);
+		border.blitBorderInto(win, Graphics::kWindowBorderActive);
+	} else {
+		win.frameRect(Common::Rect(0, 0, winW, winH), black);
+		win.frameRect(Common::Rect(1, 1, winW - 1, winH - 1), black);
+	}
+
+	// Keypad buttons with the symbol tile covering the digit
+	const int inset = (btnSize - tile) / 2;
+	for (int i = 0; i < 4; i++) {
+		const int bx = offL + x0 + i * pitch;
+		const bool pressed = (_coderPressed == i && _coderPressInside);
+		drawCoderTile(win, pressed ? _coderBtnDown : _coderBtnUp, bx, offT + btnY, black, white);
+		drawCoderTile(win, _coderTiles[i], bx + inset, offT + btnY + inset,
+			pressed ? white : black, pressed ? black : white);
+		_coderIconRects[i] = Common::Rect(wx + bx, wy + offT + btnY, wx + bx + btnSize, wy + offT + btnY + btnSize);
+
+		const int sx = bx + inset;
+		const Common::Rect slot(sx - 2, offT + slotY - 2, sx + tile + 2, offT + slotY + tile + 2);
+		win.frameRect(slot, black);
+		if (_coderPick[i] > 0)
+			drawCoderTile(win, _coderTiles[_coderPick[i] - 1], sx, offT + slotY, black, white);
+	}
+
+	if (_coderCursor < 4) {
+		const int cx = offL + x0 + _coderCursor * pitch + inset;
+		win.fillRect(Common::Rect(cx, offT + slotY + tile + 6, cx + tile, offT + slotY + tile + 8), black);
+	}
+
+	bool complete = true;
+	for (int i = 0; i < 4; i++) {
+		if (_coderPick[i] == 0)
+			complete = false;
+	}
+	Common::String text;
+	if (complete) {
+		uint8 code[6];
+		cryptArray(code, _coderPick[0] - 1, _coderPick[1] - 1, _coderPick[2] - 1, _coderPick[3] - 1);
+		for (int i = 0; i < 6; i++)
+			text += (char)('0' + code[i] - 2);
+	} else {
+		text = "- - - - - -";
+	}
+	font->drawString(&win, text, offL, offT + codeY, contentW, black, Graphics::kTextAlignCenter);
+
+	for (uint i = 0; i < instrLines.size(); i++)
+		instrFont->drawString(&win, instrLines[i], offL + 8, offT + instrY + (int)i * instrLineH,
+			contentW - 16, black, Graphics::kTextAlignCenter);
+
+	_gfx->drawSurface(&win.rawSurface(), wx, wy);
+	win.free();
+
+	_coderWin = Common::Rect(wx, wy, wx + winW, wy + winH);
+}
+
+// Mouse-down half of the button tracking; commit happens on mouse-up.
+bool ColonyEngine::handleColonyCoderClick(const Common::Point &pt) {
+	if (_coderWin.isEmpty() || !_coderWin.contains(pt))
+		return false;
+	for (int i = 0; i < 4; i++) {
+		if (_coderIconRects[i].contains(pt)) {
+			_coderPressed = i;
+			_coderPressInside = true;
+			break;
+		}
+	}
+	return true;
+}
+
 void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
 	ComplexSprite *ls = _lSprites[index];
 	if (!ls->onoff)
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c721852df1b..e20b86d44dc 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -288,6 +288,10 @@ ColonyEngine::~ColonyEngine() {
 			delete _flIconSurf[i];
 		}
 	}
+	for (int i = 0; i < 4; i++)
+		delete _coderTiles[i];
+	delete _coderBtnUp;
+	delete _coderBtnDown;
 	delete _frameLimiter;
 	delete _gfx;
 	delete _sound;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 2d87254937c..32a68aa8a37 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -806,6 +806,16 @@ private:
 	bool _animExitInside = false;
 	Common::Rect _animExitStrip;
 	Common::Rect _animExitButton;
+	int _coderPick[4] = {};
+	int _coderCursor = 0;
+	int _coderPressed = -1;
+	bool _coderPressInside = false;
+	Image *_coderTiles[4] = {};
+	Image *_coderBtnUp = nullptr;
+	Image *_coderBtnDown = nullptr;
+	bool _coderTilesLoaded = false;
+	Common::Rect _coderWin;
+	Common::Rect _coderIconRects[4];
 	bool _doorOpen;
 	int _liftObject = 0;  // sprite index for the carried object in lift animation
 	bool _liftUp = false;     // current lift state: true=raised, false=lowered
@@ -830,6 +840,9 @@ private:
 	void updateAnimation();
 	void drawAnimation();
 	void drawAnimationExitButton(int ox, int oy);
+	void drawColonyCoder(int animOx, int animOy);
+	void loadCoderTiles();
+	bool handleColonyCoderClick(const Common::Point &pt);
 	void drawComplexSprite(int index, int ox, int oy);
 	void drawAnimationImage(Image *img, Image *mask, int x, int y, uint32 fillColor,
 			Graphics::Surface *&bakedCache, uint64 &bakedCacheKey);




More information about the Scummvm-git-logs mailing list