[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