[Scummvm-git-logs] scummvm master -> 4d591f74db47b3a7a2f7e709bec7fc397b85b456
neuromancer
noreply at scummvm.org
Wed May 27 06:56:16 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:
5746cd0360 FREESCAPE: precise implementation for SPFX (amiga/atari)
4d591f74db FREESCAPE: precise implementation for drill placement restrictions in driller
Commit: 5746cd036035364fd17f6d6a69a9cfea16387fbb
https://github.com/scummvm/scummvm/commit/5746cd036035364fd17f6d6a69a9cfea16387fbb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-05-27T08:56:01+02:00
Commit Message:
FREESCAPE: precise implementation for SPFX (amiga/atari)
Changed paths:
engines/freescape/gfx.cpp
engines/freescape/language/instruction.cpp
diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index 532d325d4ae..16423e02ca6 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -1305,10 +1305,15 @@ void Renderer::drawBackground(uint8 color) {
uint8 r2, g2, b2;
if (_colorRemaps && _colorRemaps->contains(color)) {
- color = (*_colorRemaps)[color];
- if (_renderMode == Common::kRenderCPC && isEncodedCPCDirectColor(color))
- color = decodeCPCDirectColor(color);
- readFromPalette(color, r1, g1, b1);
+ int mappedColor = (*_colorRemaps)[color];
+ if (_renderMode == Common::kRenderAmiga || _renderMode == Common::kRenderAtariST)
+ _texturePixelFormat.colorToRGB(mappedColor, r1, g1, b1);
+ else {
+ color = mappedColor;
+ if (_renderMode == Common::kRenderCPC && isEncodedCPCDirectColor(color))
+ color = decodeCPCDirectColor(color);
+ readFromPalette(color, r1, g1, b1);
+ }
clear(r1, g1, b1);
return;
}
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index 5d93deced55..ccff0398fba 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -410,51 +410,120 @@ void FreescapeEngine::executePrint(FCLInstruction &instruction) {
_currentAreaMessages.push_back(_messagesList[index]);
}
+uint32 spfxBasePaletteColor(FreescapeEngine *engine, uint8 index) {
+ index &= 0x0f;
+ uint8 r = engine->_gfx->_palette[3 * index + 0];
+ uint8 g = engine->_gfx->_palette[3 * index + 1];
+ uint8 b = engine->_gfx->_palette[3 * index + 2];
+ return engine->_gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
+}
+
+uint32 spfxDirectPaletteColor(FreescapeEngine *engine, uint16 value) {
+ uint8 r = (value >> 8) & 0x0f;
+ uint8 g = (value >> 4) & 0x0f;
+ uint8 b = value & 0x0f;
+
+ if (engine->isAtariST()) {
+ r = ((r & 0x07) << 1) | ((r & 0x07) >> 2);
+ g = ((g & 0x07) << 1) | ((g & 0x07) >> 2);
+ b = ((b & 0x07) << 1) | ((b & 0x07) >> 2);
+ }
+
+ r = (r << 4) | r;
+ g = (g << 4) | g;
+ b = (b << 4) | b;
+ return engine->_gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
+}
+
+uint32 spfxActivePaletteColor(FreescapeEngine *engine, uint8 index) {
+ index &= 0x0f;
+ if (engine->_currentArea->_colorRemaps.contains(index))
+ return (uint32)engine->_currentArea->_colorRemaps[index];
+ return spfxBasePaletteColor(engine, index);
+}
+
+void spfxSetActivePaletteColor(FreescapeEngine *engine, uint8 index, uint32 color) {
+ index &= 0x0f;
+ if (color == spfxBasePaletteColor(engine, index))
+ engine->_currentArea->unremapColor(index);
+ else
+ engine->_currentArea->remapColor(index, color);
+}
+
+void spfxFillRange(FreescapeEngine *engine, uint8 start, uint8 end, uint32 color) {
+ if (end < start)
+ return;
+
+ for (int i = start; i <= end; i++)
+ spfxSetActivePaletteColor(engine, i, color);
+}
+
+void spfxRestoreRange(FreescapeEngine *engine, uint8 start, uint8 end) {
+ if (end < start)
+ return;
+
+ for (int i = start; i <= end; i++)
+ engine->_currentArea->unremapColor(i);
+}
+
+void spfxRotateLeft(FreescapeEngine *engine, uint8 start, uint8 end) {
+ if (end <= start)
+ return;
+
+ uint32 color = spfxActivePaletteColor(engine, start);
+ for (int i = start; i < end; i++)
+ spfxSetActivePaletteColor(engine, i, spfxActivePaletteColor(engine, i + 1));
+ spfxSetActivePaletteColor(engine, end, color);
+}
+
+void spfxRotateRight(FreescapeEngine *engine, uint8 start, uint8 end) {
+ if (end <= start)
+ return;
+
+ uint32 color = spfxActivePaletteColor(engine, end);
+ for (int i = end; i > start; i--)
+ spfxSetActivePaletteColor(engine, i, spfxActivePaletteColor(engine, i - 1));
+ spfxSetActivePaletteColor(engine, start, color);
+}
+
void FreescapeEngine::executeSPFX(FCLInstruction &instruction) {
uint16 src = instruction._source;
uint16 dst = instruction._destination;
if (isAmiga() || isAtariST()) {
- uint8 r = 0;
- uint8 g = 0;
- uint8 b = 0;
- uint32 color = 0;
-
- if (src == 2 && dst == 0) {
- // The Amiga interpreter handles SPFX $0200 by restoring the current area palette.
- _currentArea->_colorRemaps.clear();
- } else if (src & (1 << 7)) {
- uint16 v = 0;
- color = 0;
- // Extract the color to replace from the src/dst values
- v = (src & 0x77) << 8;
- v = v | (dst & 0x70);
- v = v >> 4;
-
- // Convert the color to RGB
- r = (v & 0xf00) >> 8;
- r = r << 4 | r;
- r = r & 0xff;
-
- g = (v & 0xf0) >> 4;
- g = g << 4 | g;
- g = g & 0xff;
-
- b = v & 0xf;
- b = b << 4 | b;
- b = b & 0xff;
-
- color = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
- _currentArea->remapColor(dst & 0x0f, color); // src & 0x77, dst & 0x0f
- } else if ((src & 0xf0) >> 4 == 1) {
- _gfx->readFromPalette(src & 0x0f, r, g, b);
- color = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
- for (int i = 1; i < 16; i++)
- _currentArea->remapColor(i, color);
- } else if ((src & 0x0f) == 1) {
- _gfx->readFromPalette(dst & 0x0f, r, g, b);
- color = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
- for (int i = 1; i < 16; i++)
- _currentArea->remapColor(i, color);
+ uint16 raw = ((src & 0xff) << 8) | (dst & 0xff);
+ if (raw & 0x8000) {
+ uint16 color = raw & 0x7770;
+ if (isAmiga())
+ color >>= 3;
+ else
+ color >>= 4;
+
+ spfxSetActivePaletteColor(this, raw & 0x0f, spfxDirectPaletteColor(this, color));
+ } else if ((raw & 0xf000) == 0x1000) {
+ spfxFillRange(this, (raw >> 4) & 0x0f, raw & 0x0f, spfxBasePaletteColor(this, (raw >> 8) & 0x0f));
+ } else {
+ switch (raw & 0x0f00) {
+ case 0x0000:
+ spfxSetActivePaletteColor(this, raw & 0x0f, spfxBasePaletteColor(this, (raw >> 4) & 0x0f));
+ break;
+ case 0x0100:
+ spfxFillRange(this, 0, 14, spfxBasePaletteColor(this, raw & 0x0f));
+ break;
+ case 0x0200:
+ _currentArea->_colorRemaps.clear();
+ break;
+ case 0x0300:
+ spfxRestoreRange(this, (raw >> 4) & 0x0f, raw & 0x0f);
+ break;
+ case 0x0400:
+ spfxRotateLeft(this, (raw >> 4) & 0x0f, raw & 0x0f);
+ break;
+ case 0x0500:
+ spfxRotateRight(this, (raw >> 4) & 0x0f, raw & 0x0f);
+ break;
+ default:
+ break;
+ }
}
} else {
debugC(1, kFreescapeDebugCode, "Switching palette from position %d to %d", src, dst);
Commit: 4d591f74db47b3a7a2f7e709bec7fc397b85b456
https://github.com/scummvm/scummvm/commit/4d591f74db47b3a7a2f7e709bec7fc397b85b456
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-05-27T08:56:01+02:00
Commit Message:
FREESCAPE: precise implementation for drill placement restrictions in driller
Changed paths:
engines/freescape/games/driller/driller.cpp
engines/freescape/games/driller/driller.h
diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index b67c28b4b59..bb8b0e16a72 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -81,11 +81,6 @@ DrillerEngine::DrillerEngine(OSystem *syst, const ADGameDescription *gd) : Frees
_maxEnergy = 63;
_maxShield = 63;
- Math::Vector3d drillBaseOrigin = Math::Vector3d(0, 0, 0);
- Math::Vector3d drillBaseSize = Math::Vector3d(3, 2, 3);
- _drillBase = new GeometricObject(kCubeType, 0, 0, drillBaseOrigin, drillBaseSize, nullptr, nullptr, nullptr, FCLInstructionVector(), "");
- assert(!_drillBase->isDestroyed() && !_drillBase->isInvisible());
-
if (isDemo()) {
_demoMode = !_disableDemoMode; // Most of the driller demos are non-interactive
_angleRotationIndex = 0;
@@ -106,7 +101,6 @@ DrillerEngine::DrillerEngine(OSystem *syst, const ADGameDescription *gd) : Frees
DrillerEngine::~DrillerEngine() {
delete _playerMusic;
delete _playerC64Sfx;
- delete _drillBase;
if (_borderExtra) {
delete _borderExtra;
@@ -585,15 +579,21 @@ void DrillerEngine::pressedKey(const int keycode) {
}
_gameStateVars[k8bitVariableEnergy] = _gameStateVars[k8bitVariableEnergy] - 5;
- const Math::Vector3d gasPocket3D(gasPocket.x, drillCenter.y(), gasPocket.y);
- const float distanceToPocket = (gasPocket3D - drillCenter).length();
+ const int areaScale = MAX<int>(_currentArea->getScale(), 1);
+ const int drillRawX = int(drillCenter.x() * areaScale / 32.0f);
+ const int drillRawZ = int(drillCenter.z() * areaScale / 32.0f);
+ const int gasRawX = gasPocket.x / 32;
+ const int gasRawZ = gasPocket.y / 32;
+ const int gasRadiusRaw = MAX<int>(gasPocketRadius / 32, 1);
+ // BTF6 scores the rig from raw-cell Manhattan distance, not Euclidean distance.
+ const uint distanceToPocket = ABS(gasRawX - drillRawX) + ABS(gasRawZ - drillRawZ);
debugC(1, kFreescapeDebugMove, "DRILL gas pocket raw=(%d,%d,r=%u) world=(%d,%d) radius=%u",
gasPocket.x / 32, gasPocket.y / 32, gasPocketRadius / 32, gasPocket.x, gasPocket.y, gasPocketRadius);
- debugC(1, kFreescapeDebugMove, "DRILL gas distance renderCenter=(%.2f,%.2f) gasWorld=(%d,%d) euclidean=%.2f worldRadius=%u",
- drillCenter.x(), drillCenter.z(), gasPocket.x, gasPocket.y, distanceToPocket, gasPocketRadius);
+ debugC(1, kFreescapeDebugMove, "DRILL gas distance renderCenter=(%.2f,%.2f) rawCenter=(%d,%d) gasRaw=(%d,%d) manhattan=%u rawRadius=%d",
+ drillCenter.x(), drillCenter.z(), drillRawX, drillRawZ, gasRawX, gasRawZ, distanceToPocket, gasRadiusRaw);
- float success = _useAutomaticDrilling ? 100.0f : 100.0f * (1.0f - distanceToPocket / gasPocketRadius);
- debugC(1, kFreescapeDebugMove, "DRILL gas computed success=%.2f automatic=%d", success, _useAutomaticDrilling ? 1 : 0);
+ int success = _useAutomaticDrilling ? 100 : 100 - int((100 * distanceToPocket) / gasRadiusRaw);
+ debugC(1, kFreescapeDebugMove, "DRILL gas computed success=%d automatic=%d", success, _useAutomaticDrilling ? 1 : 0);
// Play the "processing" sound up front (matches BTF660 in the
// original Amiga code, where sound 5 starts before the
// RIG POSITIONED / NO GAS FOUND messages are displayed and
@@ -615,15 +615,15 @@ void DrillerEngine::pressedKey(const int keycode) {
maxScoreMessage.replace(2, 6, Common::String::format("%d", maxScore));
insertTemporaryMessage(maxScoreMessage, _countdown - 4);
Common::String successMessage = _messagesList[6];
- successMessage.replace(0, 4, Common::String::format("%d", int(success)));
+ successMessage.replace(0, 4, Common::String::format("%d", success));
while (successMessage.size() < 14)
successMessage += " ";
insertTemporaryMessage(successMessage, _countdown - 6);
_drillSuccessByArea[_currentArea->getAreaID()] = uint32(success);
_gameStateVars[k8bitVariableScore] += uint32(maxScore * uint32(success)) / 100;
- debugC(1, kFreescapeDebugMove, "DRILL result: gas found success=%.2f maxScore=%d score=%d", success, maxScore, _gameStateVars[k8bitVariableScore]);
+ debugC(1, kFreescapeDebugMove, "DRILL result: gas found success=%d maxScore=%d score=%d", success, maxScore, _gameStateVars[k8bitVariableScore]);
- if (success >= 50.0f) {
+ if (success >= 50) {
_drillStatusByArea[_currentArea->getAreaID()] = kDrillerRigInPlace;
_gameStateVars[32]++;
} else
@@ -710,88 +710,87 @@ bool DrillerEngine::drillDeployed(Area *area) {
}
bool DrillerEngine::checkDrill(const Math::Vector3d position) {
- GeometricObject *obj = nullptr;
- Math::Vector3d origin = position;
-
- int16 id;
- int heightLastObject;
-
- origin.setValue(0, origin.x() + 128);
- origin.setValue(1, origin.y() - 5);
- origin.setValue(2, origin.z() + 128);
-
- _drillBase->setOrigin(origin);
- ObjectArray collisions = _currentArea->checkCollisions(_drillBase->_boundingBox);
- if (collisions.empty())
- return false;
-
- origin.setValue(0, origin.x() - 128);
- origin.setValue(2, origin.z() - 128);
-
- id = 255;
- obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
- assert(obj);
- obj = (GeometricObject *)obj->duplicate();
- origin.setValue(1, origin.y() + 6);
- obj->setOrigin(origin);
-
- // This bounding box is too large and can result in the drill to float next to a wall
- collisions = _currentArea->checkCollisions(obj->_boundingBox);
- if (!collisions.empty())
- return false;
-
- origin.setValue(1, origin.y() + 15);
- obj->setOrigin(origin);
-
- collisions = _currentArea->checkCollisions(obj->_boundingBox);
- if (!collisions.empty())
+ const int areaScale = MAX<int>(_currentArea->getScale(), 1);
+ const float rawScale = areaScale / 32.0f;
+ const int drillCenterX = int(position.x() * rawScale);
+ const int drillCenterZ = int((position.z() + 128.0f) * rawScale);
+ const int drillY = int(position.y() * rawScale);
+
+ // BTF604/BTF607 validate a 12-cell footprint against raw room bounds and cube objects.
+ if (drillCenterX <= 6 || drillCenterX >= 121 || drillCenterZ <= 6 || drillCenterZ >= 121) {
+ debugC(1, kFreescapeDebugMove, "DRILL rejected: original footprint bounds failed rawCenter=(%d,%d)", drillCenterX, drillCenterZ);
return false;
+ }
- origin.setValue(1, origin.y() - 10);
- heightLastObject = obj->getSize().y();
- delete obj;
-
- id = 254;
- debugC(1, kFreescapeDebugParser, "Adding object %d to room structure", id);
- obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
- assert(obj);
- // Set position for object
- origin.setValue(0, origin.x() - obj->getSize().x() / 5);
- origin.setValue(1, origin.y() + heightLastObject);
- origin.setValue(2, origin.z() - obj->getSize().z() / 5);
-
- obj = (GeometricObject *)obj->duplicate();
- obj->setOrigin(origin);
- collisions = _currentArea->checkCollisions(obj->_boundingBox);
- if (!collisions.empty())
- return false;
+ if (drillY != 0) {
+ const int floorX = drillCenterX - 1;
+ const int floorZ = drillCenterZ - 1;
+ bool supported = false;
+ ObjectMap *objects = _currentArea->getObjectsByID();
+ for (auto &it : *objects) {
+ Object *obj = it._value;
+ if (obj->isDestroyed() || obj->isInvisible() || obj->getType() != kCubeType)
+ continue;
+
+ GeometricObject *gobj = (GeometricObject *)obj;
+ const int objX = int(gobj->getOrigin().x() * rawScale);
+ const int objY = int(gobj->getOrigin().y() * rawScale);
+ const int objZ = int(gobj->getOrigin().z() * rawScale);
+ const int objSizeX = int(gobj->getSize().x() * rawScale);
+ const int objSizeY = int(gobj->getSize().y() * rawScale);
+ const int objSizeZ = int(gobj->getSize().z() * rawScale);
+
+ if (objY + objSizeY != drillY)
+ continue;
+ if (floorX < objX || floorZ < objZ)
+ continue;
+ if (floorX > objX + objSizeX - 2)
+ continue;
+ if (floorZ <= objZ + objSizeZ - 2) {
+ supported = true;
+ break;
+ }
+ }
- // Undo offset
- origin.setValue(0, origin.x() + obj->getSize().x() / 5);
- heightLastObject = obj->getSize().y();
- origin.setValue(2, origin.z() + obj->getSize().z() / 5);
- delete obj;
+ if (!supported) {
+ debugC(1, kFreescapeDebugMove, "DRILL rejected: original floor support failed rawFloor=(%d,%d,%d)", floorX, drillY, floorZ);
+ return false;
+ }
+ }
- id = 253;
- debugC(1, kFreescapeDebugParser, "Adding object %d to room structure", id);
- obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
- assert(obj);
- obj = (GeometricObject *)obj->duplicate();
+ const int footprintOffset = drillY == 0 ? 5 : 6;
+ const int footprintX = drillCenterX - footprintOffset;
+ const int footprintZ = drillCenterZ - footprintOffset;
+ ObjectMap *objects = _currentArea->getObjectsByID();
+ for (auto &it : *objects) {
+ Object *obj = it._value;
+ if (obj->isDestroyed() || obj->isInvisible() || obj->getType() != kCubeType)
+ continue;
- origin.setValue(0, origin.x() + obj->getSize().x() / 5);
- origin.setValue(1, origin.y() + heightLastObject);
- origin.setValue(2, origin.z() + obj->getSize().z() / 5);
+ GeometricObject *gobj = (GeometricObject *)obj;
+ const int objX = int(gobj->getOrigin().x() * rawScale);
+ const int objY = int(gobj->getOrigin().y() * rawScale);
+ const int objZ = int(gobj->getOrigin().z() * rawScale);
+ const int objSizeX = int(gobj->getSize().x() * rawScale);
+ const int objSizeY = int(gobj->getSize().y() * rawScale);
+ const int objSizeZ = int(gobj->getSize().z() * rawScale);
- obj->setOrigin(origin);
- collisions = _currentArea->checkCollisions(obj->_boundingBox);
- if (!collisions.empty())
- return false;
+ if (objY + objSizeY <= drillY)
+ continue;
+ if (footprintX < objX - 11)
+ continue;
+ if (drillY < objY - 22)
+ continue;
+ if (footprintZ < objZ - 11)
+ continue;
+ if (footprintX >= objX + objSizeX)
+ continue;
+ if (footprintZ < objZ + objSizeZ) {
+ debugC(1, kFreescapeDebugMove, "DRILL rejected: original object footprint hit obj=%d rawFootprint=(%d,%d,%d)", obj->getObjectID(), footprintX, drillY, footprintZ);
+ return false;
+ }
+ }
- // Undo offset
- // origin.setValue(0, origin.x() - obj->getSize().x() / 5);
- heightLastObject = obj->getSize().y();
- // origin.setValue(2, origin.z() - obj->getSize().z() / 5);
- delete obj;
return true;
}
diff --git a/engines/freescape/games/driller/driller.h b/engines/freescape/games/driller/driller.h
index 3041a23c5fe..408826ce7a4 100644
--- a/engines/freescape/games/driller/driller.h
+++ b/engines/freescape/games/driller/driller.h
@@ -82,7 +82,6 @@ private:
int _finalAreaWinConditionIndex;
int _amigaAtariEndGameStep;
bool drillDeployed(Area *area);
- GeometricObject *_drillBase;
Math::Vector3d drillPosition();
float compassYaw() const;
void addDrill(const Math::Vector3d position, bool gasFound);
More information about the Scummvm-git-logs
mailing list