[Scummvm-git-logs] scummvm master -> 964b4850bffa3b9ef541edf6620f79248c53388c

neuromancer noreply at scummvm.org
Tue Jun 2 10:21:58 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:
ec72a8167f SCUMM: RA2: clear buffer after each frame in level 5
964b4850bf SCUMM: RA2: better collision detection for level 4


Commit: ec72a8167fa1392a9bee6d4ba4485f6b09f8569c
    https://github.com/scummvm/scummvm/commit/ec72a8167fa1392a9bee6d4ba4485f6b09f8569c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T12:20:56+02:00

Commit Message:
SCUMM: RA2: clear buffer after each frame in level 5

Changed paths:
    engines/scumm/insane/rebel2/runlevels.cpp
    engines/scumm/smush/rebel/smush_player_ra2.cpp
    engines/scumm/smush/rebel/smush_player_ra2.h


diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 2d054d0c888..63167338abb 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -767,8 +767,9 @@ int InsaneRebel2::runLevel5() {
 		clearBit(0);
 
 		// Play gameplay (05PLAY.SAN)
+		// Original: FUN_0041f4d0("05PLAY.SAN", 8, -1, -1, 0)
 		debug("Rebel2: Level 5 gameplay");
-		if (!playLevelSegment("LEV05/05PLAY.SAN", 0x28))
+		if (!playLevelSegment("LEV05/05PLAY.SAN", 0x08))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 87fb6db2eef..5f22d5dc428 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -87,6 +87,8 @@ void SmushPlayerRebel2::initGamePlayerFields() {
 	_lastLoadChunkIdx = -1;
 	_totalLoadChunks = 0;
 	_ra2FrameSourceSkipY = 0;
+	_ra2FrameObjectSurfaceWidth = 0;
+	_ra2FrameObjectSurfaceHeight = 0;
 	_scrollX = 0;
 	_scrollY = 0;
 }
@@ -115,14 +117,14 @@ void SmushPlayerRebel2::initGameVideoState() {
 
 	// Handle background preservation between videos:
 	// - Cinematic videos (flags 0x20) clear the buffer for a fresh start
-	// - Gameplay videos (flags 0x28) preserve the existing screen content
+	// - Gameplay videos (flags 0x08) preserve the existing screen content
 	if (_dst != nullptr) {
 		VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
 		if ((_curVideoFlags & 0x08) == 0) {
 			// Cinematic mode (flags 0x20) - clear buffer for fresh video
 			memset(_dst, 0, vs->w * vs->h);
 		}
-		// Gameplay mode (flags 0x28): no-op, the existing screen content is preserved.
+		// Gameplay mode: no-op, the existing screen content is preserved.
 	}
 }
 
@@ -173,6 +175,8 @@ bool SmushPlayerRebel2::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameFetch FTCH: Re-decoding stored FOBJ codec=%d pos=(%d,%d) size=%dx%d dataSize=%d",
 			_storedFobjCodec, _storedFobjLeft, _storedFobjTop,
 			_storedFobjWidth, _storedFobjHeight, _storedFobjDataSize);
+		ra2PrepareFrameObjectSurface(_storedFobjLeft, _storedFobjTop,
+			_storedFobjWidth, _storedFobjHeight);
 		decodeFrameObject(_storedFobjCodec, _storedFobjData,
 			_storedFobjLeft, _storedFobjTop,
 			_storedFobjWidth, _storedFobjHeight,
@@ -562,18 +566,40 @@ void SmushPlayerRebel2::ra2HandleTextResource(const char *str, int fontId, int c
  * RA2-specific buffer selection for non-standard FOBJ dimensions.
  * Returns true when the dimensions are valid and updates _dst, _width, _height as needed.
  */
+void SmushPlayerRebel2::ra2PrepareFrameObjectSurface(int left, int top, int width, int height) {
+	_ra2FrameObjectSurfaceWidth = width;
+	_ra2FrameObjectSurfaceHeight = height;
+
+	const int64 right = (int64)left + width;
+	const int64 bottom = (int64)top + height;
+	if (right > _ra2FrameObjectSurfaceWidth && right <= INT_MAX)
+		_ra2FrameObjectSurfaceWidth = (int)right;
+	if (bottom > _ra2FrameObjectSurfaceHeight && bottom <= INT_MAX)
+		_ra2FrameObjectSurfaceHeight = (int)bottom;
+}
+
 bool SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
 	// Rebel2 uses a special buffer for all non-matching frames.
+	// Oversized FOBJ chunks are clipped against the original 424x260 surface.
 	// Level 1: First frame is 424x260 (background), small sprites reuse same buffer
 	// Level 2: Uses virtual screen directly (handled below when _specialBuffer stays null)
-	const int64 bufSize64 = (int64)width * height;
-	if (width <= 0 || height <= 0 || bufSize64 > INT_MAX) {
+	const int screenSize = _vm->_screenWidth * _vm->_screenHeight;
+	const int64 fobjSize64 = (int64)width * height;
+	int surfaceWidth = width;
+	int surfaceHeight = height;
+
+	if (fobjSize64 > screenSize) {
+		surfaceWidth = MAX(surfaceWidth, _ra2FrameObjectSurfaceWidth);
+		surfaceHeight = MAX(surfaceHeight, _ra2FrameObjectSurfaceHeight);
+	}
+
+	const int64 bufSize64 = (int64)surfaceWidth * surfaceHeight;
+	if (width <= 0 || height <= 0 || surfaceWidth <= 0 || surfaceHeight <= 0 || bufSize64 > INT_MAX) {
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Skipping invalid FOBJ dimensions %dx%d", width, height);
 		return false;
 	}
 
 	const int bufSize = (int)bufSize64;
-	const int screenSize = _vm->_screenWidth * _vm->_screenHeight;
 	if (bufSize > screenSize) {
 		// Frame is larger than screen - need special buffer
 		if (_specialBuffer == nullptr || bufSize > _specialBufferSize) {
@@ -586,18 +612,20 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
 			free(_specialBuffer);
 			_specialBuffer = newSpecialBuffer;
 			_specialBufferSize = bufSize;
-			_width = width;
-			_height = height;
+			_width = surfaceWidth;
+			_height = surfaceHeight;
 			// Zero-fill the new buffer to avoid garbage in areas not written by FOBJ codec
 			memset(_specialBuffer, 0, bufSize);
-			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Allocated new _specialBuffer %dx%d (%d bytes)", width, height, bufSize);
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Allocated new _specialBuffer %dx%d for FOBJ %dx%d (%d bytes)",
+				surfaceWidth, surfaceHeight, width, height, bufSize);
 		}
 	}
 
 	if (bufSize > screenSize &&
 	    _specialBuffer != nullptr && _specialBufferSize >= bufSize) {
 		_dst = _specialBuffer;
-		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using _specialBuffer for oversized FOBJ %dx%d", width, height);
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using _specialBuffer %dx%d for oversized FOBJ %dx%d",
+			_width, _height, width, height);
 	} else {
 		if (_specialBuffer == nullptr) {
 			VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
@@ -702,6 +730,7 @@ void SmushPlayerRebel2::ra2HandleGost(int32 subSize, Common::SeekableReadStream
 	// Priority bits (0x2000/0x4000/0x6000) are currently not modeled in
 	// ScummVM's SMUSH decoders. Coordinate-correct re-decode restores expected
 	// RA2 chapter preview behavior.
+	ra2PrepareFrameObjectSurface(left, top, _lastFobjWidth, _lastFobjHeight);
 	decodeFrameObject(_lastFobjCodec, _lastFobjData, left, top,
 		_lastFobjWidth, _lastFobjHeight, _lastFobjDataSize);
 }
@@ -761,6 +790,8 @@ bool SmushPlayerRebel2::handleGameStoreFrame() {
 }
 
 void SmushPlayerRebel2::handleGameFrameObjectPre(int codec, int left, int top, int width, int height, int dataSize) {
+	ra2PrepareFrameObjectSurface(left, top, width, height);
+
 	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameFrameObjectPre FOBJ: frame=%d codec=%d pos=(%d,%d) size=%dx%d dataSize=%d storeFrame=%d _width=%d _height=%d",
 		_frame, codec, left, top, width, height, dataSize, _storeFrame, _width, _height);
 }
@@ -775,6 +806,23 @@ void SmushPlayerRebel2::handleGameFrameObjectPost(int codec, const byte *data, i
 
 void SmushPlayerRebel2::handleGameFrameStart() {
 	_hasFrameFobjForGost = false;
+
+	// FUN_00424d70 clears the target buffer before decoding a frame
+	// unless playback flags contain 0x20. Codec 21 frames in levels like
+	// LEV05/05PLAY.SAN only contain non-zero literals, so stale skipped pixels
+	// must not survive from the previous frame.
+	if ((_curVideoFlags & 0x20) == 0 && _dst != nullptr) {
+		int clearSize = 0;
+		if (_dst == _specialBuffer && _width > 0 && _height > 0) {
+			const int64 size64 = (int64)_width * _height;
+			if (size64 <= INT_MAX && size64 <= _specialBufferSize)
+				clearSize = (int)size64;
+		} else {
+			clearSize = _vm->_screenWidth * _vm->_screenHeight;
+		}
+		if (clearSize > 0)
+			memset(_dst, 0, clearSize);
+	}
 }
 
 bool SmushPlayerRebel2::handleGameSkipChunk(uint32 subType, int32 subSize, Common::SeekableReadStream &b) {
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 084a727fb3d..c10a544cb76 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -66,6 +66,7 @@ private:
 	void ra2HandleTextResource(const char *str, int fontId, int color,
 							   int pos_x, int pos_y, int left, int top,
 							   int width, int height, TextStyleFlags flg);
+	void ra2PrepareFrameObjectSurface(int left, int top, int width, int height);
 	bool ra2SelectFrameBuffer(int width, int height);
 	bool ra2DecodeCodec(int codec, const uint8 *src, int left, int top,
 						int width, int height, int pitch, int dataSize);
@@ -82,6 +83,8 @@ private:
 	int16 _lastLoadChunkIdx;
 	int16 _totalLoadChunks;
 	int _ra2FrameSourceSkipY;
+	int _ra2FrameObjectSurfaceWidth;
+	int _ra2FrameObjectSurfaceHeight;
 };
 
 } // End of namespace Scumm


Commit: 964b4850bffa3b9ef541edf6620f79248c53388c
    https://github.com/scummvm/scummvm/commit/964b4850bffa3b9ef541edf6620f79248c53388c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T12:20:56+02:00

Commit Message:
SCUMM: RA2: better collision detection for level 4

Changed paths:
    engines/scumm/insane/rebel2/iact.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 6fb4ffec57e..36dbc54a38e 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -810,13 +810,11 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	const LevelDifficultyParams &flightParams =
 		kDifficultyTable[CLIP(_difficulty, 0, 5)][flightParamIndex];
 
-	// Step 1: Mouse input as offset from screen center.
+	// Step 1: Raw mouse input as offset from screen center.
 	// DAT_0047a7e0 = mouseX - 160, DAT_0047a7e2 = mouseY - 100.
-	// _vm->_mouse.x/y are in virtual screen coords (0-319, 0-199)
-	// consistent with handler 8 which uses _vm->_mouse.x directly.
-	Common::Point aimPos = getGameplayAimPoint();
-	int16 inputX = (int16)(aimPos.x - 160);  // DAT_0047a7e0
-	int16 inputY = (int16)(aimPos.y - 100);  // DAT_0047a7e2
+	// Handler 7 applies DAT_0047a7fe to its local vertical input after clamping.
+	int16 inputX = (int16)(_vm->_mouse.x - 160);  // DAT_0047a7e0
+	int16 inputY = (int16)(_vm->_mouse.y - 100);  // DAT_0047a7e2
 
 	// Clamp: mouse mode uses [-160, 160] for X, [-127, 127] for Y (lines 55-70).
 	if (inputX > 160)
@@ -831,7 +829,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	// Step 2: Scale to [-127, 127] (lines 82-84).
 	// Mouse mode: scaledInputX = (DAT_0047a7e0 * 0x7f) / 0xa0.
 	int16 scaledInputX = (int16)((inputX * 127) / 160);
-	int16 scaledInputY = inputY;  // Y already in [-127, 127]
+	int16 scaledInputY = _optControlsFlipped ? (int16)-inputY : inputY;  // local_14
 
 	// Step 3: Velocity history + smoothed average (lines 141-157).
 	for (int i = 24; i > 0; i--) {
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 0477679bd45..73b03abc07a 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -432,6 +432,7 @@ public:
 	void resetLevelAttemptState(int initialPhase);
 	void resetLevelPhaseState(bool clearEnemies);
 	void resetLevelWaveState();
+	void resetHandler7FlightState();
 
 	// Random number helper (emulates FUN_004233a0)
 	int getRandomVariant(int max);
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index c93f1c24aa0..194333b6a0a 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -1549,11 +1549,12 @@ void InsaneRebel2::checkCollisionZones() {
 		return;
 
 	// Calculate aim position in centered coordinates.
-	// Original: local_10 = mouseOffset + 0xa0, then smoothed and clamped to [-0x34..0x34]
-	// Simplified mapping: mouse 0..320 → [-52..52], mouse 0..200 → [-45..45]
-	Common::Point aimPos = getGameplayAimPoint();
-	int16 aimX = (int16)((aimPos.x - 160) * 52 / 160);
-	int16 aimY = (int16)((100 - aimPos.y) * 45 / 100);
+	// Handler 0x26 applies the mouse-mode vertical inversion before DAT_0047a7fe
+	// (FUN_407FCB lines 108-123), so this must not use getGameplayAimPoint().
+	const int rawX = _vm->_mouse.x - 160;
+	const int rawY = _vm->_mouse.y - 100;
+	int16 aimX = (int16)(rawX * 52 / 160);
+	int16 aimY = (int16)((_optControlsFlipped ? -rawY : rawY) * 45 / 100);
 
 	// Clamp to original ranges (DAT_0047a7fc < 1 path)
 	if (aimX > 0x34)
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 63167338abb..24c88ce2394 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -294,6 +294,41 @@ void InsaneRebel2::resetLevelWaveState() {
 	_rebelWaveState = 0;
 }
 
+void InsaneRebel2::resetHandler7FlightState() {
+	_hitCooldown = 0;
+	_flyShipScreenX = 0xd4;
+	_flyShipScreenY = 0x82;
+	_smoothedVelocity = 0;
+	_verticalInput = 0;
+	_flyOverlayRepeatCount = 0;
+	_viewShift = 0;
+	_perspectiveX = 0;
+	_perspectiveY = 0;
+	_windParamX = 0;
+	_windParamY = 0;
+	_corridorLeftX = 0;
+	_corridorTopY = 0;
+	_corridorRightX = 0x1a8;
+	_corridorBottomY = 0x104;
+	_facingRight = false;
+	_spaceShotDirection = 0;
+
+	memset(_velocityHistory, 0, sizeof(_velocityHistory));
+	memset(_windHistoryX, 0, sizeof(_windHistoryX));
+	memset(_windHistoryY, 0, sizeof(_windHistoryY));
+
+	for (int i = 0; i < 2; i++) {
+		_spaceShots[i].counter = 0;
+		_spaceShots[i].targetX = 0;
+		_spaceShots[i].targetY = 0;
+		_spaceShots[i].leftGunX = 0;
+		_spaceShots[i].leftGunY = 0;
+		_spaceShots[i].rightGunX = 0;
+		_spaceShots[i].rightGunY = 0;
+		_spaceShots[i].variant = 0;
+	}
+}
+
 // ---------------------------------------------------------------------------
 // Level 2 Handler - FUN_00418063
 // Multiple parts with P1/P2/P3 subdirectories
@@ -600,6 +635,7 @@ int InsaneRebel2::runLevel3() {
 
 		// Reset bit table before gameplay starts - FUN_00423880 calls FUN_00423a00(0)
 		clearBit(0);
+		resetHandler7FlightState();
 
 		// Play phase 1 gameplay (03PLAY1.SAN)
 		debug("Rebel2: Level 3 Phase 1");
@@ -651,6 +687,7 @@ int InsaneRebel2::runLevel3() {
 
 		// Reset bit table before gameplay starts
 		clearBit(0);
+		resetHandler7FlightState();
 
 		// Play phase 2 gameplay (03PLAY2.SAN)
 		debug("Rebel2: Level 3 Phase 2");




More information about the Scummvm-git-logs mailing list