[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