[Scummvm-git-logs] scummvm master -> 90490c2792c55d85cbda11825683eca383eceecc

neuromancer noreply at scummvm.org
Mon Jun 1 19:59:10 UTC 2026


This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
bf3a892f5f SCUMM: RA2: improved input handling
5b40d53e96 SCUMM: RA2: fixed a bug in the duplicate/delete pilot code
4dd584d879 SCUMM: RA: make sure music is not interrupted
90490c2792 SCUMM: RA1: improve menu navigation using gamepad


Commit: bf3a892f5fe68a8bda3cc3728ac7da6637a11637
    https://github.com/scummvm/scummvm/commit/bf3a892f5fe68a8bda3cc3728ac7da6637a11637
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T21:58:55+02:00

Commit Message:
SCUMM: RA2: improved input handling

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index fe30e70a2dc..6fb4ffec57e 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -801,11 +801,14 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	//   -> velocity history averaging -> physics delta (clamped +/-12/frame)
 	//   -> position clamping -> corridor collision -> perspective offsets
 	//
-	// Level data table (DAT_0047e0e8 + level*0x242 + difficulty*0x22):
-	//   offset 0: smoothing param (>>4 +1 = window size)
+	// Level data table (DAT_0047e0e8 + difficulty*0x242 + levelType*0x22):
 	//   offset 2: Y speed          offset 4: X speed (levelSpeed)
-	//   offset 6: wind multiplier  offset 14: corridor damage
-	// We don't have the actual level data, so we use calibrated defaults.
+	//   offset 6: wind multiplier
+	// Our extracted difficulty table starts at DAT_0047e0f0, so for Handler 7
+	// level types these fields map to lift/slide/drift of the preceding row.
+	const int flightParamIndex = CLIP(_rebelLevelType - 1, 0, 16);
+	const LevelDifficultyParams &flightParams =
+		kDifficultyTable[CLIP(_difficulty, 0, 5)][flightParamIndex];
 
 	// Step 1: Mouse input as offset from screen center.
 	// DAT_0047a7e0 = mouseX - 160, DAT_0047a7e2 = mouseY - 100.
@@ -836,7 +839,6 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	}
 	_velocityHistory[0] = scaledInputX;
 
-	// Window size = (levelData[0] >> 4) + 1. Calibrated default: 5.
 	const int smoothWindow = 5;
 	int velSum = 0;
 	for (int i = 0; i < smoothWindow; i++) {
@@ -845,8 +847,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	_smoothedVelocity = (int16)(velSum / smoothWindow);  // DAT_0044370c
 
 	// Step 4: Wind history (lines 158-173).
-	// Wind multiplier comes from level data[6]. Without data, use 0 (no wind).
-	const int16 windMult = 0;
+	const int16 windMult = flightParams.driftRate;
 	int windSumX = 0, windSumY = 0;
 	for (int i = 14; i > 0; i--) {
 		_windHistoryX[i] = _windHistoryX[i - 1];
@@ -863,12 +864,8 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	int16 windEffectY = (int16)((windMult * (windSumY + _windParamY)) / 15);
 
 	// Step 5: Position delta (lines 174-242).
-	// levelSpeed (offset 4): calibrated so max velocity (127) -> delta 12.
-	//   8 = (speed * 127) >> 9 -> speed around 32
-	// levelYSpeed (offset 2): calibrated so max input (127) -> delta ~6.
-	//   6 = (speed * 127) >> 10 -> speed around 48
-	const int16 levelSpeed = 32;
-	const int16 levelYSpeed = 48;
+	const int16 levelSpeed = flightParams.slideRate;
+	const int16 levelYSpeed = flightParams.liftRate;
 	int16 absSmoothVel = ABS(_smoothedVelocity);
 	int16 positionDeltaX;
 
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 4c363df1be4..501186e152b 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -1596,7 +1596,12 @@ Common::Point InsaneRebel2::getGameplayAimPoint() {
 	// Pure getter (queried many times per frame): the aim/reticle follows the virtual
 	// mouse position. Directional controls pan that position incrementally once per frame
 	// via updateGameplayAimFromGamepad(), rather than snapping the reticle to a screen edge.
-	return Common::Point(_vm->_mouse.x, _vm->_mouse.y);
+	int y = _vm->_mouse.y;
+	if (_optControlsFlipped) {
+		// Original DAT_0047a7fe reverses only the up/down gameplay axis.
+		y = CLIP<int>(200 - y, 0, 199);
+	}
+	return Common::Point(_vm->_mouse.x, y);
 }
 
 // Apply the user's configured analog deadzone so a resting stick reports no


Commit: 5b40d53e96ed8200127fcbfea77a98ac16285d9f
    https://github.com/scummvm/scummvm/commit/5b40d53e96ed8200127fcbfea77a98ac16285d9f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T21:58:55+02:00

Commit Message:
SCUMM: RA2: fixed a bug in the duplicate/delete pilot code

Changed paths:
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.h


diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index 8dfdd0cac36..5a7221e3ad1 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -1239,6 +1239,32 @@ int InsaneRebel2::runLevelSelect() {
 			continue;
 		}
 
+		// --- Pilot operation submenu completed ---
+		if (_pilotMenuMode == kPilotModeCopySelect || _pilotMenuMode == kPilotModeDeleteSelect) {
+			int pilotIndex = _levelSelection;
+			if (pilotIndex >= 0 && pilotIndex < _numPilots) {
+				if (_pilotMenuMode == kPilotModeCopySelect) {
+					copyPilot(pilotIndex);
+					savePilots();
+					_levelSelection = pilotIndex;
+					debug("Rebel2: Copied pilot %d, now %d pilots", pilotIndex, _numPilots);
+				} else {
+					deletePilot(pilotIndex);
+					savePilots();
+					if (_numPilots > 0) {
+						_levelSelection = (pilotIndex != 0) ? pilotIndex - 1 : 0;
+					} else {
+						_levelSelection = 0;
+					}
+					debug("Rebel2: Deleted pilot %d, %d remaining", pilotIndex, _numPilots);
+				}
+			}
+			_pilotMenuMode = kPilotModeSelect;
+			_levelItemCount = _numPilots + 4;
+			_gameState = kStatePilotSelect;
+			continue;
+		}
+
 		// --- Normal pilot menu selection ---
 		debug("Rebel2: Pilot selection: %d (numPilots=%d)", _levelSelection, _numPilots);
 
@@ -1270,27 +1296,22 @@ int InsaneRebel2::runLevelSelect() {
 			continue;
 
 		} else if (_levelSelection == _numPilots + 1) {
-			// COPY PILOT
+			// COPY PILOT - original opens a second menu to select the source pilot.
 			if (_numPilots > 0 && _numPilots < kMaxPilots) {
-				// Copy first pilot (slot 0) by default — original swaps with selected
-				int srcIdx = (_levelSelection > 0 && _levelSelection <= _numPilots) ? _levelSelection - 1 : 0;
-				copyPilot(srcIdx);
-				savePilots();
-				_levelItemCount = _numPilots + 4;
-				debug("Rebel2: Copied pilot %d, now %d pilots", srcIdx, _numPilots);
+				_pilotMenuMode = kPilotModeCopySelect;
+				_levelSelection = 0;
+				_levelItemCount = _numPilots;
+				debug("Rebel2: COPY PILOT - selecting source");
 			}
 			continue;
 
 		} else if (_levelSelection == _numPilots + 2) {
-			// DELETE PILOT
+			// DELETE PILOT - original opens a second menu to select the target pilot.
 			if (_numPilots > 0) {
-				// Delete the first pilot (slot 0) — original has confirm sub-flow
-				deletePilot(0);
-				savePilots();
-				_levelItemCount = _numPilots + 4;
-				if (_levelSelection >= _levelItemCount)
-					_levelSelection = _levelItemCount - 1;
-				debug("Rebel2: Deleted pilot, %d remaining", _numPilots);
+				_pilotMenuMode = kPilotModeDeleteSelect;
+				_levelSelection = 0;
+				_levelItemCount = _numPilots;
+				debug("Rebel2: DELETE PILOT - selecting target");
 			}
 			continue;
 
@@ -1306,8 +1327,8 @@ int InsaneRebel2::runLevelSelect() {
 }
 
 int InsaneRebel2::processLevelSelectInput() {
-	// Process input for pilot selection and difficulty submenu
-	// Handles kPilotModeSelect, kPilotModeNameInput, and kStateDifficultySelect
+	// Process input for pilot selection and difficulty submenu.
+	// Handles pilot list, pilot operation submenus, name input, and difficulty.
 	// Returns: -1 = no action, 0+ = item selected
 
 	int result = -1;
@@ -1364,8 +1385,13 @@ int InsaneRebel2::processLevelSelectInput() {
 
 	// Normal menu navigation (pilot select or difficulty submenu)
 	bool isDifficultyMode = (_gameState == kStateDifficultySelect);
+	bool isPilotOperationMode =
+		(_pilotMenuMode == kPilotModeCopySelect || _pilotMenuMode == kPilotModeDeleteSelect);
 	int &selection = isDifficultyMode ? _difficultySelection : _levelSelection;
-	int itemCount = isDifficultyMode ? 6 : _levelItemCount;
+	int itemCount = isDifficultyMode ? 6 : (isPilotOperationMode ? _numPilots : _levelItemCount);
+	if (itemCount <= 0)
+		return -1;
+
 	const int itemBaseY = itemCount * -5 + 0x68;
 	const int itemSpacing = 10;
 
@@ -1398,6 +1424,11 @@ int InsaneRebel2::processLevelSelectInput() {
 			case Common::KEYCODE_ESCAPE:
 				if (isDifficultyMode) {
 					_gameState = kStatePilotSelect;
+				} else if (isPilotOperationMode) {
+					bool wasCopyMode = (_pilotMenuMode == kPilotModeCopySelect);
+					_pilotMenuMode = kPilotModeSelect;
+					_levelItemCount = _numPilots + 4;
+					_levelSelection = _numPilots + (wasCopyMode ? 1 : 2);
 				} else {
 					result = _levelItemCount - 1;  // Last item = MAIN MENU
 				}
@@ -1437,6 +1468,11 @@ int InsaneRebel2::processLevelSelectInput() {
 		case Common::EVENT_RETURN_TO_LAUNCHER:
 			if (isDifficultyMode) {
 				_gameState = kStatePilotSelect;
+			} else if (isPilotOperationMode) {
+				_pilotMenuMode = kPilotModeSelect;
+				_levelItemCount = _numPilots + 4;
+				_levelSelection = _levelItemCount - 1;
+				result = _levelSelection;
 			} else {
 				result = _levelItemCount - 1;
 			}
@@ -1480,6 +1516,30 @@ void InsaneRebel2::drawLevelSelectOverlay(byte *renderBitmap, int pitch, int wid
 		return;
 	}
 
+	if (_pilotMenuMode == kPilotModeCopySelect || _pilotMenuMode == kPilotModeDeleteSelect) {
+		Common::String pilotNameStrs[kMaxPilots];
+		for (int i = 0; i < _numPilots; i++) {
+			pilotNameStrs[i] = Common::String::format("^f01^c005%s^f00", _pilots[i].name);
+		}
+
+		const char *pilotItems[kMaxPilots + 1];
+		int idx = 0;
+		pilotItems[idx++] = splayer->getString(_pilotMenuMode == kPilotModeCopySelect ? 28 : 27);
+
+		for (int i = 0; i < _numPilots; i++) {
+			pilotItems[idx++] = pilotNameStrs[i].c_str();
+		}
+
+		for (int i = 0; i < idx; i++) {
+			if (!pilotItems[i] || !pilotItems[i][0]) {
+				pilotItems[i] = "";
+			}
+		}
+
+		drawMenuItems(renderBitmap, pitch, width, height, pilotItems, _numPilots, _levelSelection);
+		return;
+	}
+
 	// -------------------------------------------------------------------
 	// Pilot menu - FUN_0041f5ae(0, &DAT_00457768, N+4, 0)
 	// -------------------------------------------------------------------
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 926c23d69ab..6f24d48a69f 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -296,7 +296,9 @@ public:
 	enum PilotMenuMode {
 		kPilotModeSelect = 0,     // Normal pilot list selection
 		kPilotModeNameInput = 1,  // Typing a new pilot name
-		kPilotModeDifficulty = 2  // Difficulty submenu
+		kPilotModeDifficulty = 2, // Difficulty submenu
+		kPilotModeCopySelect = 3, // Selecting source pilot to copy
+		kPilotModeDeleteSelect = 4 // Selecting pilot to delete
 	};
 	PilotMenuMode _pilotMenuMode;
 	Common::String _pilotNameInput;      // Current name being typed


Commit: 4dd584d879196b8cc1c5306280820abbdcd60f4a
    https://github.com/scummvm/scummvm/commit/4dd584d879196b8cc1c5306280820abbdcd60f4a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T21:58:55+02:00

Commit Message:
SCUMM: RA: make sure music is not interrupted

Changed paths:
    engines/scumm/insane/rebel/rebel_audio.cpp
    engines/scumm/insane/rebel/rebel_audio.h
    engines/scumm/insane/rebel1/iact.cpp
    engines/scumm/insane/rebel1/rebel.cpp
    engines/scumm/insane/rebel1/rebel.h
    engines/scumm/insane/rebel1/runlevels.cpp


diff --git a/engines/scumm/insane/rebel/rebel_audio.cpp b/engines/scumm/insane/rebel/rebel_audio.cpp
index b06a58fd884..07204d4806d 100644
--- a/engines/scumm/insane/rebel/rebel_audio.cpp
+++ b/engines/scumm/insane/rebel/rebel_audio.cpp
@@ -87,10 +87,203 @@ void RebelAudio::queueData(int trackIdx, const uint8 *data, int32 size, int volu
 	_vm->_mixer->setChannelBalance(_handles[trackIdx], scaledPan);
 }
 
+bool RebelAudio::processAudioCodes(SmushPlayer *player, int idx, int32 &tmpFeedSize, int &mixVolume) {
+	uint8 *code, *buf, subcode, value;
+	int chunk;
+
+	while (tmpFeedSize) {
+		code = player->_smushDispatch[idx].headerPtr;
+
+		switch (code[0]) {
+		case SAUD_OP_INIT:
+			player->_smushDispatch[idx].audioLength = 0;
+			buf = player->_smushDispatch[idx].headerPtr;
+			player->_smushDispatch[idx].audioRemaining = READ_BE_UINT32(buf + 2);
+			player->_smushDispatch[idx].currentOffset = READ_BE_UINT32(buf + 6);
+			player->_smushDispatch[idx].sampleRate = player->_smushAudioSampleRate;
+			player->_smushDispatch[idx].headerPtr += player->_smushDispatch[idx].headerPtr[1] + 2;
+			if (player->_smushDispatch[idx].audioRemaining < player->_smushTracks[idx].availableSize + (player->_smushTracks[idx].availableSize >= player->_smushTracks[idx].sdatSize ? 0 : 15000) - player->_smushTracks[idx].dataSize) {
+				chunk = player->_smushTracks[idx].availableSize - player->_smushTracks[idx].dataSize - player->_smushDispatch[idx].audioRemaining + 15000;
+				if (chunk > player->_smushDispatch[idx].currentOffset) {
+					player->_smushTracks[idx].state = TRK_STATE_INACTIVE;
+					player->_smushTracks[idx].groupId = GRP_MASTER;
+					tmpFeedSize = 0;
+					break;
+				}
+
+				player->_smushDispatch[idx].audioRemaining += chunk;
+				player->_smushDispatch[idx].currentOffset -= chunk;
+			}
+			break;
+
+		case SAUD_OP_UPDATE_HEADER:
+		case SAUD_OP_COMPARE_GT:
+		case SAUD_OP_COMPARE_LT:
+		case SAUD_OP_COMPARE_EQ:
+		case SAUD_OP_COMPARE_NE:
+			subcode = code[4];
+			switch (subcode) {
+			case SAUD_VALUEID_ALL_VOLS:
+				value = player->_smushTrackVols[0];
+				break;
+			case SAUD_VALUEID_TRK_VOL:
+				value = player->_smushTracks[idx].volume;
+				break;
+			case SAUD_VALUEID_TRK_PAN:
+				value = player->_smushTracks[idx].pan;
+				break;
+			default:
+				value = player->_smushAudioTable[subcode];
+				break;
+			}
+
+			switch (code[0]) {
+			case SAUD_OP_UPDATE_HEADER:
+				value = value || (subcode == 0);
+				break;
+			case SAUD_OP_COMPARE_GT:
+				value = value > code[5];
+				break;
+			case SAUD_OP_COMPARE_LT:
+				value = value < code[5];
+				break;
+			case SAUD_OP_COMPARE_EQ:
+				value = value == code[5];
+				break;
+			case SAUD_OP_COMPARE_NE:
+				value = value != code[5];
+				break;
+			default:
+				break;
+			}
+
+			if (!value) {
+				player->_smushDispatch[idx].headerPtr = &code[code[1] + 2];
+			} else {
+				// Rebel Assault SAUD branch targets are signed relative displacements.
+				player->_smushDispatch[idx].headerPtr = code + READ_BE_INT16(&code[2]);
+			}
+			break;
+
+		case SAUD_OP_SET_PARAM:
+			switch (code[2]) {
+			case SAUD_VALUEID_ALL_VOLS:
+				player->_smushTrackVols[0] = code[3];
+				break;
+			case SAUD_VALUEID_TRK_VOL:
+				player->_smushTracks[idx].volume = code[3];
+				mixVolume = (player->_smushTrackVols[0] * player->_smushTracks[idx].volume) / 127;
+
+				if ((player->_smushTracks[idx].flags & TRK_TYPE_MASK) == IS_BKG_MUSIC && player->isChanActive(CHN_SPEECH))
+					mixVolume = (mixVolume * player->_gainReductionMultiplier) >> 8;
+				break;
+			case SAUD_VALUEID_TRK_PAN:
+				player->_smushTracks[idx].pan = code[3];
+				break;
+			default:
+				player->_smushAudioTable[code[2]] = code[3];
+				break;
+			}
+			player->_smushDispatch[idx].headerPtr = &code[code[1] + 2];
+			break;
+
+		case SAUD_OP_INCR_PARAM:
+			switch (code[2]) {
+			case SAUD_VALUEID_ALL_VOLS:
+				player->_smushTrackVols[0] += code[3];
+				break;
+			case SAUD_VALUEID_TRK_VOL:
+				player->_smushTracks[idx].volume += code[3];
+				break;
+			case SAUD_VALUEID_TRK_PAN:
+				player->_smushTracks[idx].pan += code[3];
+				break;
+			default:
+				player->_smushAudioTable[code[2]] += code[3];
+				break;
+			}
+			player->_smushDispatch[idx].headerPtr = &code[code[1] + 2];
+			break;
+
+		case SAUD_OP_SET_OFFSET:
+			player->_smushDispatch[idx].audioLength = 0;
+			buf = player->_smushDispatch[idx].headerPtr;
+			player->_smushDispatch[idx].audioRemaining = READ_BE_UINT32(buf + 2);
+			player->_smushDispatch[idx].currentOffset = READ_BE_UINT32(buf + 6);
+			player->_smushDispatch[idx].sampleRate = player->_smushAudioSampleRate;
+
+			player->_smushDispatch[idx].headerPtr += player->_smushDispatch[idx].headerPtr[1] + 2;
+			if (player->_smushDispatch[idx].audioRemaining < player->_smushTracks[idx].availableSize + (player->_smushTracks[idx].availableSize >= player->_smushTracks[idx].sdatSize ? 0 : 15000) - player->_smushTracks[idx].dataSize) {
+				chunk = player->_smushTracks[idx].availableSize - player->_smushTracks[idx].dataSize - player->_smushDispatch[idx].audioRemaining + 15000;
+				if (chunk > player->_smushDispatch[idx].currentOffset) {
+					player->_smushTracks[idx].state = TRK_STATE_INACTIVE;
+					player->_smushTracks[idx].groupId = GRP_MASTER;
+					tmpFeedSize = 0;
+					break;
+				}
+
+				player->_smushDispatch[idx].audioRemaining += chunk;
+				player->_smushDispatch[idx].currentOffset -= chunk;
+			}
+			break;
+
+		case SAUD_OP_SET_LENGTH:
+			if (!player->_smushDispatch[idx].audioLength) {
+				player->_smushDispatch[idx].audioLength = READ_BE_UINT32(&code[6]);
+				player->_smushDispatch[idx].elapsedAudio = 0;
+			}
+
+			buf = player->_smushDispatch[idx].headerPtr;
+			player->_smushDispatch[idx].audioRemaining = player->_smushDispatch[idx].elapsedAudio + READ_BE_UINT32(buf + 2);
+
+			player->_smushDispatch[idx].currentOffset = READ_BE_UINT32(buf + 14);
+			if (player->_smushDispatch[idx].currentOffset > player->_smushDispatch[idx].audioLength)
+				player->_smushDispatch[idx].currentOffset = player->_smushDispatch[idx].audioLength;
+
+			player->_smushDispatch[idx].sampleRate = player->_smushAudioSampleRate;
+
+			player->_smushDispatch[idx].audioLength -= player->_smushDispatch[idx].currentOffset;
+			player->_smushDispatch[idx].elapsedAudio += player->_smushDispatch[idx].currentOffset;
+
+			if (player->_smushDispatch[idx].audioLength) {
+				player->_smushDispatch[idx].headerPtr = &code[code[1] + 2];
+			} else {
+				player->_smushDispatch[idx].headerPtr = code + READ_BE_INT16(&code[18]);
+			}
+
+			if (player->_smushDispatch[idx].audioRemaining >= player->_smushTracks[idx].availableSize + (player->_smushTracks[idx].availableSize >= player->_smushTracks[idx].sdatSize ? 0 : 15000) - player->_smushTracks[idx].dataSize) {
+				chunk = player->_smushTracks[idx].availableSize - player->_smushTracks[idx].dataSize - player->_smushDispatch[idx].audioRemaining + 15000;
+				if (chunk > player->_smushDispatch[idx].currentOffset) {
+					player->_smushTracks[idx].state = TRK_STATE_INACTIVE;
+					player->_smushTracks[idx].groupId = GRP_MASTER;
+					tmpFeedSize = 0;
+				} else {
+					player->_smushDispatch[idx].audioRemaining += chunk;
+					player->_smushDispatch[idx].currentOffset -= chunk;
+				}
+			}
+			break;
+
+		default:
+			player->_smushTracks[idx].state = TRK_STATE_INACTIVE;
+			player->_smushTracks[idx].groupId = GRP_MASTER;
+			tmpFeedSize = 0;
+		}
+
+		if (player->_smushDispatch[idx].currentOffset > 0)
+			return false;
+	}
+
+	return true;
+}
+
 void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 	if (!player)
 		return;
 
+	if (player->_paused)
+		return;
+
 	if (player->_smushTracksNeedInit) {
 		player->_smushTracksNeedInit = false;
 		for (int i = 0; i < SMUSH_MAX_TRACKS; i++) {
@@ -176,6 +369,7 @@ void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 						if (!dispatch.dataBuf || offset < 0 || offset + mixInFrameCount > dispatch.dataSize)
 							break;
 
+						track.state = TRK_STATE_PLAYING;
 						queueData(i, &dispatch.dataBuf[offset], mixInFrameCount, mixVolume, track.pan);
 
 						dispatch.currentOffset -= mixInFrameCount;
@@ -191,9 +385,7 @@ void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 				}
 
 				if (dispatch.currentOffset <= 0) {
-					if (!player->processAudioCodes(i, tmpFeedSize, mixVolume))
-						break;
-					if (dispatch.currentOffset <= 0)
+					if (processAudioCodes(player, i, tmpFeedSize, mixVolume) && tmpFeedSize <= 0)
 						break;
 				} else if (tmpFeedSize <= 0) {
 					break;
diff --git a/engines/scumm/insane/rebel/rebel_audio.h b/engines/scumm/insane/rebel/rebel_audio.h
index c20dace1e5e..10e0548bf71 100644
--- a/engines/scumm/insane/rebel/rebel_audio.h
+++ b/engines/scumm/insane/rebel/rebel_audio.h
@@ -48,6 +48,8 @@ public:
 private:
 	static const int kMaxTracks = 4;
 
+	bool processAudioCodes(SmushPlayer *player, int idx, int32 &tmpFeedSize, int &mixVolume);
+
 	ScummEngine_v7 *_vm;
 	Audio::QueuingAudioStream *_streams[kMaxTracks];
 	Audio::SoundHandle _handles[kMaxTracks];
diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index f3d2a507789..db4c4593dc9 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -1222,6 +1222,7 @@ void InsaneRebel1::updateShipPhysics() {
 	if (_pathBranchEnabled && _gameCounter >= kPathBranchCounter) {
 		if (_shipPosX > kRA1CenterX) {
 			_rightPathSelected = true;
+			preserveInteractiveVideoAudioState();
 			_vm->_smushVideoShouldFinish = true;
 			debugC(DEBUG_INSANE, "RA1: Right path selected (counter=%d, shipX=%d)", _gameCounter, _shipPosX);
 		} else {
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 8341fa0c1cd..f8efc33a1ea 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -313,6 +313,10 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_hudDirtyFlag = 0;
 	_maxChapterUnlocked = 0;
 	_interactiveVideoActive = false;
+	_restoreInteractiveVideoAudioState = false;
+	memset(_savedInteractiveVideoTrackState, 0, sizeof(_savedInteractiveVideoTrackState));
+	memset(_savedInteractiveVideoTrackGroupId, 0, sizeof(_savedInteractiveVideoTrackGroupId));
+	_savedInteractiveVideoTrackCount = 0;
 	_gameCounter = 0;
 	_pathBranchEnabled = false;
 	_rightPathSelected = false;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 0b7d2da299b..83e8381390c 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -199,6 +199,8 @@ private:
 	// Play interactive gameplay video (with ship physics + HUD)
 	void playInteractiveVideo(const char *filename, int32 startFrame = 0);
 	void resetInteractiveVideoAudio();
+	void preserveInteractiveVideoAudioState();
+	void restoreInteractiveVideoAudioState();
 	void setupInteractiveVideoState(int32 startFrame);
 	void resolveSeek(const char *filename, int32 startFrame, int32 &videoOffset, int32 &videoStartFrame);
 	void captureInteractiveVideoInput();
@@ -488,6 +490,10 @@ private:
 
 	// Streamed SMUSH audio
 	RebelAudio _audio;
+	bool _restoreInteractiveVideoAudioState;
+	int16 _savedInteractiveVideoTrackState[SMUSH_MAX_TRACKS];
+	int _savedInteractiveVideoTrackGroupId[SMUSH_MAX_TRACKS];
+	int _savedInteractiveVideoTrackCount;
 	static const int kNumSfx = 8;
 	enum SfxSlot {
 		kSfxLaserShot = 0,
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index f1d67c8e49b..b1e4303c7f4 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -1552,6 +1552,42 @@ void InsaneRebel1::resetInteractiveVideoAudio() {
 	initAudio(sampleRate);
 }
 
+void InsaneRebel1::preserveInteractiveVideoAudioState() {
+	SmushPlayer *splayer = _vm->_splayer;
+
+	_restoreInteractiveVideoAudioState = false;
+	_savedInteractiveVideoTrackCount = 0;
+	if (!splayer)
+		return;
+
+	_savedInteractiveVideoTrackCount = MIN<int>(splayer->_smushNumTracks, SMUSH_MAX_TRACKS);
+	for (int i = 0; i < _savedInteractiveVideoTrackCount; i++) {
+		_savedInteractiveVideoTrackState[i] = splayer->_smushTracks[i].state;
+		_savedInteractiveVideoTrackGroupId[i] = splayer->_smushTracks[i].groupId;
+	}
+
+	_restoreInteractiveVideoAudioState = true;
+}
+
+void InsaneRebel1::restoreInteractiveVideoAudioState() {
+	if (!_restoreInteractiveVideoAudioState)
+		return;
+
+	_restoreInteractiveVideoAudioState = false;
+	if (_vm->shouldQuit() || _vm->_saveLoadFlag)
+		return;
+
+	SmushPlayer *splayer = _vm->_splayer;
+	if (!splayer)
+		return;
+
+	const int trackCount = MIN<int>(_savedInteractiveVideoTrackCount, splayer->_smushNumTracks);
+	for (int i = 0; i < trackCount; i++) {
+		splayer->_smushTracks[i].state = _savedInteractiveVideoTrackState[i];
+		splayer->_smushTracks[i].groupId = _savedInteractiveVideoTrackGroupId[i];
+	}
+}
+
 void InsaneRebel1::setupInteractiveVideoState(int32 startFrame) {
 	const bool level7RouteSplice = (_currentLevel == 6 && _levelRouteIndex > 0);
 	const bool resumingRoute = startFrame > 0;
@@ -1658,6 +1694,7 @@ void InsaneRebel1::releaseInteractiveVideoInput() {
 
 void InsaneRebel1::playInteractiveVideoFile(const char *filename, int32 videoOffset, int32 videoStartFrame) {
 	_vm->_splayer->play(filename, 12, videoOffset, videoStartFrame);
+	restoreInteractiveVideoAudioState();
 	_interactiveVideoActive = false;
 }
 
@@ -1669,8 +1706,10 @@ void InsaneRebel1::playInteractiveVideo(const char *filename, int32 startFrame)
 
 	int32 videoStartFrame = 0;
 	int32 videoOffset = 0;
+	const bool preserveRuntimeState = (startFrame > 0) || (_currentLevel == 6 && _levelRouteIndex > 0);
 
-	resetInteractiveVideoAudio();
+	if (!preserveRuntimeState)
+		resetInteractiveVideoAudio();
 	setupInteractiveVideoState(startFrame);
 	resolveSeek(filename, startFrame, videoOffset, videoStartFrame);
 	captureInteractiveVideoInput();


Commit: 90490c2792c55d85cbda11825683eca383eceecc
    https://github.com/scummvm/scummvm/commit/90490c2792c55d85cbda11825683eca383eceecc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T21:58:55+02:00

Commit Message:
SCUMM: RA1: improve menu navigation using gamepad

Changed paths:
    engines/scumm/insane/rebel1/menu.cpp
    engines/scumm/metaengine.cpp


diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 0dbc31baf07..68bbd4aae9d 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -91,6 +91,10 @@ static const char *getRebel1ActionName(Common::CustomEventType customType) {
 		return "attack";
 	case kScummActionInsaneSwitch:
 		return "switch";
+	case kScummActionInsaneBack:
+		return "back";
+	case kScummActionInsaneSkip:
+		return "skip";
 	default:
 		return "other";
 	}
@@ -171,6 +175,8 @@ static RA1MenuCommand getRebel1MenuCommandFromAction(ScummAction action) {
 		return kRA1MenuCommandRight;
 	case kScummActionInsaneAttack:
 		return kRA1MenuCommandAccept;
+	case kScummActionInsaneSkip:
+		return kRA1MenuCommandCancel;
 	default:
 		return kRA1MenuCommandNone;
 	}
@@ -326,6 +332,10 @@ bool InsaneRebel1::handleTextEntryAction(ScummAction action) {
 	case kScummActionInsaneAttack:
 		selectTextEntryChar();
 		return true;
+	case kScummActionInsaneSwitch:
+	case kScummActionInsaneSkip:
+		finishTextEntry(true);
+		return true;
 	default:
 		return false;
 	}
@@ -482,6 +492,12 @@ bool InsaneRebel1::handleControllerMenuAction(ScummAction action) {
 	if (_textEntryActive)
 		return handleTextEntryAction(action);
 
+	if (action == kScummActionInsaneSwitch || action == kScummActionInsaneSkip) {
+		if (_levelSelectActive || _optionsActive)
+			return handleMenuCommand(kRA1MenuCommandCancel);
+		return true;
+	}
+
 	return handleMenuCommand(getRebel1MenuCommandFromAction(action));
 }
 
@@ -692,7 +708,24 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			_activeInputSource = kInputSourceJoystickDigital;
 		}
 
-		if (_highScoresActive && pressed && event.customType == kScummActionInsaneAttack) {
+		if (event.customType == kScummActionInsaneBack) {
+			if (!pressed)
+				return true;
+			if (_player) {
+				const bool wasPaused = _player->_paused;
+				if (!wasPaused)
+					_player->pause();
+				_vm->openMainMenuDialog();
+				if (!wasPaused)
+					_player->unpause();
+			}
+			return true;
+		}
+
+		if (_highScoresActive && pressed &&
+			(event.customType == kScummActionInsaneAttack ||
+			 event.customType == kScummActionInsaneSwitch ||
+			 event.customType == kScummActionInsaneSkip)) {
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
@@ -700,6 +733,16 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		if (pressed && handleControllerMenuAction((ScummAction)event.customType))
 			return true;
 
+		if (event.customType == kScummActionInsaneSkip) {
+			if (!pressed)
+				return true;
+			if (!_interactiveVideoActive) {
+				_vm->_smushVideoShouldFinish = true;
+				return true;
+			}
+			return true;
+		}
+
 		if (_interactiveVideoActive && !_menuActive && event.customType == kScummActionInsaneAttack) {
 			_playerFired = pressed;
 			return true;
@@ -732,14 +775,30 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		return true;
 	}
 
-	if (event.type == Common::EVENT_MAINMENU && _interactiveVideoActive && !_menuActive) {
+	if (event.type == Common::EVENT_MAINMENU) {
+		if (_menuActive || _highScoresActive)
+			return true;
+
 		const uint32 now = _vm->_system->getMillis();
 		const uint32 elapsedSinceAxis = _lastJoystickAxisEventTime ? now - _lastJoystickAxisEventTime : 0xffffffffu;
-		debugC(DEBUG_INSANE, "RA1 input mainmenu-event: gameplay=1 elapsedSinceAxis=%u storedAxis=(%d,%d)",
-			elapsedSinceAxis, _joystickAxisX, _joystickAxisY);
-		if (elapsedSinceAxis <= kRA1JoystickAxisEscGuardMs) {
-			debugC(DEBUG_INSANE, "RA1 input ignored mainmenu event after recent joystick axis movement (%u ms)", elapsedSinceAxis);
-			return true;
+
+		if (_interactiveVideoActive && !_menuActive) {
+			debugC(DEBUG_INSANE, "RA1 input mainmenu-event: gameplay=1 elapsedSinceAxis=%u storedAxis=(%d,%d)",
+				elapsedSinceAxis, _joystickAxisX, _joystickAxisY);
+			if (elapsedSinceAxis <= kRA1JoystickAxisEscGuardMs) {
+				debugC(DEBUG_INSANE, "RA1 input ignored mainmenu event after recent joystick axis movement (%u ms)", elapsedSinceAxis);
+				return true;
+			}
+
+			if (_player) {
+				const bool wasPaused = _player->_paused;
+				if (!wasPaused)
+					_player->pause();
+				_vm->openMainMenuDialog();
+				if (!wasPaused)
+					_player->unpause();
+				return true;
+			}
 		}
 	}
 
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index c43f81ea35d..d0e91a67b89 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -1019,7 +1019,7 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 	Common::String gameId = ConfMan.get("gameid", target);
 	Action *act;
 
-	if (gameId == "rebel2") {
+	if (gameId == "rebel1" || gameId == "rebel2") {
 		for (uint i = 0; i < keymaps.size(); ++i) {
 			if (keymaps[i]->getId() == "engine-default") {
 				delete keymaps.remove_at(i);
@@ -1141,24 +1141,28 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel1AxisUp);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_Y-");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_Y-");
+		act->addDefaultInputMapping("JOY_HAT_Y-");
 		rebel1Keymap->addAction(act);
 
 		act = new Action("RA1STICKDOWN", _("Stick down"));
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel1AxisDown);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_Y+");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_Y+");
+		act->addDefaultInputMapping("JOY_HAT_Y+");
 		rebel1Keymap->addAction(act);
 
 		act = new Action("RA1STICKLEFT", _("Stick left"));
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel1AxisLeft);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_X-");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_X-");
+		act->addDefaultInputMapping("JOY_HAT_X-");
 		rebel1Keymap->addAction(act);
 
 		act = new Action("RA1STICKRIGHT", _("Stick right"));
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel1AxisRight);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_X+");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_X+");
+		act->addDefaultInputMapping("JOY_HAT_X+");
 		rebel1Keymap->addAction(act);
 
 		act = new Action("RA1FIRE", _("Fire / select"));
@@ -1166,11 +1170,21 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 		act->addDefaultInputMapping("JOY_A");
 		rebel1Keymap->addAction(act);
 
-		act = new Action("RA1BACK", _("Back / skip"));
-		act->setKeyEvent(KeyState(KEYCODE_ESCAPE, ASCII_ESCAPE));
+		act = new Action("RA1CANCEL", _("Menu back"));
+		act->setCustomEngineActionEvent(kScummActionInsaneSwitch);
+		act->addDefaultInputMapping("JOY_X");
+		rebel1Keymap->addAction(act);
+
+		act = new Action("RA1SKIP", _("Skip / menu back"));
+		act->setCustomEngineActionEvent(kScummActionInsaneSkip);
 		act->addDefaultInputMapping("JOY_B");
-		act->addDefaultInputMapping("JOY_Y");
+		rebel1Keymap->addAction(act);
+
+		act = new Action("RA1BACK", _("Menu"));
+		act->setCustomEngineActionEvent(kScummActionInsaneBack);
+		act->addDefaultInputMapping("ESCAPE");
 		act->addDefaultInputMapping("JOY_START");
+		act->addDefaultInputMapping("AC_BACK");
 		rebel1Keymap->addAction(act);
 
 		keymaps.push_back(rebel1Keymap);




More information about the Scummvm-git-logs mailing list