[Scummvm-git-logs] scummvm master -> 1f45acf529301fd0c767bff7a1f1560d47262a03

neuromancer noreply at scummvm.org
Mon Oct 12 18:25:49 UTC 2026


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

Summary:
039b205e9b COLONY: build by default
7dbf5fa1f4 COLONY: mark the game with the testing flag
1f45acf529 COLONY: add frame limiters to intro sequence steps


Commit: 039b205e9b928df7d3432a004ad0eea575df959f
    https://github.com/scummvm/scummvm/commit/039b205e9b928df7d3432a004ad0eea575df959f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-12T20:23:16+02:00

Commit Message:
COLONY: build by default

Changed paths:
    engines/colony/configure.engine


diff --git a/engines/colony/configure.engine b/engines/colony/configure.engine
index 9cc4df07430..ee31d680cfd 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" no "" "" "highres 16bit 3d" ""
+add_engine colony "The Colony" yes "" "" "highres 16bit 3d" ""


Commit: 7dbf5fa1f4e855b0b71df277abb3b2e132a6c0ae
    https://github.com/scummvm/scummvm/commit/7dbf5fa1f4e855b0b71df277abb3b2e132a6c0ae
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-12T20:23:16+02:00

Commit Message:
COLONY: mark the game with the testing flag

Changed paths:
    engines/colony/detection.cpp


diff --git a/engines/colony/detection.cpp b/engines/colony/detection.cpp
index 3840776d953..b7a6656f58b 100644
--- a/engines/colony/detection.cpp
+++ b/engines/colony/detection.cpp
@@ -54,7 +54,7 @@ const ADGameDescription gameDescriptions[] = {
 			"Zounds", "9de09af0a8d62f30d6863ff86c95cc51", 552576),
 		Common::EN_ANY,
 		Common::kPlatformMacintosh,
-		ADGF_NO_FLAGS,
+		ADGF_TESTING,
 		GUIO2(GUIO_RENDERMACINTOSH, GUIO_RENDEREGA)
 	},
 	{
@@ -65,7 +65,7 @@ const ADGameDescription gameDescriptions[] = {
 			"COL.EXE", "3fa09322759a47d817d2bda36aae9255", 250837),
 		Common::EN_ANY,
 		Common::kPlatformDOS,
-		ADGF_NO_FLAGS,
+		ADGF_TESTING,
 		GUIO2(GUIO_RENDEREGA, GUIO_RENDERMACINTOSH)
 	},
 	AD_TABLE_END_MARKER


Commit: 1f45acf529301fd0c767bff7a1f1560d47262a03
    https://github.com/scummvm/scummvm/commit/1f45acf529301fd0c767bff7a1f1560d47262a03
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-12T20:23:16+02:00

Commit Message:
COLONY: add frame limiters to intro sequence steps

Changed paths:
  A engines/colony/intro_timing.h
    engines/colony/intro.cpp
    test/module.mk


diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index bab6ee7f1ce..d65a727e5ad 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -32,6 +32,7 @@
 #include "common/translation.h"
 #include "graphics/cursorman.h"
 #include "graphics/font.h"
+#include "graphics/framelimiter.h"
 #include "graphics/fonts/dosfont.h"
 #include "graphics/fonts/macfont.h"
 #include "graphics/macgui/macdialog.h"
@@ -41,11 +42,64 @@
 #include "image/pict.h"
 
 #include "colony/colony.h"
+#include "colony/intro_timing.h"
 #include "colony/renderer.h"
 #include "colony/sound.h"
 
 namespace Colony {
 
+// Execute every animation tick, including XOR drawing, but present only once
+// per batch of elapsed ticks. Slow swaps must not slow the simulation down.
+class IntroAnimation {
+public:
+	IntroAnimation(OSystem *system, Renderer *gfx, uint32 stepDuration)
+		: _system(system), _gfx(gfx), _timing(system->getMillis(), stepDuration),
+		  _frameLimiter(system, 60, false) {}
+
+	void nextStep() {
+		if (!_remainingSteps) {
+			present();
+			waitForNextStep();
+			_frameLimiter.startFrame();
+			// Sample once per batch so even a slow renderer returns to the
+			// presentation loop. Callers still check input between each tick.
+			_remainingSteps = _timing.stepsDue(_system->getMillis());
+		}
+		--_remainingSteps;
+		_timing.advance();
+		_dirty = true;
+	}
+
+	void finish() {
+		present();
+		// Keep the last tick visible for its remaining time, accounting for
+		// the time already spent rendering and waiting for VSync.
+		waitForNextStep();
+	}
+
+private:
+	void waitForNextStep() {
+		uint32 delay;
+		while ((delay = _timing.timeToNextStep(_system->getMillis())) != 0)
+			_system->delayMillis(delay);
+	}
+
+	void present() {
+		if (_dirty) {
+			_frameLimiter.delayBeforeSwap();
+			_gfx->copyToScreen();
+			_dirty = false;
+		}
+	}
+
+	OSystem *_system;
+	Renderer *_gfx;
+	IntroTiming _timing;
+	Graphics::FrameLimiter _frameLimiter;
+	uint32 _remainingSteps = 0;
+	bool _dirty = false;
+};
+
 class ColonyMacDialog : public Graphics::MacDialog {
 public:
 	ColonyMacDialog(Graphics::ManagedSurface *screen, Graphics::MacWindowManager *wm, int width, Graphics::MacText *mactext, int maxTextWidth, Graphics::MacDialogButtonArray *buttons, uint defaultButton)
@@ -398,24 +452,19 @@ void ColonyEngine::playIntro() {
 		// 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 {
+		if (_soundOn)
 			_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();
+		const uint32 crashStart = _system->getMillis();
+		Graphics::FrameLimiter crashLimiter(_system, 60, false);
+		while (!shouldQuit() && (_soundOn ? _sound->isPlaying() : _system->getMillis() - crashStart < 16 * 125)) {
+			crashLimiter.startFrame();
+			const uint32 frame = (_system->getMillis() - crashStart) / 125;
+			_gfx->clear(frame % 2 ? _gfx->black() : _gfx->white());
+			crashLimiter.delayBeforeSwap();
+			_gfx->copyToScreen();
 		}
+		if (_soundOn)
+			_sound->stop();
 		_gfx->clear(_gfx->black());
 		_gfx->copyToScreen();
 		delete macFont;
@@ -518,12 +567,13 @@ void ColonyEngine::playIntro() {
 		const uint32 crashFlashDurationMs = 700;
 		const uint32 crashFlashStepMs = 200;
 		const uint32 crashStart = _system->getMillis();
-		int frame = 0;
+		Graphics::FrameLimiter crashLimiter(_system, 60, false);
 		while (!shouldQuit() && _system->getMillis() - crashStart < crashFlashDurationMs) {
+			crashLimiter.startFrame();
+			const uint32 frame = (_system->getMillis() - crashStart) / crashFlashStepMs;
 			_gfx->clear(frame % 2 ? _gfx->black() : _gfx->white());
+			crashLimiter.delayBeforeSwap();
 			_gfx->copyToScreen();
-			_system->delayMillis(crashFlashStepMs);
-			frame++;
 		}
 		_sound->stop();
 		_gfx->clear(_gfx->black());
@@ -612,6 +662,7 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 	int inc = macStyle ? 4 : 8;
 	if (macStyle) {
 		// Mac: scroll the text in from below.
+		IntroAnimation scrollIn(_system, _gfx, 16);
 		for (int scrollOff = _height; scrollOff > 0 && !qt; scrollOff -= inc) {
 			if (checkSkipRequested()) {
 				qt = true;
@@ -619,18 +670,18 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 				break;
 			}
 
+			scrollIn.nextStep();
 			_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) {
+			scrollIn.finish();
 			_gfx->clear(_gfx->black());
 			for (int i = 0; i < storyLength; i++) {
 				if (strlen(story[i]) > 0)
@@ -656,6 +707,7 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 	// Phase 2: Scroll text off the top of the screen
 	// Original: scrollRect continues moving up, text slides upward
 	if (!qt) {
+		IntroAnimation scrollOut(_system, _gfx, 16);
 		for (int scrollOff = 0; scrollOff > -_height && !qt; scrollOff -= inc) {
 			if (checkSkipRequested()) {
 				qt = true;
@@ -663,15 +715,16 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 				break;
 			}
 
+			scrollOut.nextStep();
 			_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);
 		}
+		if (!qt)
+			scrollOut.finish();
 	}
 
 	// DOS stops BeamMe here; Mac lets it continue and waits in playIntro().
@@ -745,6 +798,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 	// Animate: original loops ~200 frames or until Mars sound repeats 2x.
 	// With btn set (About) it streaks on until the user clicks, and unlike the
 	// skip path that click still falls through to the fade-out below.
+	IntroAnimation stars(_system, _gfx, 16);
 	for (int k = 0; btn || k < 120; k++) {
 		if (btn) {
 			if (checkClickRequested())
@@ -754,6 +808,7 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 			return true;
 		}
 
+		stars.nextStep();
 		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], 0xFFFFFFFF);
@@ -781,20 +836,21 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 			// Draw new star position
 			_gfx->drawLine(xsave1[i], ysave1[i], xsave2[i], ysave2[i], 0xFFFFFFFF);
 		}
-		_gfx->copyToScreen();
-		_system->delayMillis(16);
 	}
+	stars.finish();
 
 	// Fade-out phase: stars fly off without resetting (trails accumulate via XOR)
 	int nstars = 2 * ((MAXSTAR - 0x030) / deltapd);
 	if (nstars > 200)
 		nstars = 200;
+	IntroAnimation departure(_system, _gfx, 8);
 	for (int k = 0; k < nstars; k++) {
 		if (checkSkipRequested()) {
 			_gfx->setXorMode(false);
 			return true;
 		}
 
+		departure.nextStep();
 		for (int i = 0; i < NSTARS; i++) {
 			int d = dist[i];
 			int s = xang[i];
@@ -816,9 +872,8 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 				_gfx->drawLine(x1, y1, x2, y2, 0xFFFFFFFF);
 			}
 		}
-		_gfx->copyToScreen();
-		_system->delayMillis(8);
 	}
+	departure.finish();
 
 	_gfx->setXorMode(false);
 	return false;
@@ -841,12 +896,17 @@ bool ColonyEngine::makeBlackHole() {
 	for (int i = 1; i < 1024; i++)
 		rtable[i] = (128 * 128) / i;
 
+	IntroAnimation spiral(_system, _gfx, 16);
 	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) {
+			if (checkSkipRequested())
+				return true;
+			spiral.nextStep();
+
 			// 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.
@@ -881,17 +941,9 @@ bool ColonyEngine::makeBlackHole() {
 
 				_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();
+	spiral.finish();
 	return false;
 }
 
@@ -965,12 +1017,14 @@ bool ColonyEngine::makePlanet() {
 	_gfx->copyToScreen();
 
 	// Phase 2: rotate the planet in place (25 frames)
+	IntroAnimation rotation(_system, _gfx, 33);
 	for (int frame = 0; frame < 25; frame++) {
 		if (checkSkipRequested()) {
 			_gfx->setXorMode(false);
 			return true;
 		}
 
+		rotation.nextStep();
 		save = 0;
 		for (int j = 0; j < 256; j += PDELTA) {
 			for (int k = start, l = dstart; k < 128; k += PDELTA, l += PDELTA) {
@@ -999,18 +1053,19 @@ bool ColonyEngine::makePlanet() {
 		dstart++;
 		if (dstart == PDELTA)
 			dstart = 0;
-		_gfx->copyToScreen();
-		_system->delayMillis(33);
 	}
+	rotation.finish();
 
 	// Phase 3: planet approaches camera (zoom from dist 800 to 32)
 	starcnt = 0;
+	IntroAnimation approach(_system, _gfx, 16);
 	for (int i = 800; i > 32; i -= 16) {
 		if (checkSkipRequested()) {
 			_gfx->setXorMode(false);
 			return true;
 		}
 
+		approach.nextStep();
 		// Erase stars as planet passes them
 		for (int m = 0; m < 4 && starcnt < STAR_COUNT; m++) {
 			_gfx->setPixel(xstars[starcnt], ystars[starcnt], 0xFFFFFFFF);
@@ -1045,9 +1100,8 @@ bool ColonyEngine::makePlanet() {
 		dstart++;
 		if (dstart == PDELTA)
 			dstart = 0;
-		_gfx->copyToScreen();
-		_system->delayMillis(16);
 	}
+	approach.finish();
 
 	_gfx->setXorMode(false);
 	return false;
@@ -1329,24 +1383,24 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 	_gfx->copyToScreen();
 
 	// Phase 1: Scroll text in from the right to center.
-	// Original steps are one pixel on Mac and 16 on DOS. Double the intro's
-	// distance per frame so it also scrolls twice as fast with VSync enabled.
+	// Original steps are one pixel on Mac and 16 on DOS. The intro moves
+	// twice that distance per tick, independently of the display frame rate.
 	int targetX = (_width - swidth) / 2;
 	const int startX = macStyle ? _width : (_width + 16);
 	const int stepX = (macStyle ? 1 : 16) * (gameOver ? 1 : 2);
 	const int endX = macStyle ? -swidth : (-swidth - 16);
 	const uint32 scrollDelayMs = macStyle ? 8 : (1000 / 60);
 
+	IntroAnimation scrollIn(_system, _gfx, scrollDelayMs);
 	for (int x = startX; x > targetX;) {
+		if (checkSkipRequested())
+			return true;
+		scrollIn.nextStep();
 		x = MAX(targetX, x - stepX);
 		_gfx->fillRect(textBand, 0);
 		_gfx->drawString(font, str, x, centery + 2, textIndex, Graphics::kTextAlignLeft);
-		_gfx->copyToScreen();
-
-		if (checkSkipRequested())
-			return true;
-		_system->delayMillis(scrollDelayMs);
 	}
+	scrollIn.finish();
 
 	// Wait for each cue before the next flash.
 	_sound->stop();
@@ -1382,15 +1436,15 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 	// Original intro.c TimeSquare line 323: PlayMars() restarts the loop.
 	if (macStyle)
 		_sound->play(Sound::kMars, true);
+	IntroAnimation scrollOut(_system, _gfx, scrollDelayMs);
 	for (int x = targetX; x > endX; x -= stepX) {
-		_gfx->fillRect(textBand, 0);
-		_gfx->drawString(font, str, x, centery + 2, textIndex, Graphics::kTextAlignLeft);
-		_gfx->copyToScreen();
-
 		if (checkSkipRequested())
 			return true;
-		_system->delayMillis(scrollDelayMs);
+		scrollOut.nextStep();
+		_gfx->fillRect(textBand, 0);
+		_gfx->drawString(font, str, x, centery + 2, textIndex, Graphics::kTextAlignLeft);
 	}
+	scrollOut.finish();
 
 	return false;
 }
diff --git a/engines/colony/intro_timing.h b/engines/colony/intro_timing.h
new file mode 100644
index 00000000000..e2a944d0e37
--- /dev/null
+++ b/engines/colony/intro_timing.h
@@ -0,0 +1,58 @@
+/* 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_INTRO_TIMING_H
+#define COLONY_INTRO_TIMING_H
+
+#include "common/scummsys.h"
+
+namespace Colony {
+
+// Keep simulation deadlines independent of drawing, presentation and sleep time.
+class IntroTiming {
+public:
+	IntroTiming(uint32 startTime, uint32 stepDuration)
+		: _nextStepTime(startTime), _stepDuration(stepDuration) {
+		assert(stepDuration > 0);
+	}
+
+	uint32 stepsDue(uint32 now) const {
+		const int32 elapsed = now - _nextStepTime;
+		return elapsed >= 0 ? 1 + uint32(elapsed) / _stepDuration : 0;
+	}
+
+	uint32 timeToNextStep(uint32 now) const {
+		const int32 remaining = _nextStepTime - now;
+		return remaining > 0 ? uint32(remaining) : 0;
+	}
+
+	void advance() {
+		_nextStepTime += _stepDuration;
+	}
+
+private:
+	uint32 _nextStepTime;
+	const uint32 _stepDuration;
+};
+
+} // End of namespace Colony
+
+#endif
diff --git a/test/module.mk b/test/module.mk
index 171ac91a6e5..e889d5079f6 100644
--- a/test/module.mk
+++ b/test/module.mk
@@ -40,6 +40,10 @@ endif
 # libcommon needs libformats and libformats needs libcommon: so libcommon is put twice
 TEST_LIBS +=	audio/libaudio.a math/libmath.a common/libcommon.a common/formats/libformats.a common/compression/libcompression.a common/libcommon.a image/libimage.a graphics/libgraphics.a
 
+ifeq ($(ENABLE_COLONY), STATIC_PLUGIN)
+	TESTS += $(srcdir)/test/engines/colony/*.h
+endif
+
 ifeq ($(ENABLE_WINTERMUTE), STATIC_PLUGIN)
 	TESTS += $(srcdir)/test/engines/wintermute/*.h
 	TEST_LIBS += engines/wintermute/libwintermute.a




More information about the Scummvm-git-logs mailing list