[Scummvm-git-logs] scummvm master -> cd0b8a3f65c2a56c323b0e127eb090198d52b783
AndywinXp
noreply at scummvm.org
Thu Aug 8 14:10:25 UTC 2024
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
cd0b8a3f65 SCUMM: v1-4: Improve flashlight accuracy
Commit: cd0b8a3f65c2a56c323b0e127eb090198d52b783
https://github.com/scummvm/scummvm/commit/cd0b8a3f65c2a56c323b0e127eb090198d52b783
Author: AndywinXp (andywinxp at gmail.com)
Date: 2024-08-08T16:10:18+02:00
Commit Message:
SCUMM: v1-4: Improve flashlight accuracy
Verified from various v1-4 disasms.
This fixes and closes #15274:
"SCUMM: MANIAC: Flashlight shape not accurate"
Changed paths:
engines/scumm/gfx.cpp
engines/scumm/scumm_v5.h
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 2ded2ab1f49..d176874b3f7 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -1738,48 +1738,132 @@ void ScummEngine::moveScreen(int dx, int dy, int height) {
}
void ScummEngine_v5::clearFlashlight() {
- _flashlight.isDrawn = false;
+ _flashlight.eraseFlag = false;
_flashlight.buffer = nullptr;
}
+static const byte townsCurveData[] = { 0x01, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0x7F, 0xFF };
+
+static const byte v1FwdCurveData[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xFF,
+ 0x00, 0x00, 0xFF, 0xFF,
+ 0x00, 0x00, 0xFF, 0xFF,
+ 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const byte v1BkwdCurveData[] = {
+ 0xFF, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0x00,
+ 0xFF, 0xFF, 0xFF, 0x00,
+ 0xFF, 0xFF, 0xFF, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+
+static const byte v2FwdCurveData[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0F, 0xFF,
+ 0x00, 0x00, 0xFF, 0xFF,
+ 0x00, 0x0F, 0xFF, 0xFF,
+ 0x00, 0xFF, 0xFF, 0xFF,
+ 0x0F, 0xFF, 0xFF, 0xFF,
+ 0x0F, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const byte v2BkwdCurveData[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00,
+ 0xFF, 0xFF, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0x00,
+ 0xFF, 0xFF, 0xFF, 0x00,
+ 0xFF, 0xFF, 0xFF, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const byte v4FwdCurveData[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const byte v4BkwdCurveData[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
void ScummEngine_v5::drawFlashlight() {
- int i, j, x, y;
+ int x, y;
VirtScreen *vs = &_virtscr[kMainVirtScreen];
- byte backgroundColor = 0;
+ byte blackColor = 0x00;
// NES uses 0x1d for black
if (g_scumm->_game.platform == Common::kPlatformNES)
- backgroundColor = 0x1d;
+ blackColor = 0x1D;
// Remove the flash light first if it was previously drawn
- if (_flashlight.isDrawn) {
+ if (_flashlight.eraseFlag) {
markRectAsDirty(kMainVirtScreen, _flashlight.x, _flashlight.x + _flashlight.w,
_flashlight.y, _flashlight.y + _flashlight.h, USAGE_BIT_DIRTY);
if (_flashlight.buffer) {
- fill(_flashlight.buffer, vs->pitch, backgroundColor, _flashlight.w, _flashlight.h, vs->format.bytesPerPixel);
+ fill(_flashlight.buffer, vs->pitch, blackColor, _flashlight.w, _flashlight.h, vs->format.bytesPerPixel);
}
- _flashlight.isDrawn = false;
+
+ _flashlight.eraseFlag = false;
}
if (_flashlight.xStrips == 0 || _flashlight.yStrips == 0)
return;
- // Calculate the area of the flashlight
+ // Calculate the position of the flashlight.
if (_game.id == GID_ZAK || _game.id == GID_MANIAC) {
- x = _mouse.x + vs->xstart;
- y = _mouse.y - vs->topline;
+ x = (_mouse.x + vs->xstart);
+ y = (_mouse.y - vs->topline);
} else {
Actor *a = derefActor(VAR(VAR_EGO), "drawFlashlight");
x = a->getPos().x;
y = a->getPos().y;
}
+
+ // The original only shows the flashlight in locations whose:
+ // - X position is a multiple of 8;
+ // - Y position is a multiple of 2.
+ //
+ // Failing to do so will create temporary glitches on the corners
+ // of the flashlight when attempting moving the mouse too fast...
+ // I'm not sure FM-Towns does this, so I'm leaving it off,
+ // which mean it will glitch just like with the old code...
+ if (_game.platform != Common::kPlatformFMTowns) {
+ x &= ~7;
+ y &= ~1;
+ }
+
_flashlight.w = _flashlight.xStrips * 8;
_flashlight.h = _flashlight.yStrips * 8;
_flashlight.x = x - _flashlight.w / 2 - _screenStartStrip * 8;
_flashlight.y = y - _flashlight.h / 2;
- if (_game.id == GID_LOOM)
+ if (_game.id == GID_LOOM && _game.version == 3 && _game.platform == Common::kPlatformFMTowns)
_flashlight.y -= 12;
// Clip the flashlight at the borders
@@ -1793,7 +1877,7 @@ void ScummEngine_v5::drawFlashlight() {
_flashlight.y = vs->h - _flashlight.h;
// Redraw any actors "under" the flashlight
- for (i = _flashlight.x / 8; i < (_flashlight.x + _flashlight.w) / 8; i++) {
+ for (int i = _flashlight.x / 8; i < (_flashlight.x + _flashlight.w) / 8; i++) {
assert(0 <= i && i < _gdi->_numStrips);
setGfxUsageBit(_screenStartStrip + i, USAGE_BIT_DIRTY);
vs->tdirty[i] = 0;
@@ -1808,34 +1892,141 @@ void ScummEngine_v5::drawFlashlight() {
// C64 & NES does not round the flashlight
if (_game.platform != Common::kPlatformC64 && _game.platform != Common::kPlatformNES) {
- // Round the corners. To do so, we simply hard-code a set of nicely
- // rounded corners.
- static const int corner_data[] = { 8, 6, 4, 3, 2, 2, 1, 1 };
- int minrow = 0;
- int maxcol = (_flashlight.w - 1) * vs->format.bytesPerPixel;
- int maxrow = (_flashlight.h - 1) * vs->pitch;
-
- for (i = 0; i < 8; i++, minrow += vs->pitch, maxrow -= vs->pitch) {
- int d = corner_data[i];
-
- for (j = 0; j < d; j++) {
- if (vs->format.bytesPerPixel == 2) {
+ // Round the corners. Different versions have different rounding parameters.
+ if (vs->format.bytesPerPixel == 1) {
+ int width, height, heightLoc;
+ byte maskValue;
+ bool isIndy3VGA = (_game.id == GID_INDY3 && (_game.features & GF_OLD256));
+
+ height = _flashlight.h - 1;
+
+ byte *buffPtr = _flashlight.buffer;
+
+ if (_game.platform == Common::kPlatformFMTowns) {
+ for (int i = 0; i < 8; ++i) {
+ heightLoc = vs->pitch * height;
+ width = _flashlight.w - 1;
+
+ for (byte j = 128, idx = 0; j; j >>= 1, idx++) {
+ if ((j & townsCurveData[i]) != 0)
+ maskValue = 0xFF; // Pixel ON
+ else
+ maskValue = 0x00; // Pixel OFF
+
+ buffPtr[idx] &= maskValue;
+ buffPtr[idx + heightLoc] &= maskValue;
+ buffPtr[idx + width] &= maskValue;
+ buffPtr[idx + width + heightLoc] &= maskValue;
+
+ width -= 2;
+ }
+
+ buffPtr += vs->pitch;
+ height -= 2;
+ }
+ } else {
+ const byte *fwdCurvePtr, *bkwdCurvePtr;
+
+ switch (_game.version) {
+ case 1:
+ fwdCurvePtr = v1FwdCurveData;
+ bkwdCurvePtr = v1BkwdCurveData;
+ break;
+ case 2:
+ fwdCurvePtr = v2FwdCurveData;
+ bkwdCurvePtr = v2BkwdCurveData;
+ break;
+ case 3:
+ if (isIndy3VGA) {
+ fwdCurvePtr = v4FwdCurveData;
+ bkwdCurvePtr = v4BkwdCurveData;
+ } else {
+ fwdCurvePtr = v2FwdCurveData;
+ bkwdCurvePtr = v2BkwdCurveData;
+ }
+
+ break;
+ default:
+ fwdCurvePtr = bkwdCurvePtr = v4FwdCurveData;
+ }
+
+ if (_game.version <= 3 && !isIndy3VGA) {
+ width = _flashlight.w - 8;
+
+ for (int i = 8, curveHorizLine = 0; i > 0; i--, curveHorizLine += 4) {
+ heightLoc = vs->pitch * height;
+
+ // v1 and v2 use a nibble mask on an half resolution buffer! ARGH!
+ // We have to double the maskings in order to match the original,
+ // without handling an half-res flashlight buffer...
+ for (byte j = 0, ptInCurveHorizLine = 0; j < 8; j += 2, ptInCurveHorizLine++) {
+ // Top left
+ buffPtr[j] &= (fwdCurvePtr[ptInCurveHorizLine + curveHorizLine] & 0xF0) ? 0xFF : 0x00;
+ buffPtr[j + 1] &= (fwdCurvePtr[ptInCurveHorizLine + curveHorizLine] & 0x0F) ? 0xFF : 0x00;
+
+ // Bottom left
+ buffPtr[j + heightLoc] &= (fwdCurvePtr[ptInCurveHorizLine + curveHorizLine] & 0xF0) ? 0xFF : 0x00;
+ buffPtr[j + heightLoc + 1] &= (fwdCurvePtr[ptInCurveHorizLine + curveHorizLine] & 0x0F) ? 0xFF : 0x00;
+
+ // Top right
+ buffPtr[j + width] &= (bkwdCurvePtr[ptInCurveHorizLine + curveHorizLine] & 0x0F) ? 0xFF : 0x00;
+ buffPtr[j + width + 1] &= (bkwdCurvePtr[ptInCurveHorizLine + curveHorizLine] & 0xF0) ? 0xFF : 0x00;
+
+ // Bottom right
+ buffPtr[j + width + heightLoc] &= (bkwdCurvePtr[ptInCurveHorizLine + curveHorizLine] & 0x0F) ? 0xFF : 0x00;
+ buffPtr[j + width + heightLoc + 1] &= (bkwdCurvePtr[ptInCurveHorizLine + curveHorizLine] & 0xF0) ? 0xFF : 0x00;
+ }
+
+ height -= 2;
+ buffPtr += vs->pitch;
+ }
+ } else {
+ byte maskValueFwd, maskValueBkwd;
+
+ for (int i = 0; i < 8; ++i) {
+ heightLoc = vs->pitch * height;
+
+ width = _flashlight.w - 8;
+
+ for (byte j = 8, idx = 0; j; j--, idx++) {
+ maskValueFwd = fwdCurvePtr[i * 8 + idx];
+ maskValueBkwd = isIndy3VGA ? bkwdCurvePtr[i * 8 + idx] : bkwdCurvePtr[i * 8 + j - 1];
+
+ buffPtr[idx] &= maskValueFwd; // Top left
+ buffPtr[idx + heightLoc] &= maskValueFwd; // Bottom left
+ buffPtr[idx + width] &= maskValueBkwd; // Top right
+ buffPtr[idx + width + heightLoc] &= maskValueBkwd; // Bottom right
+ }
+
+ buffPtr += vs->pitch;
+ height -= 2;
+ }
+ }
+ }
+ } else {
+ // The bytesPerPixel == 2 case should only happen for the PC-Engine version of Loom...
+ // I'd rather avoid attempting to reverse what happens here in the original and just
+ // use the old code...
+
+ static const int corner_data[] = { 8, 6, 4, 3, 2, 2, 1, 1 };
+ int minrow = 0;
+ int maxcol = (_flashlight.w - 1) * vs->format.bytesPerPixel;
+ int maxrow = (_flashlight.h - 1) * vs->pitch;
+
+ for (int i = 0; i < 8; i++, minrow += vs->pitch, maxrow -= vs->pitch) {
+ int d = corner_data[i];
+
+ for (int j = 0; j < d; j++) {
WRITE_UINT16(&_flashlight.buffer[minrow + 2 * j], 0);
WRITE_UINT16(&_flashlight.buffer[minrow + maxcol - 2 * j], 0);
WRITE_UINT16(&_flashlight.buffer[maxrow + 2 * j], 0);
WRITE_UINT16(&_flashlight.buffer[maxrow + maxcol - 2 * j], 0);
}
- else {
- _flashlight.buffer[minrow + j] = backgroundColor;
- _flashlight.buffer[minrow + maxcol - j] = backgroundColor;
- _flashlight.buffer[maxrow + j] = backgroundColor;
- _flashlight.buffer[maxrow + maxcol - j] = backgroundColor;
- }
}
}
}
- _flashlight.isDrawn = true;
+ _flashlight.eraseFlag = true;
}
int ScummEngine_v0::getCurrentLights() const {
diff --git a/engines/scumm/scumm_v5.h b/engines/scumm/scumm_v5.h
index 557381b2132..2bed37ddeb1 100644
--- a/engines/scumm/scumm_v5.h
+++ b/engines/scumm/scumm_v5.h
@@ -35,7 +35,7 @@ protected:
int x, y, w, h;
byte *buffer;
uint16 xStrips, yStrips;
- bool isDrawn;
+ bool eraseFlag;
} _flashlight;
char _saveLoadVarsFilename[256];
More information about the Scummvm-git-logs
mailing list