[Scummvm-git-logs] scummvm master -> 3dc821daaecb64a36ee648daafb3170f0cd750ff
neuromancer
noreply at scummvm.org
Wed Jun 3 07:57:26 UTC 2026
This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
651c56dba8 SCUMM: RA1: better collision/shoot detection in level 1 part 2
87bf21fc04 SCUMM: RA2: fixed corruption in L1 -> L2
3dc821daae SCUMM: RA2: implement arrow hints to avoid collisions
Commit: 651c56dba8d022cad40c19d75f69ef6de4c68997
https://github.com/scummvm/scummvm/commit/651c56dba8d022cad40c19d75f69ef6de4c68997
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T08:41:45+02:00
Commit Message:
SCUMM: RA1: better collision/shoot detection in level 1 part 2
Changed paths:
engines/scumm/insane/rebel1/iact.cpp
engines/scumm/insane/rebel1/render.cpp
diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index b5d28d028d7..fb8e22f4b00 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -102,6 +102,21 @@ inline bool ra1DispatcherHudOnlyWhenDisabled(uint32 opcode) {
}
}
+bool ra1TargetCursorUsesProjection(uint16 opcode) {
+ switch (opcode) {
+ // FOBJ target chunks can precede the paired 0x0A GAME chunk, leaving 0x08
+ // as the active opcode in turret sections such as L1PLAY2.
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x1A:
+ return true;
+ default:
+ return false;
+ }
+}
+
inline bool isLevel2DamageLatch(uint16 code) {
switch (code) {
case 0x0003:
@@ -1108,8 +1123,9 @@ void InsaneRebel1::updateShipPhysics() {
// FUN_1DEB5 updates the curve table via FUN_22549 after SetCameraOffset.
// The full DOS path blends a few roll-history terms; use the current roll
- // accumulator so side-looking still bends the gameplay projection.
- rebuildProjectionTable(CLIP<int16>((int16)(-(_rollAccum >> 7)), -0x1A, 0x1A), 0x1A);
+ // accumulator so side-looking still bends the gameplay projection. DOS
+ // negates before the arithmetic shift.
+ rebuildProjectionTable((int16)CLIP<int32>((-_rollAccum) >> 7, -0x1A, 0x1A), 0x1A);
// --- Step 8: Direction sprite index (FUN_1DEB5 LAB_1e23e) ---
// Horizontal component from _74CA (rollAccum):
@@ -1272,9 +1288,9 @@ void InsaneRebel1::updateTurretShipDirection(int16 offsetY) {
void InsaneRebel1::getCollisionShipCenter(int16 &x, int16 &y) const {
// Original 0x0D/0x0E collision compares script zones transformed by
- // FUN_2248C against the source-buffer ship center (base center +
- // g_shipOffset). Keep this in the same 384x242 space; the final viewport
- // crop is only a presentation step.
+ // FUN_223FE against the gameplay-window ship center (base center +
+ // g_shipOffset). This is DOS screen space; render overlays add the viewport
+ // offset separately when drawing into ScummVM's larger source buffer.
//
// In Level 1 part 2, HandleGameOp0A_TurretVariant reuses _shipPos for the
// targeting cursor, so collision must read the movement accumulator instead.
@@ -1409,7 +1425,8 @@ void InsaneRebel1::updateTurretPhysics() {
// FUN_1E6A7 rebuilds the side-look curve with a shallower table than the
// main flight handler, derived directly from roll.
- rebuildProjectionTable((int16)(-(_rollAccum >> 9)), 0x0D);
+ // DOS negates before the arithmetic shift.
+ rebuildProjectionTable((int16)((-_rollAccum) >> 9), 0x0D);
// Regeneration + survival bonus via FUN_1BB0E call in this path.
if ((_frameCounter & 0x1F) == 0) {
@@ -2090,10 +2107,10 @@ void InsaneRebel1::handleGameOpcode0DCorridor(int32 subSize, Common::SeekableRea
int16 centerX = corridorLeft + corridorWidth / 2;
int16 centerY = corridorTop + corridorHeight / 2;
- // DOS FUN_1C54D calls FUN_2248C here, which adds the current
- // camera offset to the scripted rectangle center before testing it
- // against the source-buffer ship center.
- unprojectGameplayPoint(centerX, centerY);
+ // DOS FUN_1C54D calls FUN_223FE here, which projects the scripted
+ // rectangle center into gameplay-window space before testing it against the
+ // ship center.
+ projectGameplayPoint(centerX, centerY);
_corridorLeftX = centerX - corridorWidth / 2;
_corridorTopY = centerY - corridorHeight / 2;
@@ -2165,8 +2182,8 @@ void InsaneRebel1::handleGameOpcode0EZone(int32 subSize, Common::SeekableReadStr
int16 centerX = zoneLeft + zoneWidth / 2;
int16 centerY = zoneTop + zoneHeight / 2;
- // Same transform as opcode 0x0D/FUN_1C54D.
- unprojectGameplayPoint(centerX, centerY);
+ // Same gameplay-window FUN_223FE transform as opcode 0x0D/FUN_1C54D.
+ projectGameplayPoint(centerX, centerY);
zoneLeft = centerX - zoneWidth / 2;
zoneTop = centerY - zoneHeight / 2;
@@ -2410,16 +2427,17 @@ void InsaneRebel1::processShot() {
}
// checkTargetHit â FUN_1C0EF (0x1C0EF). AABB target detection with snap tolerance.
-// The original compares target bounds against the cursor after UnprojectScreenPoint()
-// because g_shipPos is a screen-space cursor. Most handlers in this port draw the
-// cursor into the 384x242 source buffer before the viewport crop, so their cursor is
-// already in the same target space as raw FOBJ bounds. Opcode 0x0B remains screen-space
-// and still needs the original project/unproject conversion.
+// The original compares raw FOBJ bounds against the cursor after
+// UnprojectScreenPoint(). Keep that separate from 0x0D/0x0E collision, which projects
+// zones into gameplay-window screen space before comparing against the ship center.
void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16 right, int16 bottom) {
int16 snap = _tuning.snap;
- const bool screenSpaceCursor = (getEffectiveGameOpcode() == 0x0B);
- int16 curX = getGameplayCursorX();
- int16 curY = getGameplayCursorY();
+ const uint16 effectiveOpcode = getEffectiveGameOpcode();
+ const bool screenSpaceCursor = ra1TargetCursorUsesProjection(effectiveOpcode);
+ const int16 screenCursorX = getGameplayCursorX();
+ const int16 screenCursorY = getGameplayCursorY();
+ int16 curX = screenCursorX;
+ int16 curY = screenCursorY;
if (screenSpaceCursor)
unprojectGameplayPoint(curX, curY);
const int slot = _targetCount;
@@ -2447,6 +2465,11 @@ void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16
if (slot < kMaxTargetBoxes)
_targetBoxVariant[slot] = CLIP<int16>((int16)(_targetBoxVariant[slot] + 3), 0, 5);
+ debugC(DEBUG_INSANE, "RA1 target near: opcode=0x%02x target=%d raw=[%d,%d]-[%d,%d] cursorScreen=(%d,%d) cursorTest=(%d,%d) snap=%d prox=%d view=(%d,%d)",
+ effectiveOpcode, targetIdx, left, top, right, bottom,
+ screenCursorX, screenCursorY, curX, curY, snap, _targetProximity,
+ _perspectiveX, _perspectiveY);
+
// Check tight lock: cursor within target + snap (no extra margin)
if (curX > left - snap && curX < right + snap &&
curY > top - snap && curY < bottom + snap) {
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index ac5c801a121..f428e495c39 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -31,9 +31,8 @@ inline int ra1OverlayViewOffsetX(const InsaneRebel1 *rebel1) {
if (!rebel1 || !rebel1->isInteractiveVideoActive())
return 0;
- // In opcode 0x0B (FUN_1CDA7), marker/shot coordinates are in the gameplay
- // window. Under ScummVM's FUN_224FD crop emulation, shift them into the
- // 384-wide source buffer so they stay aligned after the source-window crop.
+ // Opcode 0x0B target/GOST markers are handled as raw/projected coordinates
+ // in their callers. Keep this helper scoped to that legacy path.
return (rebel1->getEffectiveGameOpcode() == 0x0B) ? rebel1->getPerspectiveX() : 0;
}
@@ -44,6 +43,42 @@ inline int ra1OverlayViewOffsetY(const InsaneRebel1 *rebel1) {
return (rebel1->getEffectiveGameOpcode() == 0x0B) ? rebel1->getPerspectiveY() : 0;
}
+int ra1GameplayWindowOffsetX(const InsaneRebel1 *rebel1) {
+ if (!rebel1 || !rebel1->isInteractiveVideoActive())
+ return 0;
+
+ // Ship/cursor/shot coordinates are in DOS's 320x200 gameplay window. Under
+ // ScummVM's FUN_224FD crop emulation, shift them into the 384x242 source
+ // buffer so the final source-window crop presents them at the same screen
+ // position DOS used for gameplay and collision.
+ switch (rebel1->getEffectiveGameOpcode()) {
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ return rebel1->getPerspectiveX();
+ default:
+ return 0;
+ }
+}
+
+int ra1GameplayWindowOffsetY(const InsaneRebel1 *rebel1) {
+ if (!rebel1 || !rebel1->isInteractiveVideoActive())
+ return 0;
+
+ switch (rebel1->getEffectiveGameOpcode()) {
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ return rebel1->getPerspectiveY();
+ default:
+ return 0;
+ }
+}
+
void drawBankString(const RA1SpriteBank &bank, byte *dst, int pitch, int width, int height,
int x, int y, const char *text) {
if (!dst || !text || bank.numSprites <= 0)
@@ -840,8 +875,8 @@ void InsaneRebel1::renderTargetBoxes(byte *dst, int pitch, int width, int height
void InsaneRebel1::renderTargeting(byte *dst, int pitch, int width, int height) {
const char kRA1TorpedoIndicator[] = "<<d";
const RA1SpriteBank &markerBank = (_techFontBank.numSprites > 0) ? _techFontBank : _hudFontBank;
- const int overlayX = ra1OverlayViewOffsetX(this);
- const int overlayY = ra1OverlayViewOffsetY(this);
+ const int overlayX = ra1GameplayWindowOffsetX(this);
+ const int overlayY = ra1GameplayWindowOffsetY(this);
if (markerBank.numSprites > 0) {
// FUN_1CB22 can switch marker sets via DAT_75FF bit 1.
// Baseline RA1 targeting uses '^' and animation e..h.
@@ -1155,21 +1190,21 @@ void InsaneRebel1::renderLaserShots(byte *dst, int pitch, int width, int height)
0, -3, -19, -24, -30, -28, -30, -29, -20, -5
};
const int spritesPerSet = 5;
- const int overlayX = ra1OverlayViewOffsetX(this);
- const int overlayY = ra1OverlayViewOffsetY(this);
+ const int overlayX = ra1GameplayWindowOffsetX(this);
+ const int overlayY = ra1GameplayWindowOffsetY(this);
const int leftStartX = 0;
const int rightStartX = 0x13F; // 319
const uint16 effectiveOpcode = getEffectiveGameOpcode();
const bool onFootMode = (effectiveOpcode == 0x19 || effectiveOpcode == 0x1A);
const bool turretMode = (effectiveOpcode == 0x08 || effectiveOpcode == 0x0A);
const bool flightVariantMode = (effectiveOpcode == 0x09);
- int shipBaseX = _shipPosX;
- int shipBaseY = flightVariantMode ? _shipPosY : (overlayY + _shipPosY);
+ int shipBaseX = overlayX + _shipPosX;
+ int shipBaseY = overlayY + _shipPosY;
if (turretMode) {
int16 centerX, centerY;
getTurretShipCenter(centerX, centerY);
- shipBaseX = centerX;
- shipBaseY = centerY;
+ shipBaseX = overlayX + centerX;
+ shipBaseY = overlayY + centerY;
}
for (int i = 0; i < kMaxShotSlots; i++) {
@@ -1589,8 +1624,8 @@ void InsaneRebel1::renderShip(byte *dst, int pitch, int width, int height) {
shipScreenY = centerY;
}
- int drawX = shipScreenX - spr.width / 2;
- int drawY = shipScreenY - spr.height / 2;
+ int drawX = ra1GameplayWindowOffsetX(this) + shipScreenX - spr.width / 2;
+ int drawY = ra1GameplayWindowOffsetY(this) + shipScreenY - spr.height / 2;
renderSprite(dst, pitch, width, height, drawX, drawY, spr);
}
@@ -1601,8 +1636,8 @@ void InsaneRebel1::renderExplosions(byte *dst, int pitch, int width, int height)
if (_bangBank.numSprites <= 0)
return;
- const int overlayX = ra1OverlayViewOffsetX(this);
- const int overlayY = ra1OverlayViewOffsetY(this);
+ const int overlayX = ra1GameplayWindowOffsetX(this);
+ const int overlayY = ra1GameplayWindowOffsetY(this);
// In 0x08/0x0A turret handlers, explosion anchors use the ship center, not
// the targeting cursor stored in _shipPos. Flight handlers already keep the
// ship center in _shipPos.
@@ -1612,8 +1647,8 @@ void InsaneRebel1::renderExplosions(byte *dst, int pitch, int width, int height)
if (effectiveOpcode == 0x08 || effectiveOpcode == 0x0A) {
int16 centerX, centerY;
getTurretShipCenter(centerX, centerY);
- shipScreenX = centerX;
- shipScreenY = centerY;
+ shipScreenX = overlayX + centerX;
+ shipScreenY = overlayY + centerY;
}
// --- Death shake explosions (FUN_1DEB5 LAB_1e0e3) ---
Commit: 87bf21fc0426da2da7817f87072d774f8ee656c3
https://github.com/scummvm/scummvm/commit/87bf21fc0426da2da7817f87072d774f8ee656c3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T09:18:52+02:00
Commit Message:
SCUMM: RA2: fixed corruption in L1 -> L2
Changed paths:
engines/scumm/insane/rebel2/runlevels.cpp
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index a71a39285c5..77f5c4cbdb0 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -269,9 +269,8 @@ void InsaneRebel2::resetLevelPhaseState(bool clearEnemies) {
_grd002Sprite = nullptr;
_grdShotOriginTableLoaded = false;
- static const int handler25FrameSlots[] = { 4, 6, 7, 10, 12, 13 };
- for (uint i = 0; i < ARRAYSIZE(handler25FrameSlots); ++i) {
- EmbeddedSanFrame &frame = _rebelEmbeddedHud[handler25FrameSlots[i]];
+ for (uint i = 0; i < ARRAYSIZE(_rebelEmbeddedHud); ++i) {
+ EmbeddedSanFrame &frame = _rebelEmbeddedHud[i];
free(frame.pixels);
frame.pixels = nullptr;
frame.width = 0;
Commit: 3dc821daaecb64a36ee648daafb3170f0cd750ff
https://github.com/scummvm/scummvm/commit/3dc821daaecb64a36ee648daafb3170f0cd750ff
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T09:53:01+02:00
Commit Message:
SCUMM: RA2: implement arrow hints to avoid collisions
Changed paths:
engines/scumm/insane/rebel2/rebel.h
engines/scumm/insane/rebel2/render.cpp
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 6ccc021848f..c4ed62eb146 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -836,10 +836,10 @@ public:
// Reset collision zone counters (called at end of frame)
void resetCollisionZones();
- // Per-frame collision checking against registered zones (FUN_4092D9 first loop)
+ // Per-frame collision checking against registered zones (FUN_4092D9)
// Tests aim/ship position against primary zone quadrilaterals
// Applies collision damage from DAT_0047e0f6 when inside obstacle zone
- void checkCollisionZones();
+ void checkCollisionZones(byte *renderBitmap, int pitch, int width, int height, int32 curFrame);
// Handler 7 collision system (FUN_40E35E)
// Mode 0/2: Obstacle collision using secondary zones â inside quad = hit
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 9302279fbbd..e5a373e253f 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -39,6 +39,13 @@ namespace Scumm {
extern void smushDecodeRLE(byte *dst, const byte *src, int left, int top, int width, int height, int pitch);
extern void smushDecodeUncompressed(byte *dst, const byte *src, int left, int top, int width, int height, int pitch);
+int getRebel2IndicatorScale(int width, int height) {
+ // Retail only doubles these anchors in true high-res mode (DAT_0047a808 >= 2).
+ // RA2's 424x260 low-res gameplay buffer is still displayed through a 320x200
+ // viewport, so it uses the original 320x200 indicator coordinates.
+ return (width >= 640 || height >= 400) ? 2 : 1;
+}
+
static bool isValidEmbeddedFrame(const InsaneRebel2::EmbeddedSanFrame &frame) {
return frame.valid && frame.pixels && frame.width > 0 && frame.height > 0;
}
@@ -1545,8 +1552,8 @@ void InsaneRebel2::resetCollisionZones() {
//
// checkCollisionZones -- Per-frame collision test against primary zones (FUN_4092D9).
//
-void InsaneRebel2::checkCollisionZones() {
- // Per-frame collision checking â FUN_4092D9 first loop (lines 39-202).
+void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width, int height, int32 curFrame) {
+ // Per-frame collision checking â FUN_4092D9.
// Tests aim/ship position against primary collision zone quadrilaterals.
//
// Original coordinate system:
@@ -1580,6 +1587,12 @@ void InsaneRebel2::checkCollisionZones() {
if (aimY < -0x2d)
aimY = -0x2d;
+ LevelDifficultyParams dparams = getDifficultyParams();
+ const bool showDirectionalWarnings = (dparams.flags & 8) != 0;
+ const bool showGenericWarnings = !showDirectionalWarnings && ((dparams.flags & 0x10) != 0);
+ const bool warningFrame = ((curFrame & 2) != 0) && _smush_iconsNut && (showDirectionalWarnings || showGenericWarnings);
+ const int indicatorScale = getRebel2IndicatorScale(width, height);
+
for (int i = 0; i < _primaryZoneCount; i++) {
CollisionZone &zone = _primaryZones[i];
if (!zone.active)
@@ -1590,11 +1603,6 @@ void InsaneRebel2::checkCollisionZones() {
if (zone.filterValue >= 1000)
continue;
- // Frame check: field2 - 1 == field1
- // Original: sVar2 + -1 == (int)sVar1
- if (zone.field2 - 1 != zone.field1)
- continue;
-
// Center zone vertices by subtracting buffer center (0xD4=212, 0x82=130)
// Original: sVar4 = x1 - 0xD4, sVar8 = y1 - 0x82, etc.
int cx1 = zone.x1 - 0xD4;
@@ -1606,64 +1614,94 @@ void InsaneRebel2::checkCollisionZones() {
int cx4 = zone.x4 - 0xD4;
int cy4 = zone.y4 - 0x82;
- // Point-in-quadrilateral test â FUN_4092D9 lines 119-128
- // Tests if aim position is OUTSIDE the safe corridor (= collision with obstacle).
- // Original uses 4 edge interpolation tests connected by OR (any failure = collision).
- //
- // Edge 1: interpolate Y along top edge (v1âv2) at aim X position
- // if aimY < interpolated Y â outside top edge â collision
- // Edge 2: interpolate Y along bottom edge (v4âv3) at aim X position
- // if interpolated Y < aimY â outside bottom edge â collision
- // Edge 3: interpolate X along left edge (v1âv4) at aim Y position
- // if aimX < interpolated X â outside left edge â collision
- // Edge 4: interpolate X along right edge (v2âv3) at aim Y position
- // if interpolated X < aimX â outside right edge â collision
- bool collision = false;
-
- // Avoid division by zero for degenerate edges
- if (cx2 != cx1) {
- int interpY1 = ((aimX - cx1) * (cy2 - cy1)) / (cx2 - cx1) + cy1;
- if (aimY < interpY1)
- collision = true;
- }
- if (!collision && cx3 != cx4) {
- int interpY2 = ((aimX - cx4) * (cy3 - cy4)) / (cx3 - cx4) + cy4;
- if (interpY2 < aimY)
- collision = true;
- }
- if (!collision && cy4 != cy1) {
- int interpX1 = ((aimY - cy1) * (cx4 - cx1)) / (cy4 - cy1) + cx1;
- if (aimX < interpX1)
- collision = true;
- }
- if (!collision && cy3 != cy2) {
- int interpX2 = ((aimY - cy2) * (cx3 - cx2)) / (cy3 - cy2) + cx2;
- if (interpX2 < aimX)
- collision = true;
- }
+ // Frame check: field2 - 1 == field1
+ // Original: sVar2 + -1 == (int)sVar1
+ if (zone.field2 - 1 == zone.field1) {
+ // Point-in-quadrilateral test â FUN_4092D9 lines 119-128
+ // Tests if aim position is OUTSIDE the safe corridor (= collision with obstacle).
+ // Original uses 4 edge interpolation tests connected by OR (any failure = collision).
+ //
+ // Edge 1: interpolate Y along top edge (v1âv2) at aim X position
+ // if aimY < interpolated Y â outside top edge â collision
+ // Edge 2: interpolate Y along bottom edge (v4âv3) at aim X position
+ // if interpolated Y < aimY â outside bottom edge â collision
+ // Edge 3: interpolate X along left edge (v1âv4) at aim Y position
+ // if aimX < interpolated X â outside left edge â collision
+ // Edge 4: interpolate X along right edge (v2âv3) at aim Y position
+ // if interpolated X < aimX â outside right edge â collision
+ bool collision = false;
+
+ // Avoid division by zero for degenerate edges
+ if (cx2 != cx1) {
+ int interpY1 = ((aimX - cx1) * (cy2 - cy1)) / (cx2 - cx1) + cy1;
+ if (aimY < interpY1)
+ collision = true;
+ }
+ if (!collision && cx3 != cx4) {
+ int interpY2 = ((aimX - cx4) * (cy3 - cy4)) / (cx3 - cx4) + cy4;
+ if (interpY2 < aimY)
+ collision = true;
+ }
+ if (!collision && cy4 != cy1) {
+ int interpX1 = ((aimY - cy1) * (cx4 - cx1)) / (cy4 - cy1) + cx1;
+ if (aimX < interpX1)
+ collision = true;
+ }
+ if (!collision && cy3 != cy2) {
+ int interpX2 = ((aimY - cy2) * (cx3 - cx2)) / (cy3 - cy2) + cx2;
+ if (interpX2 < aimX)
+ collision = true;
+ }
- if (collision) {
- // Collision detected â apply damage from collision damage table
- // Original: DAT_0047a7ec += DAT_0047e0f6[chapter * 0x242 + level * 0x22]
- LevelDifficultyParams params = getDifficultyParams();
- int collisionDamage = (params.dodgeDamage >= 0) ? params.dodgeDamage : 0;
-
- if (!_rebelInvulnerable) {
- _playerDamage += collisionDamage;
- if (_playerDamage > 255)
- _playerDamage = 255;
- debug("Rebel2: COLLISION damage! zone=%d aim=(%d,%d) damage=%d total=%d",
- i, aimX, aimY, collisionDamage, _playerDamage);
+ if (collision) {
+ // Collision detected â apply damage from collision damage table
+ // Original: DAT_0047a7ec += DAT_0047e0f6[chapter * 0x242 + level * 0x22]
+ int collisionDamage = (dparams.dodgeDamage >= 0) ? dparams.dodgeDamage : 0;
+
+ if (!_rebelInvulnerable) {
+ _playerDamage += collisionDamage;
+ if (_playerDamage > 255)
+ _playerDamage = 255;
+ debug("Rebel2: COLLISION damage! zone=%d aim=(%d,%d) damage=%d total=%d",
+ i, aimX, aimY, collisionDamage, _playerDamage);
+ }
+ // Visual effect â FUN_00420515 (palette flash)
+ initDamageFlash();
+ // TODO: FUN_0041189e sound based on collision direction
+ } else {
+ // Safely passed â award score bonus
+ // Original: FUN_0041bf8d(DAT_0047e100[levelIdx])
+ if (dparams.dodgePoints > 0) {
+ addScore(dparams.dodgePoints);
+ }
}
- // Visual effect â FUN_00420515 (palette flash)
- initDamageFlash();
- // TODO: FUN_0041189e sound based on collision direction
- } else {
- // Safely passed â award score bonus
- // Original: FUN_0041bf8d(DAT_0047e100[levelIdx])
- LevelDifficultyParams scoreParams = getDifficultyParams();
- if (scoreParams.dodgePoints > 0) {
- addScore(scoreParams.dodgePoints);
+ } else if (warningFrame && zone.field2 - 0x0c < zone.field1) {
+ // FUN_4092D9 near-collision indicators. Easy mode (flags bit 0x08)
+ // draws directional arrows from cockpit icon slots 0x2a..0x2d.
+ // Novice mode (flags bit 0x10) draws a generic indicator from slot 0x36.
+ const int iconChars = _smush_iconsNut->getNumChars();
+ if (showDirectionalWarnings) {
+ int avgX = (cx1 + cx2 + cx3 + cx4) / 4;
+ int avgY = (cy1 + cy2 + cy3 + cy4) / 4;
+
+ if (avgX >= 0x15 && iconChars > 0x2d) {
+ renderNutSprite(renderBitmap, pitch, width, height,
+ 0xd7 * indicatorScale + _viewX, 0x55 * indicatorScale + _viewY, _smush_iconsNut, 0x2d);
+ } else if (avgX < -0x14 && iconChars > 0x2c) {
+ renderNutSprite(renderBitmap, pitch, width, height,
+ 0x69 * indicatorScale + _viewX, 0x55 * indicatorScale + _viewY, _smush_iconsNut, 0x2c);
+ }
+
+ if (avgY >= 0x15 && iconChars > 0x2b) {
+ renderNutSprite(renderBitmap, pitch, width, height,
+ 0xa0 * indicatorScale + _viewX, 0x82 * indicatorScale + _viewY, _smush_iconsNut, 0x2b);
+ } else if (avgY < -0x14 && iconChars > 0x2a) {
+ renderNutSprite(renderBitmap, pitch, width, height,
+ 0xa0 * indicatorScale + _viewX, 0x28 * indicatorScale + _viewY, _smush_iconsNut, 0x2a);
+ }
+ } else if (showGenericWarnings && iconChars > 0x36) {
+ renderNutSprite(renderBitmap, pitch, width, height,
+ 0xa0 * indicatorScale + _viewX, 0x1e * indicatorScale + _viewY, _smush_iconsNut, 0x36);
}
}
}
@@ -1925,7 +1963,7 @@ void InsaneRebel2::renderHandler7WarningCues(byte *renderBitmap, int pitch, int
// Note: These are cue sprites (often perceived as "shadows"), not the aiming reticle.
LevelDifficultyParams dparams = getDifficultyParams();
if ((curFrame & 2) != 0 && (dparams.flags & 8) != 0 && _smush_iconsNut) {
- int scale = (_vm->_screenWidth > 320 || _vm->_screenHeight > 200) ? 2 : 1;
+ int scale = getRebel2IndicatorScale(width, height);
if ((warningMask & 1) != 0 && _smush_iconsNut->getNumChars() > 0x2d) {
renderNutSprite(renderBitmap, pitch, width, height,
@@ -2340,7 +2378,7 @@ void InsaneRebel2::checkGameplayPostRenderCollisions(byte *renderBitmap, int pit
// Mode 1/3: PRIMARY zones (0x0D) - wall/boundary per-edge with push-back
// Uses ship position in raw buffer coords, hit cooldown, directional damage.
if (_rebelHandler == 0x26) {
- checkCollisionZones();
+ checkCollisionZones(renderBitmap, pitch, width, height, curFrame);
} else if (_rebelHandler == 7) {
checkHandler7CollisionZones(renderBitmap, pitch, width, height, curFrame);
}
More information about the Scummvm-git-logs
mailing list