[Scummvm-git-logs] scummvm master -> c14a871dd6b241e0048aeeef8f94015defe8468a
neuromancer
noreply at scummvm.org
Thu Jul 2 06:39:02 UTC 2026
This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
5876cedb9b SCUMM: RA2: L2 and L11 starts with the player fully in cover
504aed8ed3 SCUMM: RA2: make sure force field are disabled when destroying all its parts
1e934bfa1c SCUMM: RA2: fidelity fixes for L10 and L15
cbc1f4d7ed SCUMM: RA2: implement timepoints in all the levels
ceb75188de SCUMM: RA2: correctly implemented waves in L2 and L12
c14a871dd6 SCUMM: RA2: added missing gameplay sounds
Commit: 5876cedb9b0cfd4cef9d8ba61ce50d27f350f8c4
https://github.com/scummvm/scummvm/commit/5876cedb9b0cfd4cef9d8ba61ce50d27f350f8c4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-02T08:36:36+02:00
Commit Message:
SCUMM: RA2: L2 and L11 starts with the player fully in cover
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 e6aaec7d7a6..520ac299ea5 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -268,10 +268,11 @@ void InsaneRebel2::resetLevelPhaseState(bool clearEnemies) {
if (clearEnemies)
_enemies.clear();
- if (_selectedChapter == 1 || _selectedChapter == 10) {
- _rebelAutopilot = 1;
- _rebelDamageLevel = 5;
- }
+ // Every phase starts with the player fully in cover. This only has an
+ // effect in the on-foot cover sections (handler 25, levels 2 and 11);
+ // the other levels ignore these values.
+ _rebelAutopilot = 1;
+ _rebelDamageLevel = 5;
}
void InsaneRebel2::clearEmbeddedHudFrames() {
Commit: 504aed8ed3a0e053c1ea614541065d5ee7b1b39a
https://github.com/scummvm/scummvm/commit/504aed8ed3a0e053c1ea614541065d5ee7b1b39a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-02T08:36:37+02:00
Commit Message:
SCUMM: RA2: make sure force field are disabled when destroying all its parts
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 3e4eef11df8..c693f280798 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -295,32 +295,27 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
}
}
} else if (value > 99 && value < 110) {
+ int idx = value - 100;
if (!isBitSet(targetId)) {
- int idx = value - 100;
- if (idx >= 0 && idx < 10) {
- _rebelValueCounters[idx]++;
- _rebelLastCounter = _rebelValueCounters[idx];
- if (_rebelShieldGateActive && targetId >= 0 && targetId < 512) {
- _rebelGaugeSlot[targetId] = (int8)idx;
- _rebelGaugeArmed = true;
- _rebelLastArmedSlot = idx;
- }
- debugC(DEBUG_INSANE, "IACT Opcode2: Increment VAL counter[%d] -> %d (target=%d)", value, _rebelValueCounters[idx], targetId);
- }
+ _rebelValueCounters[idx]++;
+ debugC(DEBUG_INSANE, "IACT Opcode2: Increment counter[%d] -> %d (target=%d)", idx, _rebelValueCounters[idx], targetId);
+ }
+ _rebelLastCounter = _rebelValueCounters[idx];
+ if (_rebelShieldGateActive) {
+ _rebelGaugeArmed = true;
+ _rebelLastArmedSlot = idx;
}
-
} else if (value > 0x3ff) {
for (int slot = 1; slot <= 9; ++slot) {
if ((value & (1 << (slot - 1))) != 0) {
if (!isBitSet(targetId)) {
- _rebelMaskCounters[slot]++;
- _rebelLastCounter = _rebelMaskCounters[slot];
- if (_rebelShieldGateActive && targetId >= 0 && targetId < 512) {
- _rebelGaugeSlot[targetId] = (int8)(10 + slot);
- _rebelGaugeArmed = true;
- _rebelLastArmedSlot = 10 + slot;
- }
- debugC(DEBUG_INSANE, "IACT Opcode2: Increment MASK counter[%d] -> %d (target=%d)", slot, _rebelMaskCounters[slot], targetId);
+ _rebelValueCounters[slot]++;
+ debugC(DEBUG_INSANE, "IACT Opcode2: Increment counter[%d] (mask 0x%x) -> %d (target=%d)", slot, value, _rebelValueCounters[slot], targetId);
+ }
+ _rebelLastCounter = _rebelValueCounters[slot];
+ if (_rebelShieldGateActive) {
+ _rebelGaugeArmed = true;
+ _rebelLastArmedSlot = slot;
}
}
}
@@ -2147,13 +2142,19 @@ void InsaneRebel2::enemyUpdate(byte *renderBitmap, Common::SeekableReadStream &b
int16 w = b.readSint16LE();
int16 h = b.readSint16LE();
- // Turret surface targets mirror their gauge group state.
+ // Turret surface targets mirror their gauge group state: hidden while the group
+ // counter sits at zero, shown while it is nonzero, with a blink-out (alternating
+ // odd/even ticks) during the 6 frames after the group is depleted.
if (par3 == 2 && _rebelHandler == 0x26) {
- const int surfaceIdx = (par4 >= 100 && par4 < 110) ? (par4 - 100) : -1;
- if (surfaceIdx >= 0 && _rebelGaugeCleared[surfaceIdx])
- setBit(enemyId);
- else
- clearBit(enemyId);
+ if (par4 >= 100 && par4 < 110) {
+ const int surfaceIdx = par4 - 100;
+ if (_rebelValueCounters[surfaceIdx] == 0 && (_rebelGaugeBlink[surfaceIdx] & 1) == 0)
+ setBit(enemyId);
+ else
+ clearBit(enemyId);
+ if (_rebelGaugeBlink[surfaceIdx] != 0)
+ _rebelGaugeBlink[surfaceIdx]--;
+ }
return;
}
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 3903f0b3f04..935d70e7a75 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -244,19 +244,15 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
for (int i = 0; i < 10; ++i) {
_rebelValueCounters[i] = 0;
- _rebelMaskCounters[i] = 0;
+ _rebelGaugeBlink[i] = 0;
}
_rebelLastCounter = 0;
- for (int i = 0; i < 512; ++i)
- _rebelGaugeSlot[i] = -1;
_rebelShieldGateActive = false;
_rebelShieldDestroyed = false;
_rebelReactorMode = false;
_rebelGaugeArmed = false;
_rebelLastArmedSlot = -1;
- for (int i = 0; i < 10; ++i)
- _rebelGaugeCleared[i] = false;
_difficulty = 1;
_targetLockTimer = 0;
@@ -1610,25 +1606,18 @@ int32 InsaneRebel2::processMouse() {
setBit(it->id);
- // Shield hit-point gauge: if this target feeds a gauge counter, destroying it
- // decrements that counter; the shield is destroyed when it reaches 0.
- if (_rebelShieldGateActive && it->id >= 0 && it->id < 512 && _rebelGaugeSlot[it->id] >= 0) {
- int slot = _rebelGaugeSlot[it->id];
- short *counter = (slot < 10) ? &_rebelValueCounters[slot]
- : &_rebelMaskCounters[slot - 10];
- if (*counter > 0) {
- (*counter)--;
- _rebelLastCounter = *counter;
- if (*counter == 0) {
- if (slot < 10)
- _rebelGaugeCleared[slot] = true;
- if (!_rebelReactorMode) {
- _rebelShieldDestroyed = true;
- debugC(DEBUG_INSANE, "Shield destroyed (gauge slot %d depleted by target %d)", slot, it->id);
- }
+ // Shield hit-point gauge (handler 0x26): the target's type field addresses
+ // the gauge group(s) it feeds â 100-109 directly, > 0x3ff as a bitmask â
+ // and destroying it decrements each of them.
+ if (_rebelHandler == 0x26) {
+ if (it->type >= 100 && it->type < 110) {
+ decrementGaugeGroup(it->type - 100, it->id);
+ } else if (it->type > 0x3ff) {
+ for (int slot = 1; slot <= 9; ++slot) {
+ if ((it->type & (1 << (slot - 1))) != 0)
+ decrementGaugeGroup(slot, it->id);
}
}
- _rebelGaugeSlot[it->id] = -1;
}
if (it->type > 0 && it->type < 32) {
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index f4ccbf1b8d1..efd13079ae1 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -692,20 +692,18 @@ public:
int _rebelViewMode1;
int _rebelViewMode2;
+ // Turret (0x26) gauge groups, addressed by value 100-109 or bitmask > 0x3ff (bit k-1 -> slot k); surfaces show while nonzero, then blink out.
short _rebelValueCounters[10];
- short _rebelMaskCounters[10];
+ short _rebelGaugeBlink[10];
int _rebelLastCounter;
- // Shield hit-point gauge: opcode-2 sets up a per-target counter; destroying a tracked
- // target decrements it, and the looping attack run ends when it reaches 0.
- int8 _rebelGaugeSlot[512];
bool _rebelShieldGateActive;
bool _rebelShieldDestroyed;
bool _rebelReactorMode;
bool _rebelGaugeArmed;
int _rebelLastArmedSlot;
- bool _rebelGaugeCleared[10];
void resetShieldGauge();
+ void decrementGaugeGroup(int slot, int targetId);
// Handler-specific shot state.
struct TurretShot {
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index ddaa6bf2aa6..c7c97513b07 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -2415,8 +2415,7 @@ void InsaneRebel2::procPostRendering(byte *renderBitmap, int32 codecparam, int32
if (_rebelReactorMode && _rebelGaugeArmed && _rebelLastArmedSlot >= 0 &&
(_player->_curVideoFlags & 0x40) != 0) {
const int slot = _rebelLastArmedSlot;
- const short remaining = (slot < 10) ? _rebelValueCounters[slot]
- : _rebelMaskCounters[slot - 10];
+ const short remaining = _rebelValueCounters[slot];
if (remaining <= 0)
_rebelShieldDestroyed = true;
}
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 520ac299ea5..d9796d6bf2c 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -296,16 +296,24 @@ void InsaneRebel2::resetLevelWaveState() {
void InsaneRebel2::resetShieldGauge() {
for (int i = 0; i < 10; ++i) {
_rebelValueCounters[i] = 0;
- _rebelMaskCounters[i] = 0;
+ _rebelGaugeBlink[i] = 0;
}
- for (int i = 0; i < 512; ++i)
- _rebelGaugeSlot[i] = -1;
_rebelLastCounter = -1; // non-zero sentinel: gauge not yet depleted
_rebelShieldDestroyed = false;
_rebelGaugeArmed = false;
_rebelLastArmedSlot = -1;
- for (int i = 0; i < 10; ++i)
- _rebelGaugeCleared[i] = false;
+}
+
+void InsaneRebel2::decrementGaugeGroup(int slot, int targetId) {
+ _rebelValueCounters[slot]--;
+ if (_rebelValueCounters[slot] == 0) {
+ _rebelGaugeBlink[slot] = 6;
+ if (_rebelShieldGateActive && !_rebelReactorMode) {
+ _rebelShieldDestroyed = true;
+ debugC(DEBUG_INSANE, "Shield destroyed (gauge slot %d depleted by target %d)", slot, targetId);
+ }
+ }
+ _rebelLastCounter = _rebelValueCounters[slot];
}
void InsaneRebel2::resetExplosions() {
@@ -1023,6 +1031,7 @@ int InsaneRebel2::runLevel9() {
clearBit(0);
_rebelKillCounter = 0;
_rebelHitCounter = 0;
+ resetShieldGauge();
_rebelPhaseState = 0xfffffffe;
Commit: 1e934bfa1c5b6ff4e6124c12a19e22c73d8bebc3
https://github.com/scummvm/scummvm/commit/1e934bfa1c5b6ff4e6124c12a19e22c73d8bebc3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-02T08:36:37+02:00
Commit Message:
SCUMM: RA2: fidelity fixes for L10 and L15
Changed paths:
engines/scumm/insane/rebel2/rebel.cpp
engines/scumm/insane/rebel2/rebel.h
engines/scumm/insane/rebel2/render.cpp
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 935d70e7a75..d02a717f90f 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -1285,7 +1285,7 @@ InsaneRebel2::LevelDifficultyParams InsaneRebel2::getDifficultyParams() const {
// Lv1->0, Lv2->1, Lv3->2, Lv4->3, Lv5->4,
// Lv6A->5, Lv6B->6, Lv7->7 ... Lv14->14, Lv15A->15, Lv15B->16.
// Our Level 6 phase flow sets _currentPhase to 1/2 accordingly.
- // Level 15 phase switch to 16 is currently approximated by _currentPhase >= 2.
+ // Level 15 switches to type 0x10 at frame 0x21e (updateLevel15TypeSwitch).
if (_selectedLevel <= 0) {
lvIdx = CLIP((int)_rebelLevelType, 0, 16);
} else if (_selectedLevel <= 5) {
@@ -1295,7 +1295,7 @@ InsaneRebel2::LevelDifficultyParams InsaneRebel2::getDifficultyParams() const {
} else if (_selectedLevel <= 14) {
lvIdx = _selectedLevel;
} else { // _selectedLevel == 15
- lvIdx = (_currentPhase >= 2) ? 16 : 15;
+ lvIdx = (_rebelLevelType >= 0x10) ? 16 : 15;
}
lvIdx = CLIP(lvIdx, 0, 16);
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index efd13079ae1..081e3f207fa 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -640,6 +640,7 @@ public:
void checkHandler7BoundaryZones(uint16 &warningMask);
void renderHandler7WarningCues(byte *renderBitmap, int pitch, int width, int height, int32 curFrame, uint16 warningMask);
void updateLevel7Fork(int32 curFrame);
+ void updateLevel15TypeSwitch(int32 curFrame);
int16 _playerDamage;
int16 _playerShield;
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index c7c97513b07..42da57d39bc 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -629,8 +629,16 @@ void InsaneRebel2::spawnSpaceShot(int x, int y) {
playSfx(6, 127, 0);
_spaceShots[i].counter = getShotMaxDuration();
+ // Shots are stored in presentation-window space (the projection without
+ // the scroll/crop offset), like the original; spawning happens before the
+ // frame's presentation pass, so strip the offset the projection baked in
+ // and let renderSpaceLaserShots apply the current frame's one.
Common::Point projected = getHandler7ProjectedPoint();
Common::Point target = getHandler7ShotTargetPoint();
+ projected.x -= _viewX;
+ projected.y -= _viewY;
+ target.x -= _viewX;
+ target.y -= _viewY;
int tableIndex = CLIP<int>(_shipDirectionIndex, 0, 34);
_spaceShots[i].targetX = target.x;
@@ -2126,6 +2134,12 @@ void InsaneRebel2::updateLevel7Fork(int32 curFrame) {
}
}
+void InsaneRebel2::updateLevel15TypeSwitch(int32 curFrame) {
+ // From frame 0x21e level 15 uses the type-0x10 difficulty column; runLevel15 resets to 0xf per attempt.
+ if (_selectedLevel == 15 && curFrame >= 0x21e)
+ _rebelLevelType = 0x10;
+}
+
void InsaneRebel2::renderPostRenderMenuCursor(byte *renderBitmap, int pitch, int width, int height) {
static const byte kRa2MenuCursor[] = {
0, 0, 1, 1, 1, 1, 1,
@@ -2407,6 +2421,7 @@ void InsaneRebel2::procPostRendering(byte *renderBitmap, int32 codecparam, int32
updatePostRenderScroll(width, height);
updatePostRenderDeath();
updateLevel7Fork(curFrame);
+ updateLevel15TypeSwitch(curFrame);
// End the looping attack-run segment once the shield/reactor is destroyed.
if (_rebelShieldGateActive) {
@@ -3980,6 +3995,13 @@ void InsaneRebel2::renderVehicleLaserShots(byte *renderBitmap, int pitch, int wi
void InsaneRebel2::renderSpaceLaserShots(byte *renderBitmap, int pitch, int width, int height) {
int16 maxDuration = getShotMaxDuration();
+ // Shots are stored in presentation-window space; convert with the offset the
+ // current frame's presentation actually consumed (drawLaserBeam subtracts it
+ // back in high-res, the low-res paths draw into the scrolled/warped buffer).
+ const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+ const int16 nativeViewX = renderHiRes ? _hiResPresentationViewX : _viewX;
+ const int16 nativeViewY = renderHiRes ? _hiResPresentationViewY : _viewY;
+
for (int i = 0; i < 2; i++) {
if (_spaceShots[i].counter <= 0)
continue;
@@ -3987,12 +4009,12 @@ void InsaneRebel2::renderSpaceLaserShots(byte *renderBitmap, int pitch, int widt
int16 pan = ((_spaceShots[i].targetX - 160) * (2 - _spaceShots[i].counter)) / 2;
pan = CLIP<int16>(pan, -127, 127);
- int16 targetX = _spaceShots[i].targetX;
- int16 targetY = _spaceShots[i].targetY;
- int16 leftGunX = _spaceShots[i].leftGunX;
- int16 leftGunY = _spaceShots[i].leftGunY;
- int16 rightGunX = _spaceShots[i].rightGunX;
- int16 rightGunY = _spaceShots[i].rightGunY;
+ int16 targetX = _spaceShots[i].targetX + nativeViewX;
+ int16 targetY = _spaceShots[i].targetY + nativeViewY;
+ int16 leftGunX = _spaceShots[i].leftGunX + nativeViewX;
+ int16 leftGunY = _spaceShots[i].leftGunY + nativeViewY;
+ int16 rightGunX = _spaceShots[i].rightGunX + nativeViewX;
+ int16 rightGunY = _spaceShots[i].rightGunY + nativeViewY;
int16 progress = maxDuration - _spaceShots[i].counter;
drawLaserBeam(renderBitmap, pitch, width, height,
Commit: cbc1f4d7ede7be4e7a56ab00374c4424cf20d0b3
https://github.com/scummvm/scummvm/commit/cbc1f4d7ede7be4e7a56ab00374c4424cf20d0b3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-02T08:36:37+02:00
Commit Message:
SCUMM: RA2: implement timepoints in all the levels
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 081e3f207fa..dc2596d6bb3 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -456,7 +456,7 @@ public:
void renderGameplayPostFrame(byte *renderBitmap, int pitch, int width, int height,
int videoWidth, int videoHeight, int statusBarY, int32 curFrame);
void updateGameplayDamageEffects(byte *renderBitmap, int pitch, int width, int height);
- void updateGameplayDamageRecovery(int32 curFrame);
+ void updateGameplayTimedTick(int32 curFrame);
void checkGameplayPostRenderCollisions(byte *renderBitmap, int pitch, int width, int height, int32 curFrame);
void renderTurretHudOverlays(byte *renderBitmap, int pitch, int width, int height, int32 curFrame);
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 42da57d39bc..232db68f421 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -2290,15 +2290,22 @@ void InsaneRebel2::updateGameplayDamageEffects(byte *renderBitmap, int pitch, in
}
}
-void InsaneRebel2::updateGameplayDamageRecovery(int32 curFrame) {
- // timed score tick in the same slot and does not reduce damage.
- if ((_rebelHandler != 0x26 && _rebelHandler != 8 && _rebelHandler != 7) ||
- (curFrame & 0xf) != 0 || _playerDamage <= 0) {
+void InsaneRebel2::updateGameplayTimedTick(int32 curFrame) {
+ // Every 16th frame the handlers score timePoints and recover one point of
+ // damage; the cover handler (25) scores without recovering.
+ if ((curFrame & 0xf) != 0)
return;
- }
+ if (_rebelHandler != 7 && _rebelHandler != 8 && _rebelHandler != 25 && _rebelHandler != 0x26)
+ return;
+
+ const int16 timePoints = getDifficultyParams().timePoints;
+ if (timePoints > 0)
+ addScore(timePoints);
- _playerDamage--;
- _playerShield = 255 - _playerDamage;
+ if (_rebelHandler != 25 && _playerDamage > 0) {
+ _playerDamage--;
+ _playerShield = 255 - _playerDamage;
+ }
}
void InsaneRebel2::checkGameplayPostRenderCollisions(byte *renderBitmap, int pitch, int width, int height, int32 curFrame) {
@@ -2389,7 +2396,7 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
updateGameplayDamageEffects(renderBitmap, pitch, width, height);
checkGameplayPostRenderCollisions(renderBitmap, pitch, width, height, curFrame);
- updateGameplayDamageRecovery(curFrame);
+ updateGameplayTimedTick(curFrame);
renderCrosshair(renderBitmap, pitch, width, height);
Commit: ceb75188de10119772d76a738526af8ad6a463a2
https://github.com/scummvm/scummvm/commit/ceb75188de10119772d76a738526af8ad6a463a2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-02T08:36:37+02:00
Commit Message:
SCUMM: RA2: correctly implemented waves in L2 and L12
Changed paths:
engines/scumm/insane/rebel2/rebel.h
engines/scumm/insane/rebel2/runlevels.cpp
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index dc2596d6bb3..edd517494b4 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -877,6 +877,7 @@ public:
static const LevelDifficultyParams kDifficultyTable[6][17];
LevelDifficultyParams getDifficultyParams() const;
+ int16 getWaveBudgetBase(int phase) const;
void addScore(int points);
void renderScoreHUD(byte *renderBitmap, int pitch, int width, int height, int statusBarY);
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index d9796d6bf2c..1517a5a9b5f 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -316,6 +316,23 @@ void InsaneRebel2::decrementGaugeGroup(int slot, int targetId) {
_rebelLastCounter = _rebelValueCounters[slot];
}
+int16 InsaneRebel2::getWaveBudgetBase(int phase) const {
+ // Per-phase wave budgets share the tail columns (rollRate..driftRate) of the
+ // previous level-type row with the flight movement rates.
+ const LevelDifficultyParams &p =
+ kDifficultyTable[CLIP(_difficulty, 0, 5)][CLIP(_rebelLevelType - 1, 0, 16)];
+ switch (phase) {
+ case 1:
+ return p.rollRate;
+ case 2:
+ return p.liftRate;
+ case 3:
+ return p.slideRate;
+ default:
+ return p.driftRate;
+ }
+}
+
void InsaneRebel2::resetExplosions() {
for (uint i = 0; i < ARRAYSIZE(_explosions); ++i) {
_explosions[i].active = false;
@@ -370,8 +387,6 @@ void InsaneRebel2::resetHandler7FlightState() {
}
int InsaneRebel2::runLevel2() {
- const int16 kLevel2BudgetBase[3] = { 3, 3, 3 };
-
int bonusCount = 0;
int totalKills = 0;
int totalMisses = 0;
@@ -395,7 +410,7 @@ int InsaneRebel2::runLevel2() {
resetLevelPhaseState(false);
- int16 budget = kLevel2BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
+ int16 budget = getWaveBudgetBase(1) + _vm->_rnd.getRandomNumber(2);
debugC(DEBUG_INSANE, "Level 2 Phase 1 - playing 02P01_A.SAN (background) budget=%d", budget);
if (!playLevelSegment("LEV02/P1/02P01_A.SAN", 0x28))
@@ -444,7 +459,7 @@ int InsaneRebel2::runLevel2() {
_currentPhase = 2;
resetLevelPhaseState(true);
- budget = kLevel2BudgetBase[1] + _vm->_rnd.getRandomNumber(2);
+ budget = getWaveBudgetBase(2) + _vm->_rnd.getRandomNumber(2);
_rebelHandler = 8;
@@ -510,7 +525,7 @@ int InsaneRebel2::runLevel2() {
resetLevelPhaseState(true);
prevWaveState = 0;
- budget = kLevel2BudgetBase[2] + _vm->_rnd.getRandomNumber(2);
+ budget = getWaveBudgetBase(3) + _vm->_rnd.getRandomNumber(2);
_rebelHandler = 8;
@@ -1120,8 +1135,6 @@ int InsaneRebel2::runLevel11() {
int totalMisses = 0;
int prevPhaseState = 0;
- const int16 kLevel11BudgetBase[4] = { 3, 3, 3, 3 };
-
playCinematic("LEV11/11CUT.SAN");
if (_vm->shouldQuit())
return kLevelQuit;
@@ -1140,7 +1153,7 @@ int InsaneRebel2::runLevel11() {
resetLevelPhaseState(false);
- int16 budget = kLevel11BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
+ int16 budget = getWaveBudgetBase(1) + _vm->_rnd.getRandomNumber(2);
debugC(DEBUG_INSANE, "Level 11 Phase 1 - playing 11P01_A.SAN budget=%d", budget);
if (!playLevelSegment("LEV11/P1/11P01_A.SAN", 0x28))
@@ -1196,7 +1209,7 @@ int InsaneRebel2::runLevel11() {
_currentPhase = 2;
resetLevelPhaseState(true);
- budget = kLevel11BudgetBase[1] + _vm->_rnd.getRandomNumber(2);
+ budget = getWaveBudgetBase(2) + _vm->_rnd.getRandomNumber(2);
_rebelHandler = 8;
debugC(DEBUG_INSANE, "Level 11 Phase 2 - playing 11P02_A.SAN budget=%d", budget);
@@ -1248,7 +1261,7 @@ int InsaneRebel2::runLevel11() {
resetLevelPhaseState(true);
prevPhaseState = 0;
- budget = kLevel11BudgetBase[2] + _vm->_rnd.getRandomNumber(2);
+ budget = getWaveBudgetBase(3) + _vm->_rnd.getRandomNumber(2);
_rebelHandler = 8;
debugC(DEBUG_INSANE, "Level 11 Phase 3 first half - playing 11P03_A.SAN budget=%d", budget);
@@ -1323,7 +1336,7 @@ int InsaneRebel2::runLevel11() {
_rebelHandler = 8;
- budget = kLevel11BudgetBase[3] + _vm->_rnd.getRandomNumber(2);
+ // The phase-3 budget carries into the second half; no new roll.
debugC(DEBUG_INSANE, "Level 11 Phase 3 second half - playing 11P03_G.SAN budget=%d", budget);
if (!playLevelSegment("LEV11/P3/11P03_G.SAN", 0x28))
@@ -1392,8 +1405,6 @@ int InsaneRebel2::runLevel11() {
}
int InsaneRebel2::runLevel12() {
- const int16 kLevel12BudgetBase[4] = { 3, 4, 4, 4 };
-
playCinematic("LEV12/12CUT.SAN");
if (_vm->shouldQuit())
return kLevelQuit;
@@ -1409,7 +1420,7 @@ int InsaneRebel2::runLevel12() {
resetLevelPhaseState(false);
- int16 budget = kLevel12BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
+ int16 budget = getWaveBudgetBase(1) + _vm->_rnd.getRandomNumber(2);
debugC(DEBUG_INSANE, "Level 12 Phase 1 - init 12P05.SAN budget=%d", budget);
if (!playLevelSegment("LEV12/12P05.SAN", 0x28, false))
@@ -1456,7 +1467,7 @@ int InsaneRebel2::runLevel12() {
_currentPhase = 2;
resetLevelWaveState();
- budget = kLevel12BudgetBase[1] + _vm->_rnd.getRandomNumber(3);
+ budget = getWaveBudgetBase(2) + _vm->_rnd.getRandomNumber(3);
debugC(DEBUG_INSANE, "Level 12 Phase 2 - init 12P06.SAN budget=%d", budget);
if (!playLevelSegment("LEV12/12P06.SAN", 0x428, false))
@@ -1513,7 +1524,7 @@ int InsaneRebel2::runLevel12() {
_currentPhase = 3;
resetLevelWaveState();
- budget = kLevel12BudgetBase[2] + _vm->_rnd.getRandomNumber(3);
+ budget = getWaveBudgetBase(3) + _vm->_rnd.getRandomNumber(3);
debugC(DEBUG_INSANE, "Level 12 Phase 3 - init 12P07.SAN budget=%d", budget);
if (!playLevelSegment("LEV12/12P07.SAN", 0x428, false))
@@ -1567,7 +1578,7 @@ int InsaneRebel2::runLevel12() {
_currentPhase = 4;
resetLevelWaveState();
- budget = kLevel12BudgetBase[3] + _vm->_rnd.getRandomNumber(3);
+ budget = getWaveBudgetBase(4) + _vm->_rnd.getRandomNumber(3);
debugC(DEBUG_INSANE, "Level 12 Phase 4 - init 12P08.SAN budget=%d", budget);
if (!playLevelSegment("LEV12/12P08.SAN", 0x428, false))
Commit: c14a871dd6b241e0048aeeef8f94015defe8468a
https://github.com/scummvm/scummvm/commit/c14a871dd6b241e0048aeeef8f94015defe8468a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-02T08:36:37+02:00
Commit Message:
SCUMM: RA2: added missing gameplay sounds
Changed paths:
engines/scumm/insane/rebel2/menu.cpp
engines/scumm/insane/rebel2/rebel.cpp
engines/scumm/insane/rebel2/render.cpp
engines/scumm/insane/rebel2/runlevels.cpp
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index 555441cdfec..cc1ef27966b 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -663,6 +663,7 @@ int InsaneRebel2::runChapterSelect() {
}
}
_passwordInput.clear();
+ playSfx(3, 127, 0);
debugC(DEBUG_INSANE, "Password rejected for chapter %d", _chapterSelection + 1);
}
}
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index d02a717f90f..aec354ca8de 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -1326,6 +1326,7 @@ void InsaneRebel2::addScore(int points) {
int newMilestone = (_playerScore + points) / threshold;
if (oldMilestone < newMilestone) {
_playerLives++;
+ playSfx(5, 127, 0);
debugC(DEBUG_INSANE, "BONUS LIFE! Score crossed %d threshold. Lives=%d", threshold, _playerLives);
}
}
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 232db68f421..23a73c2c0a7 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -1587,6 +1587,7 @@ void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width,
}
if (!_noDamage)
initDamageFlash();
+ playSfx(1, 127, CLIP(((cx1 + cx2 + cx3 + cx4) / 4) * -4, -127, 127));
} else {
if (dparams.dodgePoints > 0) {
addScore(dparams.dodgePoints);
@@ -2976,6 +2977,23 @@ void InsaneRebel2::renderStatusBarSprites(byte *renderBitmap, int pitch, int wid
int numSprites = _smush_cockpitNut->getNumChars();
const int statusScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
+ // Critical-damage warning beep every 25 gameplay frames; volume depends on the level type.
+ if (_rebelHandler != 0 && _playerDamage > 170 && (curFrame % 25) == 0) {
+ int alertVolume;
+ switch (_rebelLevelType) {
+ case 1: case 11: case 12:
+ alertVolume = 40;
+ break;
+ case 2: case 3: case 5: case 6: case 7: case 9: case 10: case 13:
+ alertVolume = 63;
+ break;
+ default:
+ alertVolume = 127;
+ break;
+ }
+ playSfx(3, alertVolume, 0);
+ }
+
if (numSprites > 1) {
renderNutSprite(renderBitmap, pitch, width, height,
_viewX, statusBarY + _viewY, _smush_cockpitNut, 1);
@@ -4199,6 +4217,9 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
}
if (targetLocked) {
+ // A fresh lock (timer fully decayed) pings LOCKON, panned by crosshair X.
+ if (_rebelHandler == 0x26 && _targetLockTimer == 0 && (getDifficultyParams().flags & 2) == 0)
+ playSfx(4, 127, CLIP(aimPos.x - 160, -127, 127));
_targetLockTimer = 7;
} else if (_targetLockTimer > 0) {
_targetLockTimer--;
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 1517a5a9b5f..3789770b50a 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -1169,6 +1169,7 @@ int InsaneRebel2::runLevel11() {
uint16 waveSelect = waveEnd.creditedBits;
if ((_rebelPhaseState & 0x10) != 0 && (prevPhaseState & 0x10) == 0) {
+ playAuxSfx(3, 127, 0);
}
prevPhaseState = _rebelPhaseState;
@@ -1277,6 +1278,7 @@ int InsaneRebel2::runLevel11() {
return kLevelQuit;
if ((_rebelPhaseState & 0x0e) == 0x0e && (prevPhaseState & 0x0e) != 0x0e) {
+ playAuxSfx(3, 127, 0);
}
prevPhaseState = _rebelPhaseState;
More information about the Scummvm-git-logs
mailing list