[Scummvm-git-logs] scummvm master -> 42aaca95f4e3ef66184d69d3ce743ee0523ded11
neuromancer
noreply at scummvm.org
Fri Mar 27 07:08:18 UTC 2026
This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
845687b241 FREESCAPE: improved renderPlayerShootBall for all renders
8469bac0e5 FREESCAPE: added code to implement heartbeat in eclipse dos (ega)
81f54212c2 FREESCAPE: added missing sound table in eclipse dos (cga)
69e0ff5550 FREESCAPE: added missing indicators in driller amiga
9668a661bb FREESCAPE: load vehicle indicator indicators from game data in driller amiga
42aaca95f4 FREESCAPE: quit animation button in driller amiga
Commit: 845687b24138c45ce2cbc7bfe6b75627ad4d8dfa
https://github.com/scummvm/scummvm/commit/845687b24138c45ce2cbc7bfe6b75627ad4d8dfa
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-27T08:08:04+01:00
Commit Message:
FREESCAPE: improved renderPlayerShootBall for all renders
Changed paths:
engines/freescape/gfx_opengl.cpp
engines/freescape/gfx_opengl_shaders.cpp
engines/freescape/gfx_tinygl.cpp
engines/freescape/movement.cpp
diff --git a/engines/freescape/gfx_opengl.cpp b/engines/freescape/gfx_opengl.cpp
index 40ce2ca2186..7f11d3e750c 100644
--- a/engines/freescape/gfx_opengl.cpp
+++ b/engines/freescape/gfx_opengl.cpp
@@ -490,18 +490,23 @@ void OpenGLRenderer::renderPlayerShootBall(byte color, const Common::Point &posi
glColor4ub(r, g, b, 255);
int triangleAmount = 20;
float twicePi = (float)(2.0 * M_PI);
- float coef = (9 - frame) / 9.0;
- float radius = (1 - coef) * 4.0;
- Common::Point initial_position(viewArea.left + viewArea.width() / 2 + 2, viewArea.height() + viewArea.top);
- Common::Point ball_position = coef * position + (1 - coef) * initial_position;
+ // Exponential ease-out trajectory inspired by the original ZX animation.
+ // The stone shrinks as it flies into the screen (perspective).
+ float coef = 1.0f - powf(0.5f, (8 - frame + 1) / 2.0f);
+ float radius = 1.0f + frame * 0.5f;
+
+ float startX = viewArea.left + viewArea.width() / 2.0f + 2;
+ float startY = viewArea.height() + viewArea.top;
+ float ballX = coef * position.x + (1.0f - coef) * startX;
+ float ballY = coef * position.y + (1.0f - coef) * startY;
glEnableClientState(GL_VERTEX_ARRAY);
- copyToVertexArray(0, Math::Vector3d(ball_position.x, ball_position.y, 0));
+ copyToVertexArray(0, Math::Vector3d(ballX, ballY, 0));
for (int i = 0; i <= triangleAmount; i++) {
- float x = ball_position.x + (radius * cos(i * twicePi / triangleAmount));
- float y = ball_position.y + (radius * sin(i * twicePi / triangleAmount));
+ float x = ballX + (radius * cos(i * twicePi / triangleAmount));
+ float y = ballY + (radius * sin(i * twicePi / triangleAmount));
copyToVertexArray(i + 1, Math::Vector3d(x, y, 0));
}
diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index 9ed3d1464d5..2ead02a73ea 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -375,19 +375,23 @@ void OpenGLShaderRenderer::renderPlayerShootBall(byte color, const Common::Point
int triangleAmount = 20;
float twicePi = (float)(2.0 * M_PI);
- float coef = (9 - frame) / 9.0;
- float radius = (1 - coef) * 4.0;
- Common::Point position(_position.x, _screenH - _position.y);
+ // Exponential ease-out trajectory inspired by the original ZX animation.
+ float coef = 1.0f - powf(0.5f, (8 - frame + 1) / 2.0f);
+ float radius = 1.0f + frame * 0.5f;
- Common::Point initial_position(viewArea.left + viewArea.width() / 2 + 2, _screenH - (viewArea.height() + viewArea.top));
- Common::Point ball_position = coef * position + (1 - coef) * initial_position;
+ float posX = (float)_position.x;
+ float posY = (float)(_screenH - _position.y);
+ float startX = viewArea.left + viewArea.width() / 2.0f + 2;
+ float startY = (float)(_screenH - (viewArea.height() + viewArea.top));
+ float ballX = coef * posX + (1.0f - coef) * startX;
+ float ballY = coef * posY + (1.0f - coef) * startY;
- copyToVertexArray(0, Math::Vector3d(remap(ball_position.x, _screenW), remap(ball_position.y, _screenH), 0));
+ copyToVertexArray(0, Math::Vector3d(remap(ballX, _screenW), remap(ballY, _screenH), 0));
for (int i = 0; i <= triangleAmount; i++) {
- float x = remap(ball_position.x + (radius * cos(i * twicePi / triangleAmount)), _screenW);
- float y = remap(ball_position.y + (radius * sin(i * twicePi / triangleAmount)), _screenH);
+ float x = remap(ballX + (radius * cos(i * twicePi / triangleAmount)), _screenW);
+ float y = remap(ballY + (radius * sin(i * twicePi / triangleAmount)), _screenH);
copyToVertexArray(i + 1, Math::Vector3d(x, y, 0));
}
diff --git a/engines/freescape/gfx_tinygl.cpp b/engines/freescape/gfx_tinygl.cpp
index 5b2db657cfa..92bccf8df0b 100644
--- a/engines/freescape/gfx_tinygl.cpp
+++ b/engines/freescape/gfx_tinygl.cpp
@@ -271,18 +271,22 @@ void TinyGLRenderer::renderPlayerShootBall(byte color, const Common::Point &posi
tglColor4ub(r, g, b, 255);
int triangleAmount = 20;
float twicePi = (float)(2.0 * M_PI);
- float coef = (9 - frame) / 9.0;
- float radius = (1 - coef) * 4.0;
- Common::Point initial_position(viewArea.left + viewArea.width() / 2 + 2, viewArea.height() + viewArea.top);
- Common::Point ball_position = coef * position + (1 - coef) * initial_position;
+ // Exponential ease-out trajectory inspired by the original ZX animation.
+ float coef = 1.0f - powf(0.5f, (8 - frame + 1) / 2.0f);
+ float radius = 1.0f + frame * 0.5f;
+
+ float startX = viewArea.left + viewArea.width() / 2.0f + 2;
+ float startY = viewArea.height() + viewArea.top;
+ float ballX = coef * position.x + (1.0f - coef) * startX;
+ float ballY = coef * position.y + (1.0f - coef) * startY;
tglEnableClientState(TGL_VERTEX_ARRAY);
- copyToVertexArray(0, Math::Vector3d(ball_position.x, ball_position.y, 0));
+ copyToVertexArray(0, Math::Vector3d(ballX, ballY, 0));
- for(int i = 0; i <= triangleAmount; i++) {
- float x = ball_position.x + (radius * cos(i * twicePi / triangleAmount));
- float y = ball_position.y + (radius * sin(i * twicePi / triangleAmount));
+ for (int i = 0; i <= triangleAmount; i++) {
+ float x = ballX + (radius * cos(i * twicePi / triangleAmount));
+ float y = ballY + (radius * sin(i * twicePi / triangleAmount));
copyToVertexArray(i + 1, Math::Vector3d(x, y, 0));
}
diff --git a/engines/freescape/movement.cpp b/engines/freescape/movement.cpp
index f61677c5e6c..3266cb63529 100644
--- a/engines/freescape/movement.cpp
+++ b/engines/freescape/movement.cpp
@@ -236,7 +236,7 @@ void FreescapeEngine::shoot() {
playSound(_soundIndexShoot, false, _movementSoundHandle);
g_system->delayMillis(2);
- _shootingFrames = 10;
+ _shootingFrames = 8;
// Convert to normalized coordinates [-1, 1]
float ndcX = (2.0f * (_crossairPosition.x - _viewArea.left) / _viewArea.width()) - 1.0f;
Commit: 8469bac0e5296bdea8e12d305e662a1ca707399b
https://github.com/scummvm/scummvm/commit/8469bac0e5296bdea8e12d305e662a1ca707399b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-27T08:08:04+01:00
Commit Message:
FREESCAPE: added code to implement heartbeat in eclipse dos (ega)
Changed paths:
engines/freescape/games/eclipse/dos.cpp
engines/freescape/games/eclipse/eclipse.h
diff --git a/engines/freescape/games/eclipse/dos.cpp b/engines/freescape/games/eclipse/dos.cpp
index 032e7706ada..dbb887e9e3d 100644
--- a/engines/freescape/games/eclipse/dos.cpp
+++ b/engines/freescape/games/eclipse/dos.cpp
@@ -39,6 +39,71 @@ void EclipseEngine::initDOS() {
_soundIndexAreaChange = 5;
}
+void EclipseEngine::loadHeartFramesDOS(Common::SeekableReadStream *file, int restOffset, int beatOffset) {
+ // Stores into _eclipseSprites[0] (beat) and [1] (rest).
+ int offsets[2] = { beatOffset, restOffset };
+
+ for (int f = 0; f < 2; f++) {
+ file->seek(offsets[f]);
+ int height = file->readByte();
+ int widthBytes = file->readByte();
+
+ if (_renderMode == Common::kRenderEGA) {
+ // EGA: 4 plane pointers followed by monochrome data per plane.
+ int planeFileOffsets[4];
+ for (int p = 0; p < 4; p++)
+ planeFileOffsets[p] = file->readUint16LE() + 0x200;
+
+ Graphics::ManagedSurface clut8;
+ clut8.create(widthBytes * 8, height, Graphics::PixelFormat::createFormatCLUT8());
+ clut8.fillRect(Common::Rect(0, 0, widthBytes * 8, height), 0);
+
+ for (int p = 0; p < 4; p++) {
+ Graphics::ManagedSurface plane;
+ plane.create(widthBytes * 8, height, Graphics::PixelFormat::createFormatCLUT8());
+ plane.fillRect(Common::Rect(0, 0, widthBytes * 8, height), 0);
+
+ file->seek(planeFileOffsets[p]);
+ loadFrame(file, &plane, widthBytes, height, 1);
+
+ for (int y = 0; y < height; y++)
+ for (int x = 0; x < widthBytes * 8; x++)
+ if (plane.getPixel(x, y))
+ clut8.setPixel(x, y, clut8.getPixel(x, y) | (1 << p));
+ }
+
+ clut8.setPalette((byte *)kEGADefaultPalette, 0, 16);
+
+ Graphics::Surface *converted = _gfx->convertImageFormatIfNecessary(&clut8);
+ auto *surf = new Graphics::ManagedSurface();
+ surf->copyFrom(*converted);
+ converted->free();
+ delete converted;
+ _eclipseSprites.push_back(surf);
+ } else {
+ // CGA: packed 2-bit pixels (4 pixels per byte), no planes.
+ Graphics::ManagedSurface clut8;
+ clut8.create(widthBytes * 4, height, Graphics::PixelFormat::createFormatCLUT8());
+
+ for (int y = 0; y < height; y++)
+ for (int col = 0; col < widthBytes; col++) {
+ byte b = file->readByte();
+ for (int px = 0; px < 4; px++)
+ clut8.setPixel(col * 4 + px, y, (b >> (6 - px * 2)) & 3);
+ }
+
+ clut8.setPalette((byte *)kCGAPaletteRedGreenBright, 0, 4);
+
+ Graphics::Surface *converted = _gfx->convertImageFormatIfNecessary(&clut8);
+ auto *surf = new Graphics::ManagedSurface();
+ surf->copyFrom(*converted);
+ converted->free();
+ delete converted;
+ _eclipseSprites.push_back(surf);
+ }
+ }
+}
+
void EclipseEngine::loadAssetsDOSFullGame() {
Common::File file;
if (_renderMode == Common::kRenderEGA) {
@@ -62,6 +127,8 @@ void EclipseEngine::loadAssetsDOSFullGame() {
_border = load8bitBinImage(&file, 0x210);
_border->setPalette((byte *)&kEGADefaultPalette, 0, 16);
+ loadHeartFramesDOS(&file, 0x76AB, 0x76FD);
+
_indicators.push_back(loadBundledImage("eclipse_ankh_indicator"));
for (auto &it : _indicators)
@@ -85,6 +152,8 @@ void EclipseEngine::loadAssetsDOSFullGame() {
load8bitBinary(&file, 0x2530, 4);
_border = load8bitBinImage(&file, 0x210);
_border->setPalette((byte *)&kCGAPaletteRedGreen, 0, 4);
+ // TODO: CGA heart palette changes per area, needs re-decoding on area change
+ // loadHeartFramesDOS(&file, 0x5F52, 0x5F84);
swapPalette(_startArea);
} else
error("Invalid or unsupported render mode %s for Total Eclipse", Common::getRenderModeDescription(_renderMode));
@@ -161,6 +230,7 @@ void EclipseEngine::drawDOSUI(Graphics::Surface *surface) {
Common::Rect jarWater(124, 192 - _gameStateVars[k8bitVariableEnergy], 148, 192);
drawIndicator(surface, 41, 4, 16);
+ drawHeartIndicator(surface, 176, 168);
if (_renderMode == Common::kRenderEGA) {
surface->fillRect(jarWater, blue);
drawEclipseIndicator(surface, 228, 0, color3, color1);
diff --git a/engines/freescape/games/eclipse/eclipse.h b/engines/freescape/games/eclipse/eclipse.h
index 00d8fa8e421..6093b760ef4 100644
--- a/engines/freescape/games/eclipse/eclipse.h
+++ b/engines/freescape/games/eclipse/eclipse.h
@@ -99,6 +99,7 @@ public:
soundFx *load1bPCM(Common::SeekableReadStream *file, int offset);
void loadHeartFramesCPC(Common::SeekableReadStream *file, int restOffset, int beatOffset);
void loadHeartFramesZX(Common::SeekableReadStream *file, int restOffset, int beatOffset);
+ void loadHeartFramesDOS(Common::SeekableReadStream *file, int restOffset, int beatOffset);
void drawHeartIndicator(Graphics::Surface *surface, int x, int y);
Common::Array<byte> _musicData; // TEMUSIC.ST TEXT segment (Atari ST)
Commit: 81f54212c21ea1336f2b3005f7edb207293911f2
https://github.com/scummvm/scummvm/commit/81f54212c21ea1336f2b3005f7edb207293911f2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-27T08:08:04+01:00
Commit Message:
FREESCAPE: added missing sound table in eclipse dos (cga)
Changed paths:
engines/freescape/games/eclipse/dos.cpp
diff --git a/engines/freescape/games/eclipse/dos.cpp b/engines/freescape/games/eclipse/dos.cpp
index dbb887e9e3d..76ec01a6b5e 100644
--- a/engines/freescape/games/eclipse/dos.cpp
+++ b/engines/freescape/games/eclipse/dos.cpp
@@ -148,6 +148,7 @@ void EclipseEngine::loadAssetsDOSFullGame() {
loadMessagesFixedSize(&file, 0x594f, 16, 20);
loadSoundsFx(&file, 0xb9f0, 5);
+ loadSpeakerFxDOS(&file, 0x5BD6 + 0x200, 0x5AE1 + 0x200, 20);
loadFonts(&file, 0xb785);
load8bitBinary(&file, 0x2530, 4);
_border = load8bitBinImage(&file, 0x210);
Commit: 69e0ff555072b4962840fbb3fed2c680d3f5bd27
https://github.com/scummvm/scummvm/commit/69e0ff555072b4962840fbb3fed2c680d3f5bd27
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-27T08:08:04+01:00
Commit Message:
FREESCAPE: added missing indicators in driller amiga
Changed paths:
engines/freescape/games/driller/amiga.cpp
engines/freescape/games/driller/driller.h
diff --git a/engines/freescape/games/driller/amiga.cpp b/engines/freescape/games/driller/amiga.cpp
index 15d1a6d27de..ef1ae1aeef3 100644
--- a/engines/freescape/games/driller/amiga.cpp
+++ b/engines/freescape/games/driller/amiga.cpp
@@ -26,6 +26,146 @@
namespace Freescape {
+static void decodeAmigaSprite(Common::SeekableReadStream *file, Graphics::ManagedSurface *surf,
+ int dataOffset, int widthWords, int height, byte *palette,
+ const Graphics::PixelFormat &fmt) {
+ for (int y = 0; y < height; y++) {
+ for (int col = 0; col < widthWords; col++) {
+ int off = dataOffset + (y * widthWords + col) * 8;
+ file->seek(off);
+ uint16 p0 = file->readUint16BE();
+ uint16 p1 = file->readUint16BE();
+ uint16 p2 = file->readUint16BE();
+ uint16 p3 = file->readUint16BE();
+ for (int bit = 0; bit < 16; bit++) {
+ byte colorIdx = 0;
+ if (p0 & (0x8000 >> bit)) colorIdx |= 1;
+ if (p1 & (0x8000 >> bit)) colorIdx |= 2;
+ if (p2 & (0x8000 >> bit)) colorIdx |= 4;
+ if (p3 & (0x8000 >> bit)) colorIdx |= 8;
+ if (colorIdx == 0)
+ continue;
+ uint32 color = fmt.ARGBToColor(0xFF,
+ palette[colorIdx * 3], palette[colorIdx * 3 + 1], palette[colorIdx * 3 + 2]);
+ surf->setPixel(col * 16 + bit, y, color);
+ }
+ }
+ }
+}
+
+void DrillerEngine::loadRigSprites(Common::SeekableReadStream *file, int sprigsOffset) {
+ // SPRIGS: 2 word columns à 25 rows à 5 frames, stride=$1A0 (416 bytes)
+ const int frameStride = 0x1A0;
+ const int numFrames = 5;
+ uint32 transparent = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0, 0, 0);
+
+ // Get the console palette
+ byte *palette = nullptr;
+ if (_variant & GF_AMIGA_RETAIL)
+ palette = getPaletteFromNeoImage(file, 0x137f4);
+ else {
+ Common::File neoFile;
+ neoFile.open("console.neo");
+ if (neoFile.isOpen())
+ palette = getPaletteFromNeoImage(&neoFile, 0);
+ }
+ if (!palette)
+ return;
+
+ for (int f = 0; f < numFrames; f++) {
+ auto *surf = new Graphics::ManagedSurface();
+ surf->create(32, 25, _gfx->_texturePixelFormat);
+ surf->fillRect(Common::Rect(0, 0, 32, 25), transparent);
+ decodeAmigaSprite(file, surf, sprigsOffset + (f + 1) * frameStride, 2, 25, palette, _gfx->_texturePixelFormat);
+ _rigSprites.push_back(surf);
+ }
+
+ free(palette);
+}
+
+void DrillerEngine::loadIndicatorSprites(Common::SeekableReadStream *file, byte *palette,
+ int stepOffset, int angleOffset) {
+ uint32 transparent = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0, 0, 0);
+
+ // Step indicator: 1 word à 4 rows, 8 frames, stride=40
+ for (int f = 0; f < 8; f++) {
+ auto *surf = new Graphics::ManagedSurface();
+ surf->create(16, 4, _gfx->_texturePixelFormat);
+ surf->fillRect(Common::Rect(0, 0, 16, 4), transparent);
+ decodeAmigaSprite(file, surf, stepOffset + f * 40, 1, 4, palette, _gfx->_texturePixelFormat);
+ _stepSprites.push_back(surf);
+ }
+
+ // Angle indicator: 1 word à 4 rows, 8 frames, stride=40
+ for (int f = 0; f < 8; f++) {
+ auto *surf = new Graphics::ManagedSurface();
+ surf->create(16, 4, _gfx->_texturePixelFormat);
+ surf->fillRect(Common::Rect(0, 0, 16, 4), transparent);
+ decodeAmigaSprite(file, surf, angleOffset + f * 40, 1, 4, palette, _gfx->_texturePixelFormat);
+ _angleSprites.push_back(surf);
+ }
+}
+
+void DrillerEngine::loadCompassStrips(Common::SeekableReadStream *file, byte *palette,
+ int pitchStripOffset, int yawCogOffset) {
+ uint32 black = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0, 0, 0);
+
+ // Pitch strip (SPRATT): 32px wide, 144+29=173 rows of continuous data.
+ // stride=16 bytes per row, 2 word columns à 4 planes = 16 bytes/row.
+ // 144 start positions, 29 visible rows at a time â need 173 total rows.
+ {
+ int totalRows = 144 + 29;
+ _compassPitchStrip = new Graphics::ManagedSurface();
+ _compassPitchStrip->create(32, totalRows, _gfx->_texturePixelFormat);
+ _compassPitchStrip->fillRect(Common::Rect(0, 0, 32, totalRows), black);
+ decodeAmigaSprite(file, _compassPitchStrip, pitchStripOffset, 2, totalRows, palette, _gfx->_texturePixelFormat);
+ }
+
+ // Yaw compass (SPRCOG): pre-render all 72 rotation frames.
+ // The original uses 70 bytes of pre-computed needle data, accessed at different
+ // byte offsets and bit-shifted to produce 72 unique 30Ã5 pixel frames.
+ // Each frame: read long(4)+word(2) per row, shift left, mask with $3FFFFE00.
+ // The needle is written to bitplane 1 only (green in Amiga palette).
+ {
+ byte cogData[70];
+ file->seek(yawCogOffset);
+ file->read(cogData, 70);
+
+ uint32 needleColor = _gfx->_texturePixelFormat.ARGBToColor(0xFF,
+ palette[2 * 3], palette[2 * 3 + 1], palette[2 * 3 + 2]); // bitplane 1 = color 2
+
+ uint32 transparent = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0, 0, 0);
+ for (int rot = 0; rot < 72; rot++) {
+ auto *surf = new Graphics::ManagedSurface();
+ surf->create(30, 5, _gfx->_texturePixelFormat);
+ surf->fillRect(Common::Rect(0, 0, 30, 5), transparent);
+
+ int wordOff = (rot >> 3) & ~1;
+ int bitShift = rot & 15;
+ int a1 = wordOff;
+
+ for (int row = 0; row < 5; row++) {
+ uint32 longVal = ((uint32)cogData[a1] << 24) | ((uint32)cogData[a1+1] << 16) |
+ ((uint32)cogData[a1+2] << 8) | cogData[a1+3];
+ uint16 wordVal = ((uint16)cogData[a1+4] << 8) | cogData[a1+5];
+
+ longVal = (longVal << bitShift);
+ uint32 wordExt = ((uint32)wordVal << bitShift) >> 16;
+ uint32 result = (longVal | wordExt) & 0x3FFFFE00;
+
+ for (int b = 0; b < 30; b++) {
+ if (result & (0x40000000 >> b))
+ surf->setPixel(b, row, needleColor);
+ }
+
+ a1 += 14; // 6 bytes data + 8 bytes skip per row
+ }
+
+ _compassYawFrames.push_back(surf);
+ }
+ }
+}
+
void DrillerEngine::loadAssetsAmigaFullGame() {
Common::File file;
if (_variant & GF_AMIGA_RETAIL) {
@@ -48,6 +188,12 @@ void DrillerEngine::loadAssetsAmigaFullGame() {
load8bitBinary(&file, 0x29c16, 16);
loadPalettes(&file, 0x297d4);
loadSoundsFx(&file, 0x30e80, 25);
+
+ byte *palette = getPaletteFromNeoImage(&file, 0x137f4);
+ loadRigSprites(&file, 0x2407A);
+ loadIndicatorSprites(&file, palette, 0x26F9A, 0x27222);
+ loadCompassStrips(&file, palette, 0x23316, 0x26F4C);
+ free(palette);
} else if (_variant & GF_AMIGA_BUDGET) {
file.open("lift.neo");
if (!file.isOpen())
@@ -78,6 +224,18 @@ void DrillerEngine::loadAssetsAmigaFullGame() {
load8bitBinary(&file, 0x21a3e, 16);
loadPalettes(&file, 0x215fc);
+ byte *palette = nullptr;
+ Common::File neoFile;
+ neoFile.open("console.neo");
+ if (neoFile.isOpen())
+ palette = getPaletteFromNeoImage(&neoFile, 0);
+ loadRigSprites(&file, 0x1B8C8);
+ if (palette) {
+ loadIndicatorSprites(&file, palette, 0x1E288, 0x1E510);
+ loadCompassStrips(&file, palette, 0x1AB64, 0x1E23A);
+ }
+ free(palette);
+
file.close();
file.open("soundfx");
if (!file.isOpen())
@@ -290,6 +448,48 @@ void DrillerEngine::drawAmigaAtariSTUI(Graphics::Surface *surface) {
else
surface->copyRectToSurface(*_indicators[_playerHeightNumber], 106, 128, Common::Rect(_indicators[1]->w, _indicators[1]->h));
}
+
+ // Step indicator: shows current step size (0-7)
+ if (!_stepSprites.empty()) {
+ int frame = _playerStepIndex % _stepSprites.size();
+ surface->copyRectToSurfaceWithKey(*_stepSprites[frame], 48, 160,
+ Common::Rect(_stepSprites[frame]->w, _stepSprites[frame]->h), transparent);
+ }
+
+ // Angle/compass indicator: shows current rotation angle setting (0-7)
+ if (!_angleSprites.empty()) {
+ int frame = _angleRotationIndex % _angleSprites.size();
+ surface->copyRectToSurfaceWithKey(*_angleSprites[frame], 64, 160,
+ Common::Rect(_angleSprites[frame]->w, _angleSprites[frame]->h), transparent);
+ }
+
+ // Pitch compass (SPRATT): vertically scrolling strip at x=$4E=78, y=$89=137
+ // Mask $FFFC,$0078 means only 14 pixels visible: 2 from column 0 right + 12 from column 1
+ // Visible pixel range: x=14 to x=27 within the 32px strip (bits 0-1 of col0 + bits 0-2,7-15 of col1)
+ if (_compassPitchStrip) {
+ int pos = ((int)(_pitch * 0.4f) + 144) % 144;
+ Common::Rect srcRect(14, pos, 28, pos + 29);
+ surface->copyRectToSurface(*_compassPitchStrip, 78, 138, srcRect);
+ }
+
+ // Yaw compass: purple gradient background (SPRCBG) drawn first,
+ // then scrolling N/E/S/W needle (SPRCOG) drawn on top one line below.
+ // Background at x=$32â48, y=$8E=142. Needle at y=$8E+1=143.
+ if (!_compassYawFrames.empty()) {
+ float yaw = _yaw;
+ if (yaw < 0) yaw += 360;
+ if (yaw >= 360) yaw -= 360;
+ int rot = ((int)(yaw / 5.0f)) % 72;
+ surface->copyRectToSurfaceWithKey(*_compassYawFrames[rot], 49, 143,
+ Common::Rect(_compassYawFrames[rot]->w, _compassYawFrames[rot]->h), transparent);
+ }
+
+ // Drilling rig animation: cycles through 5 frames when rig is placed
+ if (!_rigSprites.empty() && _drillStatusByArea[_currentArea->getAreaID()] == 1) {
+ int frame = (_ticks / 7) % _rigSprites.size();
+ surface->copyRectToSurfaceWithKey(*_rigSprites[frame], 272, 143,
+ Common::Rect(_rigSprites[frame]->w, _rigSprites[frame]->h), transparent);
+ }
}
void DrillerEngine::drawString(const DrillerFontSize size, const Common::String &str, int x, int y, uint32 primaryColor, uint32 secondaryColor, uint32 backColor, Graphics::Surface *surface) {
@@ -329,6 +529,7 @@ void DrillerEngine::initAmigaAtari() {
_loadGameArea = Common::Rect(9, 156, 39, 164);
_borderExtra = nullptr;
+ _compassPitchStrip = nullptr;
_borderExtraTexture = nullptr;
_soundIndexShoot = 1;
diff --git a/engines/freescape/games/driller/driller.h b/engines/freescape/games/driller/driller.h
index 52ea8da286d..47d53e961d0 100644
--- a/engines/freescape/games/driller/driller.h
+++ b/engines/freescape/games/driller/driller.h
@@ -105,6 +105,20 @@ private:
void initAmigaAtari();
void initDOS();
void initZX();
+
+ // Amiga/Atari UI sprite indicators loaded from executable
+ Common::Array<Graphics::ManagedSurface *> _rigSprites; // 5 rig animation frames
+ Common::Array<Graphics::ManagedSurface *> _stepSprites; // 8 step indicator frames
+ Common::Array<Graphics::ManagedSurface *> _angleSprites; // 8 angle/compass frames
+ void loadRigSprites(Common::SeekableReadStream *file, int sprigsOffset);
+ void loadIndicatorSprites(Common::SeekableReadStream *file, byte *palette,
+ int stepOffset, int angleOffset);
+
+ // Compass indicators loaded from executable
+ Graphics::ManagedSurface *_compassPitchStrip; // pitch: 32px wide à (144+29) rows scrolling strip
+ Common::Array<Graphics::ManagedSurface *> _compassYawFrames; // yaw: 72 pre-rendered 30Ã5 frames
+ void loadCompassStrips(Common::SeekableReadStream *file, byte *palette,
+ int pitchStripOffset, int yawCogOffset);
void initCPC();
void initC64();
Commit: 9668a661bbda4fc9b0f9fed4d5492e37b25a78f0
https://github.com/scummvm/scummvm/commit/9668a661bbda4fc9b0f9fed4d5492e37b25a78f0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-27T08:08:04+01:00
Commit Message:
FREESCAPE: load vehicle indicator indicators from game data in driller amiga
Changed paths:
engines/freescape/games/driller/amiga.cpp
engines/freescape/games/driller/driller.h
diff --git a/engines/freescape/games/driller/amiga.cpp b/engines/freescape/games/driller/amiga.cpp
index ef1ae1aeef3..b2735436944 100644
--- a/engines/freescape/games/driller/amiga.cpp
+++ b/engines/freescape/games/driller/amiga.cpp
@@ -84,7 +84,7 @@ void DrillerEngine::loadRigSprites(Common::SeekableReadStream *file, int sprigsO
}
void DrillerEngine::loadIndicatorSprites(Common::SeekableReadStream *file, byte *palette,
- int stepOffset, int angleOffset) {
+ int stepOffset, int angleOffset, int vehicleOffset) {
uint32 transparent = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0, 0, 0);
// Step indicator: 1 word à 4 rows, 8 frames, stride=40
@@ -104,6 +104,19 @@ void DrillerEngine::loadIndicatorSprites(Common::SeekableReadStream *file, byte
decodeAmigaSprite(file, surf, angleOffset + f * 40, 1, 4, palette, _gfx->_texturePixelFormat);
_angleSprites.push_back(surf);
}
+
+ // Vehicle indicator: 4 words à 43 rows, 5 frames, stride=1408 ($580)
+ // Frame 0=fly, frames 1-4=tank heights 0-3
+ {
+ uint32 black = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0, 0, 0);
+ for (int f = 0; f < 5; f++) {
+ auto *surf = new Graphics::ManagedSurface();
+ surf->create(64, 43, _gfx->_texturePixelFormat);
+ surf->fillRect(Common::Rect(0, 0, 64, 43), black);
+ decodeAmigaSprite(file, surf, vehicleOffset + f * 0x580, 4, 43, palette, _gfx->_texturePixelFormat);
+ _vehicleSprites.push_back(surf);
+ }
+ }
}
void DrillerEngine::loadCompassStrips(Common::SeekableReadStream *file, byte *palette,
@@ -191,7 +204,7 @@ void DrillerEngine::loadAssetsAmigaFullGame() {
byte *palette = getPaletteFromNeoImage(&file, 0x137f4);
loadRigSprites(&file, 0x2407A);
- loadIndicatorSprites(&file, palette, 0x26F9A, 0x27222);
+ loadIndicatorSprites(&file, palette, 0x26F9A, 0x27222, 0x24D88);
loadCompassStrips(&file, palette, 0x23316, 0x26F4C);
free(palette);
} else if (_variant & GF_AMIGA_BUDGET) {
@@ -231,7 +244,7 @@ void DrillerEngine::loadAssetsAmigaFullGame() {
palette = getPaletteFromNeoImage(&neoFile, 0);
loadRigSprites(&file, 0x1B8C8);
if (palette) {
- loadIndicatorSprites(&file, palette, 0x1E288, 0x1E510);
+ loadIndicatorSprites(&file, palette, 0x1E288, 0x1E510, 0x1C5D6);
loadCompassStrips(&file, palette, 0x1AB64, 0x1E23A);
}
free(palette);
@@ -442,7 +455,15 @@ void DrillerEngine::drawAmigaAtariSTUI(Graphics::Surface *surface) {
surface->fillRect(energyBar, yellow);
}
- if (_indicators.size() > 0) {
+ if (!_vehicleSprites.empty()) {
+ int frame = _flyMode ? 0 : (_playerHeightNumber + 1);
+ frame = CLIP(frame, 0, (int)_vehicleSprites.size() - 1);
+ // Mask $FF00,$0000,$0000,$0007: 8 bits transparent left, 3 bits transparent right
+ // Visible pixels: x=8 to x=60 within the 64px sprite
+ surface->copyRectToSurface(*_vehicleSprites[frame], 104, 126,
+ Common::Rect(8, 0, 61, _vehicleSprites[frame]->h));
+ } else if (_indicators.size() > 0) {
+ // Fallback to bundled images
if (_flyMode)
surface->copyRectToSurface(*_indicators[4], 106, 128, Common::Rect(_indicators[1]->w, _indicators[1]->h));
else
diff --git a/engines/freescape/games/driller/driller.h b/engines/freescape/games/driller/driller.h
index 47d53e961d0..b1895ccb2f6 100644
--- a/engines/freescape/games/driller/driller.h
+++ b/engines/freescape/games/driller/driller.h
@@ -110,9 +110,10 @@ private:
Common::Array<Graphics::ManagedSurface *> _rigSprites; // 5 rig animation frames
Common::Array<Graphics::ManagedSurface *> _stepSprites; // 8 step indicator frames
Common::Array<Graphics::ManagedSurface *> _angleSprites; // 8 angle/compass frames
+ Common::Array<Graphics::ManagedSurface *> _vehicleSprites; // 5 vehicle mode frames (fly + 4 tank heights)
void loadRigSprites(Common::SeekableReadStream *file, int sprigsOffset);
void loadIndicatorSprites(Common::SeekableReadStream *file, byte *palette,
- int stepOffset, int angleOffset);
+ int stepOffset, int angleOffset, int vehicleOffset);
// Compass indicators loaded from executable
Graphics::ManagedSurface *_compassPitchStrip; // pitch: 32px wide à (144+29) rows scrolling strip
Commit: 42aaca95f4e3ef66184d69d3ce743ee0523ded11
https://github.com/scummvm/scummvm/commit/42aaca95f4e3ef66184d69d3ce743ee0523ded11
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-27T08:08:04+01:00
Commit Message:
FREESCAPE: quit animation button in driller amiga
Changed paths:
engines/freescape/games/driller/amiga.cpp
engines/freescape/games/driller/driller.cpp
engines/freescape/games/driller/driller.h
diff --git a/engines/freescape/games/driller/amiga.cpp b/engines/freescape/games/driller/amiga.cpp
index b2735436944..4b2615f87d8 100644
--- a/engines/freescape/games/driller/amiga.cpp
+++ b/engines/freescape/games/driller/amiga.cpp
@@ -84,7 +84,7 @@ void DrillerEngine::loadRigSprites(Common::SeekableReadStream *file, int sprigsO
}
void DrillerEngine::loadIndicatorSprites(Common::SeekableReadStream *file, byte *palette,
- int stepOffset, int angleOffset, int vehicleOffset) {
+ int stepOffset, int angleOffset, int vehicleOffset, int quitOffset) {
uint32 transparent = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0, 0, 0);
// Step indicator: 1 word à 4 rows, 8 frames, stride=40
@@ -117,6 +117,19 @@ void DrillerEngine::loadIndicatorSprites(Common::SeekableReadStream *file, byte
_vehicleSprites.push_back(surf);
}
}
+
+ // Quit/abort indicator: 2 words à 8 rows, 11 frames, stride=$90=144
+ // Frames 0-6: shutter animation, 7-10: confirmation squares filling in
+ {
+ uint32 black = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0, 0, 0);
+ for (int f = 0; f < 11; f++) {
+ auto *surf = new Graphics::ManagedSurface();
+ surf->create(32, 8, _gfx->_texturePixelFormat);
+ surf->fillRect(Common::Rect(0, 0, 32, 8), black);
+ decodeAmigaSprite(file, surf, quitOffset + f * 0x90, 2, 8, palette, _gfx->_texturePixelFormat);
+ _quitSprites.push_back(surf);
+ }
+ }
}
void DrillerEngine::loadCompassStrips(Common::SeekableReadStream *file, byte *palette,
@@ -204,7 +217,7 @@ void DrillerEngine::loadAssetsAmigaFullGame() {
byte *palette = getPaletteFromNeoImage(&file, 0x137f4);
loadRigSprites(&file, 0x2407A);
- loadIndicatorSprites(&file, palette, 0x26F9A, 0x27222, 0x24D88);
+ loadIndicatorSprites(&file, palette, 0x26F9A, 0x27222, 0x24D88, 0x26912);
loadCompassStrips(&file, palette, 0x23316, 0x26F4C);
free(palette);
} else if (_variant & GF_AMIGA_BUDGET) {
@@ -244,7 +257,7 @@ void DrillerEngine::loadAssetsAmigaFullGame() {
palette = getPaletteFromNeoImage(&neoFile, 0);
loadRigSprites(&file, 0x1B8C8);
if (palette) {
- loadIndicatorSprites(&file, palette, 0x1E288, 0x1E510, 0x1C5D6);
+ loadIndicatorSprites(&file, palette, 0x1E288, 0x1E510, 0x1C5D6, 0x1DC00);
loadCompassStrips(&file, palette, 0x1AB64, 0x1E23A);
}
free(palette);
@@ -505,6 +518,30 @@ void DrillerEngine::drawAmigaAtariSTUI(Graphics::Surface *surface) {
Common::Rect(_compassYawFrames[rot]->w, _compassYawFrames[rot]->h), transparent);
}
+ // Quit indicator (ABORTSQ): shows on the console when quit is initiated.
+ // First click: shutter rolls down (frames 0-6), then shows 3 empty squares (frame 7).
+ // Clicks 2-4: squares fill in (frames 8-10). Fourth click = quit confirmed.
+ // Mask $0000,$0FFF: 20 visible pixels.
+ // Quit sequence from assembly (ABORTSQ):
+ // Click 1: shutter rolls down (frames 0-6), settles on frame 7 (3 empty lights)
+ // Click 2: frame 8 (first light on)
+ // Click 3: frame 9 (second light on)
+ // Click 4: frame 10 (third light on, bar turns green) â next click quits
+ if (!_quitSprites.empty() && _quitConfirmCounter > 0) {
+ int frame;
+ if (_quitConfirmCounter == 1) {
+ // Shutter intro: animate frames 0-6, then hold frame 7
+ int shutterFrame = (_ticks - _quitStartTicks) / 2;
+ frame = (shutterFrame >= 7) ? 7 : shutterFrame;
+ } else {
+ // Counter 2âframe 8, 3âframe 9, 4âframe 10
+ frame = 6 + _quitConfirmCounter;
+ }
+ frame = CLIP(frame, 0, (int)_quitSprites.size() - 1);
+ surface->copyRectToSurface(*_quitSprites[frame], 176, 5,
+ Common::Rect(0, 0, 20, _quitSprites[frame]->h));
+ }
+
// Drilling rig animation: cycles through 5 frames when rig is placed
if (!_rigSprites.empty() && _drillStatusByArea[_currentArea->getAreaID()] == 1) {
int frame = (_ticks / 7) % _rigSprites.size();
@@ -551,6 +588,9 @@ void DrillerEngine::initAmigaAtari() {
_borderExtra = nullptr;
_compassPitchStrip = nullptr;
+ _quitConfirmCounter = 0;
+ _quitStartTicks = 0;
+ _quitArea = Common::Rect(188, 5, 208, 13);
_borderExtraTexture = nullptr;
_soundIndexShoot = 1;
diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index 0075625553c..e1894280cfa 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -509,6 +509,11 @@ Math::Vector3d getProjectionToPlane(const Math::Vector3d &vect, const Math::Vect
}
void DrillerEngine::pressedKey(const int keycode) {
+ // Any key press during quit confirmation cancels it
+ if ((isAmiga() || isAtariST()) && _quitConfirmCounter > 0) {
+ _quitConfirmCounter = 0;
+ }
+
if (keycode == kActionIncreaseStepSize) {
increaseStepSize();
} else if (keycode == kActionDecreaseStepSize) {
@@ -869,6 +874,8 @@ void DrillerEngine::removeDrill(Area *area) {
void DrillerEngine::initGameState() {
FreescapeEngine::initGameState();
+ _quitConfirmCounter = 0;
+ _quitStartTicks = 0;
for (auto &it : _areaMap) {
if (_drillStatusByArea[it._key] != kDrillerNoRig)
@@ -960,6 +967,14 @@ bool DrillerEngine::onScreenControls(Common::Point mouse) {
loadGameDialog();
_gfx->setViewport(_viewArea);
return true;
+ } else if ((isAmiga() || isAtariST()) && !_quitSprites.empty() && _quitArea.contains(mouse)) {
+ if (_quitConfirmCounter == 0)
+ _quitStartTicks = _ticks;
+ _quitConfirmCounter++;
+ if (_quitConfirmCounter > 4) {
+ _gameStateControl = kFreescapeGameStateEnd;
+ }
+ return true;
}
return false;
}
diff --git a/engines/freescape/games/driller/driller.h b/engines/freescape/games/driller/driller.h
index b1895ccb2f6..fcbd20e71cc 100644
--- a/engines/freescape/games/driller/driller.h
+++ b/engines/freescape/games/driller/driller.h
@@ -111,9 +111,13 @@ private:
Common::Array<Graphics::ManagedSurface *> _stepSprites; // 8 step indicator frames
Common::Array<Graphics::ManagedSurface *> _angleSprites; // 8 angle/compass frames
Common::Array<Graphics::ManagedSurface *> _vehicleSprites; // 5 vehicle mode frames (fly + 4 tank heights)
+ Common::Array<Graphics::ManagedSurface *> _quitSprites; // 11 quit animation frames
+ int _quitConfirmCounter; // 0=not quitting, 1-4=waiting for confirmations
+ int _quitStartTicks; // _ticks when quit was initiated (for shutter animation)
+ Common::Rect _quitArea; // click area for quit button on Amiga/Atari console
void loadRigSprites(Common::SeekableReadStream *file, int sprigsOffset);
void loadIndicatorSprites(Common::SeekableReadStream *file, byte *palette,
- int stepOffset, int angleOffset, int vehicleOffset);
+ int stepOffset, int angleOffset, int vehicleOffset, int quitOffset);
// Compass indicators loaded from executable
Graphics::ManagedSurface *_compassPitchStrip; // pitch: 32px wide à (144+29) rows scrolling strip
More information about the Scummvm-git-logs
mailing list