[Scummvm-git-logs] scummvm master -> fc697e4f41e629e2bb9c86868190194e4af0b319
neuromancer
noreply at scummvm.org
Tue Jun 2 17:25:34 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:
50860fbcf7 SCUMM: RA2: defensive check for paused game
fc697e4f41 SCUMM: RA2: better rendering of shooting in level 10
Commit: 50860fbcf70a619a40a42e00c3c5315f8973b4e3
https://github.com/scummvm/scummvm/commit/50860fbcf70a619a40a42e00c3c5315f8973b4e3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T19:25:14+02:00
Commit Message:
SCUMM: RA2: defensive check for paused game
Changed paths:
engines/scumm/insane/rebel2/rebel.cpp
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index f570cda54ca..e0be2b591c6 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -594,6 +594,13 @@ void InsaneRebel2::openGameplayMainMenu(SmushPlayer *splayer) {
bool InsaneRebel2::notifyEvent(const Common::Event &event) {
SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
+ // Global ScummVM dialogs pause the engine while their modal event loop runs.
+ // Do not consume those events as RA2 input: a key seen here would otherwise
+ // trip RA2's "any key unpauses gameplay" path while the Smush player must
+ // remain paused for the dialog/focus interval.
+ if (_vm->isPaused())
+ return false;
+
// Keep the gamepad reticle authoritative against stray pointer events. During
// gameplay the cursor is locked ~screen-center (levels.cpp lockMouse), so any
// spurious pointer event â e.g. a gamepad "fire" surfacing as a button-down â
Commit: fc697e4f41e629e2bb9c86868190194e4af0b319
https://github.com/scummvm/scummvm/commit/fc697e4f41e629e2bb9c86868190194e4af0b319
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T19:25:14+02:00
Commit Message:
SCUMM: RA2: better rendering of shooting in level 10
Changed paths:
engines/scumm/insane/rebel2/iact.cpp
engines/scumm/insane/rebel2/rebel.cpp
engines/scumm/insane/rebel2/rebel.h
engines/scumm/insane/rebel2/render.cpp
engines/scumm/insane/rebel2/runlevels.cpp
diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 36dbc54a38e..fef48209da6 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -1571,6 +1571,23 @@ bool InsaneRebel2::loadOpcode8Handler7FlySprites(Common::SeekableReadStream &b,
return false;
}
+// ScummVM refactor helper for opcode 8 Handler 7 shot table loading, not a separate retail function.
+bool InsaneRebel2::loadOpcode8Handler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
+ // FUN_0040c3cc case 6:
+ // par4=12 -> FUN_0040fcfa(text, DAT_004437c2, DAT_00443808)
+ // par4=13 -> FUN_0040fcfa(text, DAT_0044384e, DAT_00443894)
+ if (_rebelHandler != 7 || (par4 != 12 && par4 != 13))
+ return false;
+
+ if (loadHandler7ShotTable(b, startPos, remaining, par4)) {
+ b.seek(startPos);
+ return true;
+ }
+
+ b.seek(startPos);
+ return false;
+}
+
// ScummVM refactor helper for opcode 8 edge table loading, not a separate retail function.
bool InsaneRebel2::loadOpcode8EdgeTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
// Edge Blend Table Loading (par4 == 1000)
@@ -1800,6 +1817,9 @@ void InsaneRebel2::iactRebel2Opcode8(byte *renderBitmap, Common::SeekableReadStr
if (loadOpcode8Handler7FlySprites(b, startPos, remaining, par4))
return;
+ if (loadOpcode8Handler7ShotTable(b, startPos, remaining, par4))
+ return;
+
if (loadOpcode8EdgeTable(b, startPos, remaining, par4))
return;
@@ -1900,6 +1920,91 @@ bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, i
return true;
}
+// loadHandler7ShotTable -- Parse handler 7 laser muzzle coordinate pairs from IACT payload.
+bool InsaneRebel2::loadHandler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
+ // Retail FUN_0040fcfa parses 35 "%hd %hd" pairs from offset +18 in the IACT
+ // chunk into two parallel 35-entry tables. These tables are BSS globals in
+ // the EXE, so their values only exist in the SAN/IACT stream.
+ if (remaining < 12)
+ return false;
+
+ int64 savedPos = b.pos();
+ b.seek(startPos + 6);
+ uint32 textSize = b.readUint32LE();
+
+ int64 textPos = startPos + 10;
+ int64 maxAvail = remaining - 10;
+ if (maxAvail <= 0) {
+ b.seek(savedPos);
+ return false;
+ }
+
+ int64 bytesToRead = maxAvail;
+ if (textSize > 0 && (int64)textSize <= maxAvail)
+ bytesToRead = textSize;
+
+ char *buf = new char[(size_t)bytesToRead + 1];
+ b.seek(textPos);
+ b.read((byte *)buf, bytesToRead);
+ buf[bytesToRead] = '\0';
+
+ int16 vals[70];
+ int count = 0;
+ const char *p = buf;
+ const char *end = buf + bytesToRead;
+ while (p < end && count < 70) {
+ while (p < end && *p != '-' && *p != '+' && !Common::isDigit(*p))
+ ++p;
+ if (p >= end)
+ break;
+
+ int sign = 1;
+ if (*p == '-' || *p == '+') {
+ if (*p == '-')
+ sign = -1;
+ ++p;
+ }
+
+ if (p >= end || !Common::isDigit(*p))
+ continue;
+
+ int value = 0;
+ while (p < end && Common::isDigit(*p)) {
+ value = value * 10 + (*p - '0');
+ if (value > 32768)
+ value = 32768;
+ ++p;
+ }
+
+ vals[count++] = (int16)CLIP<int>(sign * value, -32768, 32767);
+ }
+
+ delete[] buf;
+ b.seek(savedPos);
+
+ if (count < 70) {
+ debug("Rebel2 Opcode 8: Handler7 par4=%d shot table parse failed (count=%d)", par4, count);
+ return false;
+ }
+
+ int16 *tableX = (par4 == 12) ? _flyLeftGunX : _flyRightGunX;
+ int16 *tableY = (par4 == 12) ? _flyLeftGunY : _flyRightGunY;
+ for (int i = 0; i < 35; ++i) {
+ tableX[i] = vals[i * 2];
+ tableY[i] = vals[i * 2 + 1];
+ }
+
+ if (par4 == 12)
+ _flyLeftGunTableLoaded = true;
+ else
+ _flyRightGunTableLoaded = true;
+
+ debug("Rebel2 Opcode 8: Loaded Handler7 %s gun table idx0=(%d,%d) idx17=(%d,%d) idx34=(%d,%d)",
+ (par4 == 12) ? "left" : "right",
+ tableX[0], tableY[0], tableX[17], tableY[17], tableX[34], tableY[34]);
+ return true;
+}
+
// ---------------------------------------------------------------------------
// Opcode 8 Helper Functions
// ---------------------------------------------------------------------------
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index e0be2b591c6..c76a47b1067 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -347,6 +347,12 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
_spaceShots[i].variant = 0;
}
_spaceShotDirection = 0;
+ memset(_flyLeftGunX, 0, sizeof(_flyLeftGunX));
+ memset(_flyLeftGunY, 0, sizeof(_flyLeftGunY));
+ memset(_flyRightGunX, 0, sizeof(_flyRightGunX));
+ memset(_flyRightGunY, 0, sizeof(_flyRightGunY));
+ _flyLeftGunTableLoaded = false;
+ _flyRightGunTableLoaded = false;
for (i = 0; i < 16; i++) {
_rebelEmbeddedHud[i].pixels = nullptr;
@@ -1474,7 +1480,7 @@ int32 InsaneRebel2::processMouse() {
// - Other gameplay handlers fire while button is held; slot counters still rate-limit.
bool triggerShot = (_rebelHandler == 25) ? (leftPressed && !leftWasPressed) : leftPressed;
if (triggerShot && isShootingAllowed()) {
- Common::Point mousePos = getGameplayAimPoint();
+ Common::Point mousePos = (_rebelHandler == 7) ? getHandler7ShotTargetPoint() : getGameplayAimPoint();
debug("Rebel2 Click: Mouse=(%d,%d) Enemies=%d",
mousePos.x, mousePos.y, _enemies.size());
@@ -1482,7 +1488,11 @@ int32 InsaneRebel2::processMouse() {
spawnShot(mousePos.x, mousePos.y);
// Calculate world position for hit testing
- Common::Point worldMousePos(mousePos.x + _viewX, mousePos.y + _viewY);
+ Common::Point worldMousePos = mousePos;
+ if (_rebelHandler != 7) {
+ worldMousePos.x += _viewX;
+ worldMousePos.y += _viewY;
+ }
// Check for hit on any active enemy
Common::List<enemy>::iterator it;
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 73b03abc07a..6ccc021848f 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -544,6 +544,7 @@ public:
void scanOpcode6EmbeddedAnim(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par4);
void iactRebel2Opcode8(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par2, int16 par3, int16 par4);
bool loadOpcode8Handler7FlySprites(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
+ bool loadOpcode8Handler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
bool loadOpcode8EdgeTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
bool loadOpcode8AuxSfx(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
bool loadOpcode8ShotOriginTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
@@ -607,6 +608,9 @@ public:
// Update target lock state and draw crosshair/reticle
void renderCrosshair(byte *renderBitmap, int pitch, int width, int height);
+ Common::Point getHandler7ShipDrawPoint();
+ Common::Point getHandler7ProjectedPoint();
+ Common::Point getHandler7ShotTargetPoint();
// Reset enemy active flags and collision zones at frame end
void frameEndCleanup();
@@ -618,6 +622,7 @@ public:
// Load Handler 7 FLY NUT sprites from IACT data
bool loadHandler7FlySprites(Common::SeekableReadStream &b, int64 remaining, int16 par4);
+ bool loadHandler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
// Load turret HUD overlay NUT from ANIM data
bool loadTurretHudOverlay(byte *animData, int32 size, int16 par3);
@@ -985,6 +990,12 @@ public:
};
SpaceShot _spaceShots[2];
int16 _spaceShotDirection; // DAT_0044374e - ship direction for gun lookup
+ int16 _flyLeftGunX[35]; // DAT_004437c2 - handler 7 left muzzle X table
+ int16 _flyLeftGunY[35]; // DAT_00443808 - handler 7 left muzzle Y table
+ int16 _flyRightGunX[35]; // DAT_0044384e - handler 7 right muzzle X table
+ int16 _flyRightGunY[35]; // DAT_00443894 - handler 7 right muzzle Y table
+ bool _flyLeftGunTableLoaded;
+ bool _flyRightGunTableLoaded;
// Handler-specific shot spawning
void spawnTurretShot(int x, int y); // Handler 0x26
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 194333b6a0a..9302279fbbd 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -494,6 +494,39 @@ void InsaneRebel2::spawnHandler25Shot(int x, int y) {
}
}
+Common::Point InsaneRebel2::getHandler7ShipDrawPoint() {
+ int shipCenterX = (_flyShipScreenX - 0xd4) + _perspectiveX + 160 + _viewX;
+ int shipCenterY = (_flyShipScreenY - 0x82) + _perspectiveY + 100 + _viewY;
+
+ if (!_flyShipSprite || _flyShipSprite->getNumChars() <= 0)
+ return Common::Point(shipCenterX - 0xd4, shipCenterY - 0x82);
+
+ int spriteIndex = CLIP<int>(_shipDirectionIndex, 0, _flyShipSprite->getNumChars() - 1);
+ return Common::Point(shipCenterX - _flyShipSprite->getCharWidth(spriteIndex) / 2,
+ shipCenterY - _flyShipSprite->getCharHeight(spriteIndex) / 2);
+}
+
+Common::Point InsaneRebel2::getHandler7ProjectedPoint() {
+ int viewTilt = (_viewShift * 5) / 128;
+ int xSkew = (viewTilt * 9) / 4;
+ int ySkew = viewTilt * 5;
+ int relX = _flyShipScreenX - (_perspectiveX + 0xd4);
+ int relY = _flyShipScreenY - (_perspectiveY + 0x82);
+ int projectedX = (xSkew * relY) / 0x55 + relX + 0xa0 + _viewX;
+ int projectedY = relY + 0x55 - (ySkew * relX) / 0xa0 + _viewY;
+
+ return Common::Point(projectedX, projectedY);
+}
+
+Common::Point InsaneRebel2::getHandler7ShotTargetPoint() {
+ // Retail handler 7 targets the projected ship/crosshair point computed in
+ // FUN_0040d836; it does not use the generic mouse position for combat shots.
+ Common::Point projected = getHandler7ProjectedPoint();
+
+ return Common::Point(projected.x + _smoothedVelocity / 2,
+ projected.y + ABS(_smoothedVelocity) / 4 - _verticalInput / 2 - 0x28);
+}
+
// spawnSpaceShot -- Handler 7 space combat shot spawn (FUN_40D836).
void InsaneRebel2::spawnSpaceShot(int x, int y) {
for (int i = 0; i < 2; i++) {
@@ -502,21 +535,30 @@ void InsaneRebel2::spawnSpaceShot(int x, int y) {
playSfx(6, 127, 0);
_spaceShots[i].counter = getShotMaxDuration();
- _spaceShots[i].targetX = x; // Screen coords
- _spaceShots[i].targetY = y;
-
- // Calculate gun positions from direction-based lookup tables
- // In the original, these come from tables indexed by _shipDirectionIndex
- // DAT_004437c2/DAT_00443808 for left gun, DAT_0044384e/DAT_00443894 for right gun
- // Use simplified positions relative to the ship.
- int shipScreenX = 160 + ((_shipPosX - 160) >> 3);
- int shipScreenY = 105 + ((_shipPosY - 40) >> 2);
-
- // Gun offsets (approximate from disassembly)
- _spaceShots[i].leftGunX = shipScreenX - 28;
- _spaceShots[i].leftGunY = shipScreenY + 10;
- _spaceShots[i].rightGunX = shipScreenX + 28;
- _spaceShots[i].rightGunY = shipScreenY + 10;
+ Common::Point projected = getHandler7ProjectedPoint();
+ Common::Point target = getHandler7ShotTargetPoint();
+ int tableIndex = CLIP<int>(_shipDirectionIndex, 0, 34);
+
+ _spaceShots[i].targetX = target.x;
+ _spaceShots[i].targetY = target.y;
+
+ // FUN_0040d836 uses muzzle tables loaded by FUN_0040fcfa from opcode
+ // 8 par4=12/13. Values are centered FLY coordinates, adjusted by
+ // the projected ship point before drawing the line.
+ if (_flyLeftGunTableLoaded) {
+ _spaceShots[i].leftGunX = projected.x + _flyLeftGunX[tableIndex] - 0xd4;
+ _spaceShots[i].leftGunY = projected.y + _flyLeftGunY[tableIndex] - 0x82;
+ } else {
+ _spaceShots[i].leftGunX = projected.x - 28;
+ _spaceShots[i].leftGunY = projected.y + 10;
+ }
+ if (_flyRightGunTableLoaded) {
+ _spaceShots[i].rightGunX = projected.x + _flyRightGunX[tableIndex] - 0xd4;
+ _spaceShots[i].rightGunY = projected.y + _flyRightGunY[tableIndex] - 0x82;
+ } else {
+ _spaceShots[i].rightGunX = projected.x + 28;
+ _spaceShots[i].rightGunY = projected.y + 10;
+ }
_spaceShots[i].variant = _spaceShotDirection;
break;
}
@@ -975,34 +1017,6 @@ void InsaneRebel2::initLaserTexture(NutRenderer *nut, int spriteIdx) {
debug("Rebel2: Initialized laser texture %dx%d from sprite %d (xoff=%d yoff=%d src=%dx%d)",
texWidth, texHeight, spriteIdx, srcXOff, srcYOff, srcWidth, srcHeight);
-
- // Diagnostic: dump texture pixel stats to verify data is loaded correctly
- if (_laserTexture.pixels && texWidth > 0 && texHeight > 0) {
- int third = texWidth / 3;
- int band1 = 0, band2 = 0, band3 = 0;
- for (int row = 0; row < texHeight; row++) {
- for (int col = 0; col < texWidth; col++) {
- if (_laserTexture.pixels[row * texWidth + col] != 0) {
- if (col < third)
- band1++;
- else if (col < third * 2)
- band2++;
- else
- band3++;
- }
- }
- }
- debug("Rebel2: Texture non-zero pixels by band: [0-%d]=%d [%d-%d]=%d [%d-%d]=%d",
- third - 1, band1, third, third * 2 - 1, band2, third * 2, texWidth - 1, band3);
-
- // Dump first row hex (first 64 bytes)
- Common::String hexRow;
- int dumpLen = MIN(texWidth, (int16)64);
- for (int col = 0; col < dumpLen; col++) {
- hexRow += Common::String::format("%02x ", _laserTexture.pixels[col]);
- }
- debug("Rebel2: Texture row 0 (first %d): %s", dumpLen, hexRow.c_str());
- }
}
// freeLaserTexture -- Emulates FUN_0040BBD1.
@@ -3052,9 +3066,9 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
if (spriteIndex >= numSprites)
spriteIndex = numSprites - 1;
- // Simplified FUN_41C720-like transform to current render buffer coordinates.
- int shipCenterX = (_flyShipScreenX - 0xd4) + _perspectiveX + 160 + _viewX;
- int shipCenterY = (_flyShipScreenY - 0x82) + _perspectiveY + 100 + _viewY;
+ Common::Point shipDraw = getHandler7ShipDrawPoint();
+ int shipCenterX = shipDraw.x + 0xd4;
+ int shipCenterY = shipDraw.y + 0x82;
// FUN_40D836 lines 108-136: FLY002 proximity cues near corridor danger.
if (_flyLaserSprite && _flyLaserSprite->getNumChars() > 0) {
@@ -3103,11 +3117,8 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
}
}
- // Center the sprite on the position
- int spriteW = _flyShipSprite->getCharWidth(spriteIndex);
- int spriteH = _flyShipSprite->getCharHeight(spriteIndex);
- int drawX = shipCenterX - spriteW / 2;
- int drawY = shipCenterY - spriteH / 2;
+ int drawX = shipDraw.x;
+ int drawY = shipDraw.y;
renderNutSprite(renderBitmap, pitch, width, height, drawX, drawY, _flyShipSprite, spriteIndex);
@@ -4035,8 +4046,12 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
// Update target lock state and draw crosshair/reticle
// Target lock detection (DAT_00443676 equivalent)
- Common::Point aimPos = getGameplayAimPoint();
- Common::Point worldMousePos(aimPos.x + _viewX, aimPos.y + _viewY);
+ Common::Point aimPos = (_rebelHandler == 7) ? getHandler7ShotTargetPoint() : getGameplayAimPoint();
+ Common::Point worldMousePos = aimPos;
+ if (_rebelHandler != 7) {
+ worldMousePos.x += _viewX;
+ worldMousePos.y += _viewY;
+ }
bool targetLocked = false;
for (Common::List<enemy>::iterator it = _enemies.begin(); it != _enemies.end(); ++it) {
@@ -4083,8 +4098,12 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
int ch = _smush_iconsNut->getCharHeight(reticleIndex);
// Calculate crosshair position
- int crosshairX = aimPos.x - cw / 2 + _viewX;
- int crosshairY = aimPos.y - ch / 2 + _viewY;
+ int crosshairX = aimPos.x - cw / 2;
+ int crosshairY = aimPos.y - ch / 2;
+ if (_rebelHandler != 7) {
+ crosshairX += _viewX;
+ crosshairY += _viewY;
+ }
// Handler 25 (0x19): Add view offset to crosshair position
// From FUN_41DB5E lines 198-199: X = DAT_00457914 + DAT_0045790c, Y = DAT_00457916 + DAT_0045790e
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 24c88ce2394..ba3abf80298 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -312,6 +312,12 @@ void InsaneRebel2::resetHandler7FlightState() {
_corridorBottomY = 0x104;
_facingRight = false;
_spaceShotDirection = 0;
+ memset(_flyLeftGunX, 0, sizeof(_flyLeftGunX));
+ memset(_flyLeftGunY, 0, sizeof(_flyLeftGunY));
+ memset(_flyRightGunX, 0, sizeof(_flyRightGunX));
+ memset(_flyRightGunY, 0, sizeof(_flyRightGunY));
+ _flyLeftGunTableLoaded = false;
+ _flyRightGunTableLoaded = false;
memset(_velocityHistory, 0, sizeof(_velocityHistory));
memset(_windHistoryX, 0, sizeof(_windHistoryX));
More information about the Scummvm-git-logs
mailing list